markdownlint 0.34.0 → 0.35.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 (76) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +25 -10
  3. package/demo/markdownlint-browser.js +1144 -1166
  4. package/doc/CustomRules.md +11 -6
  5. package/doc/Rules.md +66 -0
  6. package/doc/md034.md +5 -0
  7. package/doc/md044.md +10 -0
  8. package/doc/md058.md +48 -0
  9. package/helpers/README.md +3 -29
  10. package/helpers/helpers.js +56 -288
  11. package/helpers/micromark.cjs +111 -12
  12. package/helpers/package.json +1 -1
  13. package/lib/cache.js +62 -13
  14. package/lib/configuration-strict.d.ts +1185 -0
  15. package/lib/configuration.d.ts +3 -1173
  16. package/lib/constants.js +3 -2
  17. package/lib/markdownlint.d.ts +65 -56
  18. package/lib/markdownlint.js +59 -37
  19. package/lib/md001.js +15 -9
  20. package/lib/md003.js +14 -9
  21. package/lib/md004.js +55 -48
  22. package/lib/md005.js +4 -13
  23. package/lib/md007.js +5 -9
  24. package/lib/md009.js +50 -44
  25. package/lib/md010.js +48 -33
  26. package/lib/md011.js +16 -8
  27. package/lib/md012.js +17 -9
  28. package/lib/md013.js +44 -38
  29. package/lib/md014.js +35 -38
  30. package/lib/md018.js +17 -9
  31. package/lib/md019-md021.js +80 -0
  32. package/lib/md020.js +13 -7
  33. package/lib/md022.js +6 -16
  34. package/lib/md023.js +22 -23
  35. package/lib/md024.js +12 -9
  36. package/lib/md025.js +15 -10
  37. package/lib/md026.js +2 -7
  38. package/lib/md027.js +5 -10
  39. package/lib/md028.js +3 -8
  40. package/lib/md029.js +40 -34
  41. package/lib/md030.js +4 -10
  42. package/lib/md031.js +43 -23
  43. package/lib/md032.js +16 -26
  44. package/lib/md033.js +3 -8
  45. package/lib/md034.js +5 -10
  46. package/lib/md035.js +2 -7
  47. package/lib/md036.js +3 -2
  48. package/lib/md037.js +2 -7
  49. package/lib/md038.js +3 -9
  50. package/lib/md039.js +66 -54
  51. package/lib/md040.js +3 -8
  52. package/lib/md041.js +1 -1
  53. package/lib/md042.js +48 -38
  54. package/lib/md043.js +20 -14
  55. package/lib/md044.js +3 -8
  56. package/lib/md045.js +3 -8
  57. package/lib/md046.js +2 -8
  58. package/lib/md048.js +3 -7
  59. package/lib/md049-md050.js +2 -7
  60. package/lib/md051.js +6 -12
  61. package/lib/md052.js +2 -2
  62. package/lib/md053.js +2 -2
  63. package/lib/md054.js +8 -13
  64. package/lib/md055.js +7 -8
  65. package/lib/md056.js +15 -11
  66. package/lib/md058.js +43 -0
  67. package/lib/rules.js +11 -4
  68. package/package.json +19 -17
  69. package/schema/.markdownlint.jsonc +54 -51
  70. package/schema/.markdownlint.yaml +53 -50
  71. package/schema/ValidatingConfiguration.md +6 -8
  72. package/schema/markdownlint-config-schema-strict.json +1819 -0
  73. package/schema/markdownlint-config-schema.json +115 -105
  74. package/style/prettier.json +1 -0
  75. package/lib/md019.js +0 -42
  76. package/lib/md021.js +0 -65
@@ -1,4 +1,4 @@
1
- /*! markdownlint 0.34.0 https://github.com/DavidAnson/markdownlint @license MIT */
1
+ /*! markdownlint 0.35.0 https://github.com/DavidAnson/markdownlint @license MIT */
2
2
  var markdownlint;
3
3
  /******/ (() => { // webpackBootstrap
4
4
  /******/ var __webpack_modules__ = ({
@@ -30,10 +30,6 @@ const inlineCommentStartRe =
30
30
  /(<!--\s*markdownlint-(disable|enable|capture|restore|disable-file|enable-file|disable-line|disable-next-line|configure-file))(?:\s|-->)/gi;
31
31
  module.exports.inlineCommentStartRe = inlineCommentStartRe;
32
32
 
33
- // Regular expressions for range matching
34
- module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
35
- module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
36
-
37
33
  // Regular expression for blockquote prefixes
38
34
  const blockquotePrefixRe = /^[>\s]*/;
39
35
  module.exports.blockquotePrefixRe = blockquotePrefixRe;
@@ -155,7 +151,6 @@ function isBlankLine(line) {
155
151
  const startComment = "<!--";
156
152
  const endComment = "-->";
157
153
  const removeComments = (s) => {
158
- // eslint-disable-next-line no-constant-condition
159
154
  while (true) {
160
155
  const start = s.indexOf(startComment);
161
156
  const end = s.indexOf(endComment);
@@ -182,35 +177,6 @@ function isBlankLine(line) {
182
177
  }
183
178
  module.exports.isBlankLine = isBlankLine;
184
179
 
185
- /**
186
- * Compare function for Array.prototype.sort for ascending order of numbers.
187
- *
188
- * @param {number} a First number.
189
- * @param {number} b Second number.
190
- * @returns {number} Positive value if a>b, negative value if b<a, 0 otherwise.
191
- */
192
- module.exports.numericSortAscending = function numericSortAscending(a, b) {
193
- return a - b;
194
- };
195
-
196
- // Returns true iff the sorted array contains the specified element
197
- module.exports.includesSorted = function includesSorted(array, element) {
198
- let left = 0;
199
- let right = array.length - 1;
200
- while (left <= right) {
201
- // eslint-disable-next-line no-bitwise
202
- const mid = (left + right) >> 1;
203
- if (array[mid] < element) {
204
- left = mid + 1;
205
- } else if (array[mid] > element) {
206
- right = mid - 1;
207
- } else {
208
- return true;
209
- }
210
- }
211
- return false;
212
- };
213
-
214
180
  // Replaces the content of properly-formatted CommonMark comments with "."
215
181
  // This preserves the line/column information for the rest of the document
216
182
  // https://spec.commonmark.org/0.29/#html-blocks
@@ -301,208 +267,6 @@ module.exports.emphasisOrStrongStyleFor =
301
267
  }
302
268
  };
303
269
 
304
- /**
305
- * Return the number of characters of indent for a token.
306
- *
307
- * @param {Object} token MarkdownItToken instance.
308
- * @returns {number} Characters of indent.
309
- */
310
- function indentFor(token) {
311
- const line = token.line.replace(/^[\s>]*(> |>)/, "");
312
- return line.length - line.trimStart().length;
313
- }
314
- module.exports.indentFor = indentFor;
315
-
316
- // Returns the heading style for a heading token
317
- module.exports.headingStyleFor = function headingStyleFor(token) {
318
- if ((token.map[1] - token.map[0]) === 1) {
319
- if (/[^\\]#\s*$/.test(token.line)) {
320
- return "atx_closed";
321
- }
322
- return "atx";
323
- }
324
- return "setext";
325
- };
326
-
327
- /**
328
- * Return the string representation of an unordered list marker.
329
- *
330
- * @param {Object} token MarkdownItToken instance.
331
- * @returns {"asterisk" | "dash" | "plus"} String representation.
332
- */
333
- module.exports.unorderedListStyleFor = function unorderedListStyleFor(token) {
334
- switch (token.markup) {
335
- case "-":
336
- return "dash";
337
- case "+":
338
- return "plus";
339
- // case "*":
340
- default:
341
- return "asterisk";
342
- }
343
- };
344
-
345
- /**
346
- * @callback TokenCallback
347
- * @param {MarkdownItToken} token Current token.
348
- * @returns {void}
349
- */
350
-
351
- /**
352
- * Calls the provided function for each matching token.
353
- *
354
- * @param {Object} params RuleParams instance.
355
- * @param {string} type Token type identifier.
356
- * @param {TokenCallback} handler Callback function.
357
- * @returns {void}
358
- */
359
- function filterTokens(params, type, handler) {
360
- for (const token of params.parsers.markdownit.tokens) {
361
- if (token.type === type) {
362
- handler(token);
363
- }
364
- }
365
- }
366
- module.exports.filterTokens = filterTokens;
367
-
368
- /**
369
- * @typedef {Array} LineMetadata
370
- */
371
-
372
- /**
373
- * Gets a line metadata array.
374
- *
375
- * @param {Object} params RuleParams instance.
376
- * @returns {LineMetadata} Line metadata.
377
- */
378
- function getLineMetadata(params) {
379
- const lineMetadata = params.lines.map(
380
- (line, index) => [ line, index, false, 0, false, false, false ]
381
- );
382
- filterTokens(params, "fence", (token) => {
383
- lineMetadata[token.map[0]][3] = 1;
384
- lineMetadata[token.map[1] - 1][3] = -1;
385
- for (let i = token.map[0] + 1; i < token.map[1] - 1; i++) {
386
- lineMetadata[i][2] = true;
387
- }
388
- });
389
- filterTokens(params, "code_block", (token) => {
390
- for (let i = token.map[0]; i < token.map[1]; i++) {
391
- lineMetadata[i][2] = true;
392
- }
393
- });
394
- filterTokens(params, "table_open", (token) => {
395
- for (let i = token.map[0]; i < token.map[1]; i++) {
396
- lineMetadata[i][4] = true;
397
- }
398
- });
399
- filterTokens(params, "list_item_open", (token) => {
400
- let count = 1;
401
- for (let i = token.map[0]; i < token.map[1]; i++) {
402
- lineMetadata[i][5] = count;
403
- count++;
404
- }
405
- });
406
- filterTokens(params, "hr", (token) => {
407
- lineMetadata[token.map[0]][6] = true;
408
- });
409
- return lineMetadata;
410
- }
411
- module.exports.getLineMetadata = getLineMetadata;
412
-
413
- /**
414
- * @callback EachLineCallback
415
- * @param {string} line Line content.
416
- * @param {number} lineIndex Line index (0-based).
417
- * @param {boolean} inCode Iff in a code block.
418
- * @param {number} onFence + if open, - if closed, 0 otherwise.
419
- * @param {boolean} inTable Iff in a table.
420
- * @param {boolean} inItem Iff in a list item.
421
- * @param {boolean} inBreak Iff in semantic break.
422
- * @returns {void}
423
- */
424
-
425
- /**
426
- * Calls the provided function for each line.
427
- *
428
- * @param {LineMetadata} lineMetadata Line metadata object.
429
- * @param {EachLineCallback} handler Function taking (line, lineIndex, inCode,
430
- * onFence, inTable, inItem, inBreak).
431
- * @returns {void}
432
- */
433
- function forEachLine(lineMetadata, handler) {
434
- for (const metadata of lineMetadata) {
435
- // @ts-ignore
436
- handler(...metadata);
437
- }
438
- }
439
- module.exports.forEachLine = forEachLine;
440
-
441
- // Returns (nested) lists as a flat array (in order)
442
- module.exports.flattenLists = function flattenLists(tokens) {
443
- const flattenedLists = [];
444
- const stack = [];
445
- let current = null;
446
- let nesting = 0;
447
- const nestingStack = [];
448
- let lastWithMap = { "map": [ 0, 1 ] };
449
- for (const token of tokens) {
450
- if ((token.type === "bullet_list_open") ||
451
- (token.type === "ordered_list_open")) {
452
- // Save current context and start a new one
453
- stack.push(current);
454
- current = {
455
- "unordered": (token.type === "bullet_list_open"),
456
- "parentsUnordered": !current ||
457
- (current.unordered && current.parentsUnordered),
458
- "open": token,
459
- "indent": indentFor(token),
460
- "parentIndent": (current && current.indent) || 0,
461
- "items": [],
462
- "nesting": nesting,
463
- "lastLineIndex": -1,
464
- "insert": flattenedLists.length
465
- };
466
- nesting++;
467
- } else if ((token.type === "bullet_list_close") ||
468
- (token.type === "ordered_list_close")) {
469
- // Finalize current context and restore previous
470
- current.lastLineIndex = lastWithMap.map[1];
471
- flattenedLists.splice(current.insert, 0, current);
472
- delete current.insert;
473
- current = stack.pop();
474
- nesting--;
475
- } else if (token.type === "list_item_open") {
476
- // Add list item
477
- current.items.push(token);
478
- } else if (token.type === "blockquote_open") {
479
- nestingStack.push(nesting);
480
- nesting = 0;
481
- } else if (token.type === "blockquote_close") {
482
- nesting = nestingStack.pop() || 0;
483
- }
484
- if (token.map) {
485
- // Track last token with map
486
- lastWithMap = token;
487
- }
488
- }
489
- return flattenedLists;
490
- };
491
-
492
- // Calls the provided function for each heading's content
493
- module.exports.forEachHeading = function forEachHeading(params, handler) {
494
- let heading = null;
495
- for (const token of params.parsers.markdownit.tokens) {
496
- if (token.type === "heading_open") {
497
- heading = token;
498
- } else if (token.type === "heading_close") {
499
- heading = null;
500
- } else if ((token.type === "inline") && heading) {
501
- handler(heading, token.content, token);
502
- }
503
- }
504
- };
505
-
506
270
  /**
507
271
  * @callback InlineCodeSpanCallback
508
272
  * @param {string} code Code content.
@@ -608,8 +372,20 @@ function addError(onError, lineNumber, detail, context, range, fixInfo) {
608
372
  }
609
373
  module.exports.addError = addError;
610
374
 
611
- // Adds an error object with details conditionally via the onError callback
612
- module.exports.addErrorDetailIf = function addErrorDetailIf(
375
+ /**
376
+ * Adds an error object with details conditionally via the onError callback.
377
+ *
378
+ * @param {Object} onError RuleOnError instance.
379
+ * @param {number} lineNumber Line number.
380
+ * @param {Object} expected Expected value.
381
+ * @param {Object} actual Actual value.
382
+ * @param {string} [detail] Error details.
383
+ * @param {string} [context] Error context.
384
+ * @param {number[]} [range] Column and length of error.
385
+ * @param {Object} [fixInfo] RuleOnErrorFixInfo instance.
386
+ * @returns {void}
387
+ */
388
+ function addErrorDetailIf(
613
389
  onError, lineNumber, expected, actual, detail, context, range, fixInfo) {
614
390
  if (expected !== actual) {
615
391
  addError(
@@ -621,51 +397,55 @@ module.exports.addErrorDetailIf = function addErrorDetailIf(
621
397
  range,
622
398
  fixInfo);
623
399
  }
624
- };
400
+ }
401
+ module.exports.addErrorDetailIf = addErrorDetailIf;
625
402
 
626
- // Adds an error object with context via the onError callback
627
- module.exports.addErrorContext = function addErrorContext(
628
- onError, lineNumber, context, left, right, range, fixInfo) {
629
- context = ellipsify(context, left, right);
403
+ /**
404
+ * Adds an error object with context via the onError callback.
405
+ *
406
+ * @param {Object} onError RuleOnError instance.
407
+ * @param {number} lineNumber Line number.
408
+ * @param {string} context Error context.
409
+ * @param {boolean} [start] True iff the start of the text is important.
410
+ * @param {boolean} [end] True iff the end of the text is important.
411
+ * @param {number[]} [range] Column and length of error.
412
+ * @param {Object} [fixInfo] RuleOnErrorFixInfo instance.
413
+ * @returns {void}
414
+ */
415
+ function addErrorContext(
416
+ onError, lineNumber, context, start, end, range, fixInfo) {
417
+ context = ellipsify(context, start, end);
630
418
  addError(onError, lineNumber, undefined, context, range, fixInfo);
631
- };
419
+ }
420
+ module.exports.addErrorContext = addErrorContext;
632
421
 
633
422
  /**
634
- * Returns an array of code block and span content ranges.
423
+ * Adds an error object with context for a construct missing a blank line.
635
424
  *
636
- * @param {Object} params RuleParams instance.
637
- * @param {Object} lineMetadata Line metadata object.
638
- * @returns {number[][]} Array of ranges (lineIndex, columnIndex, length).
425
+ * @param {Object} onError RuleOnError instance.
426
+ * @param {string[]} lines Lines of Markdown content.
427
+ * @param {number} lineIndex Line index of line.
428
+ * @param {number} [lineNumber] Line number for override.
429
+ * @returns {void}
639
430
  */
640
- module.exports.codeBlockAndSpanRanges = (params, lineMetadata) => {
641
- const exclusions = [];
642
- // Add code block ranges (excludes fences)
643
- forEachLine(lineMetadata, (line, lineIndex, inCode, onFence) => {
644
- if (inCode && !onFence) {
645
- exclusions.push([ lineIndex, 0, line.length ]);
646
- }
647
- });
648
- // Add code span ranges (excludes ticks)
649
- filterTokens(params, "inline", (token) => {
650
- if (token.children.some((child) => child.type === "code_inline")) {
651
- const tokenLines = params.lines.slice(token.map[0], token.map[1]);
652
- forEachInlineCodeSpan(
653
- tokenLines.join("\n"),
654
- (code, lineIndex, columnIndex) => {
655
- const codeLines = code.split(newLineRe);
656
- for (const [ i, line ] of codeLines.entries()) {
657
- exclusions.push([
658
- token.lineNumber - 1 + lineIndex + i,
659
- i ? 0 : columnIndex,
660
- line.length
661
- ]);
662
- }
663
- }
664
- );
431
+ function addErrorContextForLine(onError, lines, lineIndex, lineNumber) {
432
+ const line = lines[lineIndex];
433
+ // @ts-ignore
434
+ const quotePrefix = line.match(blockquotePrefixRe)[0].trimEnd();
435
+ addErrorContext(
436
+ onError,
437
+ lineIndex + 1,
438
+ line.trim(),
439
+ undefined,
440
+ undefined,
441
+ undefined,
442
+ {
443
+ lineNumber,
444
+ "insertText": `${quotePrefix}\n`
665
445
  }
666
- });
667
- return exclusions;
668
- };
446
+ );
447
+ }
448
+ module.exports.addErrorContextForLine = addErrorContextForLine;
669
449
 
670
450
  /**
671
451
  * Determines whether the specified range is within another range.
@@ -685,18 +465,6 @@ const withinAnyRange = (ranges, lineIndex, index, length) => (
685
465
  );
686
466
  module.exports.withinAnyRange = withinAnyRange;
687
467
 
688
- // Returns a range object for a line by applying a RegExp
689
- module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
690
- let range = null;
691
- const match = line.match(regexp);
692
- if (match) {
693
- const column = match.index + 1;
694
- const length = match[0].length;
695
- range = [ column, length ];
696
- }
697
- return range;
698
- };
699
-
700
468
  // Determines if the front matter includes a title
701
469
  module.exports.frontMatterHasTitle =
702
470
  function frontMatterHasTitle(frontMatterLines, frontMatterTitlePattern) {
@@ -1189,12 +957,23 @@ const {
1189
957
  const { newLineRe } = __webpack_require__(/*! ./shared.js */ "../helpers/shared.js");
1190
958
 
1191
959
  const flatTokensSymbol = Symbol("flat-tokens");
960
+ const reparseSymbol = Symbol("reparse");
1192
961
 
1193
962
  /** @typedef {import("markdownlint-micromark").Event} Event */
1194
963
  /** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */
1195
964
  /** @typedef {import("markdownlint-micromark").TokenType} TokenType */
1196
965
  /** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */
1197
966
 
967
+ /**
968
+ * Determines if a Micromark token is within an htmlFlow.
969
+ *
970
+ * @param {Token} token Micromark token.
971
+ * @returns {boolean} True iff the token is within an htmlFlow.
972
+ */
973
+ function inHtmlFlow(token) {
974
+ return token[reparseSymbol];
975
+ }
976
+
1198
977
  /**
1199
978
  * Returns whether a token is an htmlFlow type containing an HTML comment.
1200
979
  *
@@ -1323,6 +1102,9 @@ function micromarkParseWithOffset(
1323
1102
  "children": [],
1324
1103
  "parent": ((previous === root) ? (ancestor || null) : previous)
1325
1104
  };
1105
+ if (ancestor) {
1106
+ Object.defineProperty(current, reparseSymbol, { "value": true });
1107
+ }
1326
1108
  previous.children.push(current);
1327
1109
  flatTokens.push(current);
1328
1110
  if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) {
@@ -1395,6 +1177,20 @@ function micromarkParse(
1395
1177
  );
1396
1178
  }
1397
1179
 
1180
+ /**
1181
+ * Adds a range of numbers to a set.
1182
+ *
1183
+ * @param {Set<number>} set Set of numbers.
1184
+ * @param {number} start Starting number.
1185
+ * @param {number} end Ending number.
1186
+ * @returns {void}
1187
+ */
1188
+ function addRangeToSet(set, start, end) {
1189
+ for (let i = start; i <= end; i++) {
1190
+ set.add(i);
1191
+ }
1192
+ }
1193
+
1398
1194
  /**
1399
1195
  * @callback AllowedPredicate
1400
1196
  * @param {Token} token Micromark token.
@@ -1455,10 +1251,12 @@ function filterByPredicate(tokens, allowed, transformChildren) {
1455
1251
  *
1456
1252
  * @param {Token[]} tokens Micromark tokens.
1457
1253
  * @param {TokenType[]} types Types to allow.
1254
+ * @param {boolean} [htmlFlow] Whether to include htmlFlow content.
1458
1255
  * @returns {Token[]} Filtered tokens.
1459
1256
  */
1460
- function filterByTypes(tokens, types) {
1461
- const predicate = (token) => types.includes(token.type);
1257
+ function filterByTypes(tokens, types, htmlFlow) {
1258
+ const predicate = (token) =>
1259
+ (htmlFlow || !inHtmlFlow(token)) && types.includes(token.type);
1462
1260
  const flatTokens = tokens[flatTokensSymbol];
1463
1261
  if (flatTokens) {
1464
1262
  return flatTokens.filter(predicate);
@@ -1466,6 +1264,46 @@ function filterByTypes(tokens, types) {
1466
1264
  return filterByPredicate(tokens, predicate);
1467
1265
  }
1468
1266
 
1267
+ /**
1268
+ * Gets a list of nested Micromark token descendants by type path.
1269
+ *
1270
+ * @param {Token|Token[]} parent Micromark token parent or parents.
1271
+ * @param {TokenType[]} typePath Micromark token type path.
1272
+ * @returns {Token[]} Micromark token descendants.
1273
+ */
1274
+ function getDescendantsByType(parent, typePath) {
1275
+ let tokens = Array.isArray(parent) ? parent : [ parent ];
1276
+ for (const type of typePath) {
1277
+ tokens = tokens.flatMap((t) => t.children).filter((t) => t.type === type);
1278
+ }
1279
+ return tokens;
1280
+ }
1281
+
1282
+ // eslint-disable-next-line jsdoc/valid-types
1283
+ /** @typedef {readonly string[]} ReadonlyStringArray */
1284
+
1285
+ /**
1286
+ * Gets the line/column/length exclusions for a Micromark token.
1287
+ *
1288
+ * @param {ReadonlyStringArray} lines File/string lines.
1289
+ * @param {Token} token Micromark token.
1290
+ * @returns {number[][]} Exclusions (line number, start column, length).
1291
+ */
1292
+ function getExclusionsForToken(lines, token) {
1293
+ const exclusions = [];
1294
+ const { endColumn, endLine, startColumn, startLine } = token;
1295
+ for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++) {
1296
+ const start = (lineNumber === startLine) ? startColumn : 1;
1297
+ const end = (lineNumber === endLine) ? endColumn : lines[lineNumber - 1].length;
1298
+ exclusions.push([
1299
+ lineNumber,
1300
+ start,
1301
+ end - start + 1
1302
+ ]);
1303
+ }
1304
+ return exclusions;
1305
+ }
1306
+
1469
1307
  /**
1470
1308
  * Gets the heading level of a Micromark heading tokan.
1471
1309
  *
@@ -1487,6 +1325,40 @@ function getHeadingLevel(heading) {
1487
1325
  return level;
1488
1326
  }
1489
1327
 
1328
+ /**
1329
+ * Gets the heading style of a Micromark heading tokan.
1330
+ *
1331
+ * @param {Token} heading Micromark heading token.
1332
+ * @returns {"atx" | "atx_closed" | "setext"} Heading style.
1333
+ */
1334
+ function getHeadingStyle(heading) {
1335
+ if (heading.type === "setextHeading") {
1336
+ return "setext";
1337
+ }
1338
+ const atxHeadingSequenceLength = filterByTypes(
1339
+ heading.children,
1340
+ [ "atxHeadingSequence" ]
1341
+ ).length;
1342
+ if (atxHeadingSequenceLength === 1) {
1343
+ return "atx";
1344
+ }
1345
+ return "atx_closed";
1346
+ }
1347
+
1348
+ /**
1349
+ * Gets the heading text of a Micromark heading token.
1350
+ *
1351
+ * @param {Token} heading Micromark heading token.
1352
+ * @returns {string} Heading text.
1353
+ */
1354
+ function getHeadingText(heading) {
1355
+ const headingTexts = filterByTypes(
1356
+ heading.children,
1357
+ [ "atxHeadingText", "setextHeadingText" ]
1358
+ );
1359
+ return headingTexts[0]?.text.replace(/[\r\n]+/g, " ") || "";
1360
+ }
1361
+
1490
1362
  /**
1491
1363
  * HTML tag information.
1492
1364
  *
@@ -1545,16 +1417,6 @@ function getTokenTextByType(tokens, type) {
1545
1417
  return (filtered.length > 0) ? filtered[0].text : null;
1546
1418
  }
1547
1419
 
1548
- /**
1549
- * Determines if a Micromark token has an htmlFlow-type parent.
1550
- *
1551
- * @param {Token} token Micromark token.
1552
- * @returns {boolean} True iff the token has an htmlFlow-type parent.
1553
- */
1554
- function inHtmlFlow(token) {
1555
- return getTokenParentOfType(token, [ "htmlFlow" ]) !== null;
1556
- }
1557
-
1558
1420
  /**
1559
1421
  * Determines a list of Micromark tokens matches and returns a subset.
1560
1422
  *
@@ -1608,9 +1470,14 @@ const nonContentTokens = new Set([
1608
1470
 
1609
1471
  module.exports = {
1610
1472
  "parse": micromarkParse,
1473
+ addRangeToSet,
1611
1474
  filterByPredicate,
1612
1475
  filterByTypes,
1476
+ getDescendantsByType,
1477
+ getExclusionsForToken,
1613
1478
  getHeadingLevel,
1479
+ getHeadingStyle,
1480
+ getHeadingText,
1614
1481
  getHtmlTagInfo,
1615
1482
  getMicromarkEvents,
1616
1483
  getTokenParentOfType,
@@ -1629,30 +1496,79 @@ module.exports = {
1629
1496
  /*!***********************!*\
1630
1497
  !*** ../lib/cache.js ***!
1631
1498
  \***********************/
1632
- /***/ ((module) => {
1499
+ /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
1633
1500
 
1634
1501
  "use strict";
1635
1502
  // @ts-check
1636
1503
 
1637
1504
 
1638
1505
 
1506
+ const helpers = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
1507
+ const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
1508
+
1509
+ /** @type {Map<string, object>} */
1639
1510
  const map = new Map();
1511
+ // eslint-disable-next-line no-undef-init
1512
+ let params = undefined;
1513
+
1514
+ /**
1515
+ * Initializes (resets) the cache.
1516
+ *
1517
+ * @param {import("./markdownlint").RuleParams} [p] Rule parameters object.
1518
+ * @returns {void}
1519
+ */
1520
+ function initialize(p) {
1521
+ map.clear();
1522
+ params = p;
1523
+ }
1640
1524
 
1641
- module.exports.set = (keyValuePairs) => {
1642
- for (const [ key, value ] of Object.entries(keyValuePairs)) {
1643
- map.set(key, value);
1525
+ /**
1526
+ * Gets a cached object value - computes it and caches it.
1527
+ *
1528
+ * @param {string} name Cache object name.
1529
+ * @param {Function} getValue Getter for object value.
1530
+ * @returns {Object} Object value.
1531
+ */
1532
+ function getCached(name, getValue) {
1533
+ if (map.has(name)) {
1534
+ return map.get(name);
1644
1535
  }
1645
- };
1646
- module.exports.clear = () => map.clear();
1536
+ const value = getValue();
1537
+ map.set(name, value);
1538
+ return value;
1539
+ }
1647
1540
 
1648
- module.exports.codeBlockAndSpanRanges =
1649
- () => map.get("codeBlockAndSpanRanges");
1650
- module.exports.flattenedLists =
1651
- () => map.get("flattenedLists");
1652
- module.exports.lineMetadata =
1653
- () => map.get("lineMetadata");
1654
- module.exports.referenceLinkImageData =
1655
- () => map.get("referenceLinkImageData");
1541
+ /**
1542
+ * Filters a list of Micromark tokens by type and caches the result.
1543
+ *
1544
+ * @param {import("./markdownlint").MicromarkTokenType[]} types Types to allow.
1545
+ * @param {boolean} [htmlFlow] Whether to include htmlFlow content.
1546
+ * @returns {import("./markdownlint").MicromarkToken[]} Filtered tokens.
1547
+ */
1548
+ function filterByTypesCached(types, htmlFlow) {
1549
+ return getCached(
1550
+ types.join("|"),
1551
+ () => filterByTypes(params.parsers.micromark.tokens, types, htmlFlow)
1552
+ );
1553
+ }
1554
+
1555
+ /**
1556
+ * Gets a reference link and image data object.
1557
+ *
1558
+ * @returns {Object} Reference link and image data object.
1559
+ */
1560
+ function getReferenceLinkImageData() {
1561
+ return getCached(
1562
+ getReferenceLinkImageData.name,
1563
+ () => helpers.getReferenceLinkImageData(params.parsers.micromark.tokens)
1564
+ );
1565
+ }
1566
+
1567
+ module.exports = {
1568
+ initialize,
1569
+ filterByTypesCached,
1570
+ getReferenceLinkImageData
1571
+ };
1656
1572
 
1657
1573
 
1658
1574
  /***/ }),
@@ -1674,10 +1590,11 @@ module.exports.fixableRuleNames = [
1674
1590
  "MD012", "MD014", "MD018", "MD019", "MD020", "MD021",
1675
1591
  "MD022", "MD023", "MD026", "MD027", "MD030", "MD031",
1676
1592
  "MD032", "MD034", "MD037", "MD038", "MD039", "MD044",
1677
- "MD047", "MD049", "MD050", "MD051", "MD053", "MD054"
1593
+ "MD047", "MD049", "MD050", "MD051", "MD053", "MD054",
1594
+ "MD058"
1678
1595
  ];
1679
1596
  module.exports.homepage = "https://github.com/DavidAnson/markdownlint";
1680
- module.exports.version = "0.34.0";
1597
+ module.exports.version = "0.35.0";
1681
1598
 
1682
1599
 
1683
1600
  /***/ }),
@@ -1695,7 +1612,6 @@ module.exports.version = "0.34.0";
1695
1612
 
1696
1613
  const path = __webpack_require__(/*! node:path */ "?9a52");
1697
1614
  const { promisify } = __webpack_require__(/*! node:util */ "?39e5");
1698
- const markdownit = __webpack_require__(/*! markdown-it */ "markdown-it");
1699
1615
  const micromark = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
1700
1616
  // const { deprecatedRuleNames } = require("./constants");
1701
1617
  const rules = __webpack_require__(/*! ./rules */ "../lib/rules.js");
@@ -1723,7 +1639,7 @@ function validateRuleList(ruleList, synchronous) {
1723
1639
  const allIds = {};
1724
1640
  for (const [ index, rule ] of ruleList.entries()) {
1725
1641
  const customIndex = index - rules.length;
1726
- // eslint-disable-next-line no-inner-declarations, jsdoc/require-jsdoc
1642
+ // eslint-disable-next-line jsdoc/require-jsdoc
1727
1643
  function newError(property, value) {
1728
1644
  return new Error(
1729
1645
  `Property '${property}' of custom rule at index ${customIndex} is incorrect: '${value}'.`);
@@ -1917,13 +1833,17 @@ function freezeToken(token) {
1917
1833
  /**
1918
1834
  * Annotate tokens with line/lineNumber and freeze them.
1919
1835
  *
1920
- * @param {MarkdownItToken[]} tokens Array of markdown-it tokens.
1836
+ * @param {import("markdown-it").Token[]} tokens Array of markdown-it tokens.
1921
1837
  * @param {string[]} lines Lines of Markdown content.
1922
1838
  * @returns {void}
1923
1839
  */
1924
1840
  function annotateAndFreezeTokens(tokens, lines) {
1925
1841
  let trMap = null;
1926
- for (const token of tokens) {
1842
+ // eslint-disable-next-line jsdoc/valid-types
1843
+ /** @type MarkdownItToken[] */
1844
+ // @ts-ignore
1845
+ const markdownItTokens = tokens;
1846
+ for (const token of markdownItTokens) {
1927
1847
  // Provide missing maps for table content
1928
1848
  if (token.type === "tr_open") {
1929
1849
  trMap = token.map;
@@ -2198,10 +2118,18 @@ function getEnabledRulesPerLineNumber(
2198
2118
  handleInlineConfig(lines, enableDisableFile);
2199
2119
  handleInlineConfig(lines, captureRestoreEnableDisable, updateLineState);
2200
2120
  handleInlineConfig(lines, disableLineNextLine);
2121
+ // Create the list of rules that are used at least once
2122
+ const enabledRuleList = [];
2123
+ for (const [ index, ruleName ] of allRuleNames.entries()) {
2124
+ if (enabledRulesPerLineNumber.some((enabledRulesForLine) => enabledRulesForLine[ruleName])) {
2125
+ enabledRuleList.push(ruleList[index]);
2126
+ }
2127
+ }
2201
2128
  // Return results
2202
2129
  return {
2203
2130
  effectiveConfig,
2204
- enabledRulesPerLineNumber
2131
+ enabledRulesPerLineNumber,
2132
+ enabledRuleList
2205
2133
  };
2206
2134
  }
2207
2135
 
@@ -2213,7 +2141,7 @@ function getEnabledRulesPerLineNumber(
2213
2141
  * names.
2214
2142
  * @param {string} name Identifier for the content.
2215
2143
  * @param {string} content Markdown content.
2216
- * @param {Object} md Instance of markdown-it.
2144
+ * @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
2217
2145
  * @param {Configuration} config Configuration object.
2218
2146
  * @param {ConfigurationParser[] | null} configParsers Configuration parsers.
2219
2147
  * @param {RegExp | null} frontMatter Regular expression for front matter.
@@ -2228,7 +2156,7 @@ function lintContent(
2228
2156
  aliasToRuleNames,
2229
2157
  name,
2230
2158
  content,
2231
- md,
2159
+ getMarkdownIt,
2232
2160
  config,
2233
2161
  configParsers,
2234
2162
  frontMatter,
@@ -2243,7 +2171,7 @@ function lintContent(
2243
2171
  const { frontMatterLines } = removeFrontMatterResult;
2244
2172
  content = removeFrontMatterResult.content;
2245
2173
  // Get enabled rules per line (with HTML comments present)
2246
- const { effectiveConfig, enabledRulesPerLineNumber } =
2174
+ const { effectiveConfig, enabledRulesPerLineNumber, enabledRuleList } =
2247
2175
  getEnabledRulesPerLineNumber(
2248
2176
  ruleList,
2249
2177
  content.split(helpers.newLineRe),
@@ -2253,8 +2181,11 @@ function lintContent(
2253
2181
  configParsers,
2254
2182
  aliasToRuleNames
2255
2183
  );
2184
+ const needMarkdownItTokens = enabledRuleList.some(
2185
+ (rule) => (rule.parser === "markdownit") || (rule.parser === undefined)
2186
+ );
2256
2187
  // Parse content into parser tokens
2257
- const markdownitTokens = md.parse(content, {});
2188
+ const markdownitTokens = needMarkdownItTokens ? getMarkdownIt().parse(content, {}) : [];
2258
2189
  const micromarkTokens = micromark.parse(content);
2259
2190
  // Hide the content of HTML comments from rules
2260
2191
  content = helpers.clearHtmlCommentText(content);
@@ -2281,23 +2212,13 @@ function lintContent(
2281
2212
  const parsersNone = Object.freeze({});
2282
2213
  const paramsBase = {
2283
2214
  name,
2284
- "parsers": parsersMarkdownIt,
2285
2215
  "lines": Object.freeze(lines),
2286
2216
  "frontMatterLines": Object.freeze(frontMatterLines)
2287
2217
  };
2288
- const lineMetadata =
2289
- helpers.getLineMetadata(paramsBase);
2290
- const codeBlockAndSpanRanges =
2291
- helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata);
2292
- const flattenedLists =
2293
- helpers.flattenLists(markdownitTokens);
2294
- const referenceLinkImageData =
2295
- helpers.getReferenceLinkImageData(micromarkTokens);
2296
- cache.set({
2297
- codeBlockAndSpanRanges,
2298
- flattenedLists,
2299
- lineMetadata,
2300
- referenceLinkImageData
2218
+ cache.initialize({
2219
+ ...paramsBase,
2220
+ "parsers": parsersMicromark,
2221
+ "config": null
2301
2222
  });
2302
2223
  // Function to run for each rule
2303
2224
  let results = [];
@@ -2491,8 +2412,8 @@ function lintContent(
2491
2412
  return results;
2492
2413
  }
2493
2414
  // Run all rules
2494
- const ruleListAsync = ruleList.filter((rule) => rule.asynchronous);
2495
- const ruleListSync = ruleList.filter((rule) => !rule.asynchronous);
2415
+ const ruleListAsync = enabledRuleList.filter((rule) => rule.asynchronous);
2416
+ const ruleListSync = enabledRuleList.filter((rule) => !rule.asynchronous);
2496
2417
  const ruleListAsyncFirst = [
2497
2418
  ...ruleListAsync,
2498
2419
  ...ruleListSync
@@ -2512,7 +2433,7 @@ function lintContent(
2512
2433
  } catch (error) {
2513
2434
  callbackError(error);
2514
2435
  } finally {
2515
- cache.clear();
2436
+ cache.initialize();
2516
2437
  }
2517
2438
  }
2518
2439
 
@@ -2523,7 +2444,7 @@ function lintContent(
2523
2444
  * @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
2524
2445
  * names.
2525
2446
  * @param {string} file Path of file to lint.
2526
- * @param {Object} md Instance of markdown-it.
2447
+ * @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
2527
2448
  * @param {Configuration} config Configuration object.
2528
2449
  * @param {ConfigurationParser[] | null} configParsers Configuration parsers.
2529
2450
  * @param {RegExp | null} frontMatter Regular expression for front matter.
@@ -2539,7 +2460,7 @@ function lintFile(
2539
2460
  ruleList,
2540
2461
  aliasToRuleNames,
2541
2462
  file,
2542
- md,
2463
+ getMarkdownIt,
2543
2464
  config,
2544
2465
  configParsers,
2545
2466
  frontMatter,
@@ -2559,7 +2480,7 @@ function lintFile(
2559
2480
  aliasToRuleNames,
2560
2481
  file,
2561
2482
  content,
2562
- md,
2483
+ getMarkdownIt,
2563
2484
  config,
2564
2485
  configParsers,
2565
2486
  frontMatter,
@@ -2624,12 +2545,16 @@ function lintInput(options, synchronous, callback) {
2624
2545
  const noInlineConfig = !!options.noInlineConfig;
2625
2546
  const resultVersion = (options.resultVersion === undefined) ?
2626
2547
  3 : options.resultVersion;
2627
- const md = markdownit({ "html": true });
2628
- const markdownItPlugins = options.markdownItPlugins || [];
2629
- for (const plugin of markdownItPlugins) {
2630
- // @ts-ignore
2631
- md.use(...plugin);
2632
- }
2548
+ const getMarkdownIt = () => {
2549
+ const markdownit = __webpack_require__(/*! markdown-it */ "markdown-it");
2550
+ const md = markdownit({ "html": true });
2551
+ const markdownItPlugins = options.markdownItPlugins || [];
2552
+ for (const plugin of markdownItPlugins) {
2553
+ // @ts-ignore
2554
+ md.use(...plugin);
2555
+ }
2556
+ return md;
2557
+ };
2633
2558
  const fs = options.fs || __webpack_require__(/*! node:fs */ "?d0ee");
2634
2559
  const aliasToRuleNames = mapAliasToRuleNames(ruleList);
2635
2560
  const results = newResults(ruleList);
@@ -2661,7 +2586,7 @@ function lintInput(options, synchronous, callback) {
2661
2586
  ruleList,
2662
2587
  aliasToRuleNames,
2663
2588
  currentItem,
2664
- md,
2589
+ getMarkdownIt,
2665
2590
  config,
2666
2591
  configParsers,
2667
2592
  frontMatter,
@@ -2680,7 +2605,7 @@ function lintInput(options, synchronous, callback) {
2680
2605
  aliasToRuleNames,
2681
2606
  currentItem,
2682
2607
  strings[currentItem] || "",
2683
- md,
2608
+ getMarkdownIt,
2684
2609
  config,
2685
2610
  configParsers,
2686
2611
  frontMatter,
@@ -2995,6 +2920,13 @@ module.exports = markdownlint;
2995
2920
 
2996
2921
  // Type declarations
2997
2922
 
2923
+ /**
2924
+ * Function to get an instance of the markdown-it parser.
2925
+ *
2926
+ * @callback GetMarkdownIt
2927
+ * @returns {import("markdown-it")}
2928
+ */
2929
+
2998
2930
  /**
2999
2931
  * Function to implement rule logic.
3000
2932
  *
@@ -3208,6 +3140,13 @@ module.exports = markdownlint;
3208
3140
  * @typedef {import("./configuration").Configuration} Configuration
3209
3141
  */
3210
3142
 
3143
+ /**
3144
+ * Configuration object for linting rules strictly. For the JSON schema, see
3145
+ * {@link ../schema/markdownlint-config-schema-strict.json}.
3146
+ *
3147
+ * @typedef {import("./configuration-strict").ConfigurationStrict} ConfigurationStrict
3148
+ */
3149
+
3211
3150
  /**
3212
3151
  * Rule configuration object.
3213
3152
  *
@@ -3254,7 +3193,9 @@ module.exports = markdownlint;
3254
3193
 
3255
3194
 
3256
3195
 
3257
- const { addErrorDetailIf, filterTokens } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3196
+ const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3197
+ const { getHeadingLevel } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3198
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3258
3199
 
3259
3200
  // eslint-disable-next-line jsdoc/valid-types
3260
3201
  /** @type import("./markdownlint").Rule */
@@ -3262,17 +3203,21 @@ module.exports = {
3262
3203
  "names": [ "MD001", "heading-increment" ],
3263
3204
  "description": "Heading levels should only increment by one level at a time",
3264
3205
  "tags": [ "headings" ],
3265
- "parser": "markdownit",
3206
+ "parser": "micromark",
3266
3207
  "function": function MD001(params, onError) {
3267
- let prevLevel = 0;
3268
- filterTokens(params, "heading_open", function forToken(token) {
3269
- const level = Number.parseInt(token.tag.slice(1), 10);
3270
- if (prevLevel && (level > prevLevel)) {
3271
- addErrorDetailIf(onError, token.lineNumber,
3272
- "h" + (prevLevel + 1), "h" + level);
3208
+ let prevLevel = Number.MAX_SAFE_INTEGER;
3209
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
3210
+ const level = getHeadingLevel(heading);
3211
+ if (level > prevLevel) {
3212
+ addErrorDetailIf(
3213
+ onError,
3214
+ heading.startLine,
3215
+ `h${prevLevel + 1}`,
3216
+ `h${level}`
3217
+ );
3273
3218
  }
3274
3219
  prevLevel = level;
3275
- });
3220
+ }
3276
3221
  }
3277
3222
  };
3278
3223
 
@@ -3290,8 +3235,9 @@ module.exports = {
3290
3235
 
3291
3236
 
3292
3237
 
3293
- const { addErrorDetailIf, filterTokens, headingStyleFor } =
3294
- __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3238
+ const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3239
+ const { getHeadingLevel, getHeadingStyle } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3240
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3295
3241
 
3296
3242
  // eslint-disable-next-line jsdoc/valid-types
3297
3243
  /** @type import("./markdownlint").Rule */
@@ -3299,16 +3245,16 @@ module.exports = {
3299
3245
  "names": [ "MD003", "heading-style" ],
3300
3246
  "description": "Heading style",
3301
3247
  "tags": [ "headings" ],
3302
- "parser": "markdownit",
3248
+ "parser": "micromark",
3303
3249
  "function": function MD003(params, onError) {
3304
3250
  let style = String(params.config.style || "consistent");
3305
- filterTokens(params, "heading_open", function forToken(token) {
3306
- const styleForToken = headingStyleFor(token);
3251
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
3252
+ const styleForToken = getHeadingStyle(heading);
3307
3253
  if (style === "consistent") {
3308
3254
  style = styleForToken;
3309
3255
  }
3310
3256
  if (styleForToken !== style) {
3311
- const h12 = /h[12]/.test(token.tag);
3257
+ const h12 = getHeadingLevel(heading) <= 2;
3312
3258
  const setextWithAtx =
3313
3259
  (style === "setext_with_atx") &&
3314
3260
  ((h12 && (styleForToken === "setext")) ||
@@ -3324,11 +3270,15 @@ module.exports = {
3324
3270
  } else if (style === "setext_with_atx_closed") {
3325
3271
  expected = h12 ? "setext" : "atx_closed";
3326
3272
  }
3327
- addErrorDetailIf(onError, token.lineNumber,
3328
- expected, styleForToken);
3273
+ addErrorDetailIf(
3274
+ onError,
3275
+ heading.startLine,
3276
+ expected,
3277
+ styleForToken
3278
+ );
3329
3279
  }
3330
3280
  }
3331
- });
3281
+ }
3332
3282
  }
3333
3283
  };
3334
3284
 
@@ -3346,11 +3296,16 @@ module.exports = {
3346
3296
 
3347
3297
 
3348
3298
 
3349
- const { addErrorDetailIf, listItemMarkerRe, unorderedListStyleFor } =
3350
- __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3351
- const { flattenedLists } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3299
+ const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3300
+ const { getDescendantsByType, getTokenParentOfType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3301
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3352
3302
 
3353
- const expectedStyleToMarker = {
3303
+ const markerToStyle = {
3304
+ "-": "dash",
3305
+ "+": "plus",
3306
+ "*": "asterisk"
3307
+ };
3308
+ const styleToMarker = {
3354
3309
  "dash": "-",
3355
3310
  "plus": "+",
3356
3311
  "asterisk": "*"
@@ -3360,7 +3315,13 @@ const differentItemStyle = {
3360
3315
  "plus": "asterisk",
3361
3316
  "asterisk": "dash"
3362
3317
  };
3363
- const validStyles = Object.keys(expectedStyleToMarker);
3318
+ const validStyles = new Set([
3319
+ "asterisk",
3320
+ "consistent",
3321
+ "dash",
3322
+ "plus",
3323
+ "sublist"
3324
+ ]);
3364
3325
 
3365
3326
  // eslint-disable-next-line jsdoc/valid-types
3366
3327
  /** @type import("./markdownlint").Rule */
@@ -3368,55 +3329,51 @@ module.exports = {
3368
3329
  "names": [ "MD004", "ul-style" ],
3369
3330
  "description": "Unordered list style",
3370
3331
  "tags": [ "bullet", "ul" ],
3371
- "parser": "none",
3332
+ "parser": "micromark",
3372
3333
  "function": function MD004(params, onError) {
3373
3334
  const style = String(params.config.style || "consistent");
3374
- let expectedStyle = style;
3335
+ let expectedStyle = validStyles.has(style) ? style : "dash";
3375
3336
  const nestingStyles = [];
3376
- for (const list of flattenedLists()) {
3377
- if (list.unordered) {
3378
- if (expectedStyle === "consistent") {
3379
- expectedStyle = unorderedListStyleFor(list.items[0]);
3337
+ for (const listUnordered of filterByTypesCached([ "listUnordered" ])) {
3338
+ let nesting = 0;
3339
+ if (style === "sublist") {
3340
+ /** @type {import("../helpers/micromark.cjs").Token | null} */
3341
+ let parent = listUnordered;
3342
+ // @ts-ignore
3343
+ while ((parent = getTokenParentOfType(parent, [ "listOrdered", "listUnordered" ]))) {
3344
+ nesting++;
3380
3345
  }
3381
- for (const item of list.items) {
3382
- const itemStyle = unorderedListStyleFor(item);
3383
- if (style === "sublist") {
3384
- const nesting = list.nesting;
3385
- if (!nestingStyles[nesting]) {
3386
- nestingStyles[nesting] =
3387
- (itemStyle === nestingStyles[nesting - 1]) ?
3388
- differentItemStyle[itemStyle] :
3389
- itemStyle;
3390
- }
3391
- expectedStyle = nestingStyles[nesting];
3392
- }
3393
- if (!validStyles.includes(expectedStyle)) {
3394
- expectedStyle = validStyles[0];
3395
- }
3396
- let range = null;
3397
- let fixInfo = null;
3398
- const match = item.line.match(listItemMarkerRe);
3399
- if (match) {
3400
- const column = match.index + 1;
3401
- const length = match[0].length;
3402
- range = [ column, length ];
3403
- fixInfo = {
3404
- "editColumn": match[1].length + 1,
3405
- "deleteCount": 1,
3406
- "insertText": expectedStyleToMarker[expectedStyle]
3407
- };
3346
+ }
3347
+ const listItemMarkers = getDescendantsByType(listUnordered, [ "listItemPrefix", "listItemMarker" ]);
3348
+ for (const listItemMarker of listItemMarkers) {
3349
+ const itemStyle = markerToStyle[listItemMarker.text];
3350
+ if (style === "sublist") {
3351
+ if (!nestingStyles[nesting]) {
3352
+ nestingStyles[nesting] =
3353
+ (itemStyle === nestingStyles[nesting - 1]) ?
3354
+ differentItemStyle[itemStyle] :
3355
+ itemStyle;
3408
3356
  }
3409
- addErrorDetailIf(
3410
- onError,
3411
- item.lineNumber,
3412
- expectedStyle,
3413
- itemStyle,
3414
- null,
3415
- null,
3416
- range,
3417
- fixInfo
3418
- );
3357
+ expectedStyle = nestingStyles[nesting];
3358
+ } else if (expectedStyle === "consistent") {
3359
+ expectedStyle = itemStyle;
3419
3360
  }
3361
+ const column = listItemMarker.startColumn;
3362
+ const length = listItemMarker.endColumn - listItemMarker.startColumn;
3363
+ addErrorDetailIf(
3364
+ onError,
3365
+ listItemMarker.startLine,
3366
+ expectedStyle,
3367
+ itemStyle,
3368
+ undefined,
3369
+ undefined,
3370
+ [ column, length ],
3371
+ {
3372
+ "editColumn": column,
3373
+ "deleteCount": length,
3374
+ "insertText": styleToMarker[expectedStyle]
3375
+ }
3376
+ );
3420
3377
  }
3421
3378
  }
3422
3379
  }
@@ -3437,7 +3394,7 @@ module.exports = {
3437
3394
 
3438
3395
 
3439
3396
  const { addError, addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3440
- const { filterByTypes, inHtmlFlow } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3397
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3441
3398
 
3442
3399
  // eslint-disable-next-line jsdoc/valid-types
3443
3400
  /** @type import("./markdownlint").Rule */
@@ -3447,16 +3404,7 @@ module.exports = {
3447
3404
  "tags": [ "bullet", "ul", "indentation" ],
3448
3405
  "parser": "micromark",
3449
3406
  "function": function MD005(params, onError) {
3450
- // eslint-disable-next-line jsdoc/valid-types
3451
- /** @type import("../helpers/micromark.cjs").Token[] */
3452
- const micromarkTokens =
3453
- // @ts-ignore
3454
- params.parsers.micromark.tokens;
3455
- const lists = filterByTypes(
3456
- micromarkTokens,
3457
- [ "listOrdered", "listUnordered" ]
3458
- ).filter((list) => !inHtmlFlow(list));
3459
- for (const list of lists) {
3407
+ for (const list of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
3460
3408
  const expectedIndent = list.startColumn - 1;
3461
3409
  let expectedEnd = 0;
3462
3410
  let endMatching = false;
@@ -3472,8 +3420,8 @@ module.exports = {
3472
3420
  lineNumber,
3473
3421
  expectedIndent,
3474
3422
  actualIndent,
3475
- null,
3476
- null,
3423
+ undefined,
3424
+ undefined,
3477
3425
  range
3478
3426
  // No fixInfo; MD007 handles this scenario better
3479
3427
  );
@@ -3529,8 +3477,8 @@ module.exports = {
3529
3477
 
3530
3478
 
3531
3479
  const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3532
- const { filterByTypes, getTokenParentOfType, inHtmlFlow } =
3533
- __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3480
+ const { getTokenParentOfType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3481
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3534
3482
 
3535
3483
  // eslint-disable-next-line jsdoc/valid-types
3536
3484
  /** @type import("markdownlint-micromark").TokenType[] */
@@ -3552,14 +3500,9 @@ module.exports = {
3552
3500
  const indent = Number(params.config.indent || 2);
3553
3501
  const startIndented = !!params.config.start_indented;
3554
3502
  const startIndent = Number(params.config.start_indent || indent);
3555
- // eslint-disable-next-line jsdoc/valid-types
3556
- /** @type import("../helpers/micromark.cjs").Token[] */
3557
- const micromarkTokens =
3558
- // @ts-ignore
3559
- params.parsers.micromark.tokens;
3560
3503
  const unorderedListNesting = new Map();
3561
3504
  let lastBlockQuotePrefix = null;
3562
- const tokens = filterByTypes(micromarkTokens, unorderedListTypes);
3505
+ const tokens = filterByTypesCached(unorderedListTypes);
3563
3506
  for (const token of tokens) {
3564
3507
  const { endColumn, parent, startColumn, startLine, type } = token;
3565
3508
  if (type === "blockQuotePrefix") {
@@ -3569,6 +3512,7 @@ module.exports = {
3569
3512
  /** @type {import("../helpers/micromark.cjs").Token | null} */
3570
3513
  let current = token;
3571
3514
  while (
3515
+ // @ts-ignore
3572
3516
  (current = getTokenParentOfType(current, unorderedParentTypes))
3573
3517
  ) {
3574
3518
  if (current.type === "listUnordered") {
@@ -3583,7 +3527,7 @@ module.exports = {
3583
3527
  if (nesting >= 0) {
3584
3528
  unorderedListNesting.set(token, nesting);
3585
3529
  }
3586
- } else if (!inHtmlFlow(token)) {
3530
+ } else {
3587
3531
  // listItemPrefix
3588
3532
  const nesting = unorderedListNesting.get(parent);
3589
3533
  if (nesting !== undefined) {
@@ -3631,9 +3575,9 @@ module.exports = {
3631
3575
 
3632
3576
 
3633
3577
 
3634
- const { addError, filterTokens, forEachLine, includesSorted,
3635
- numericSortAscending } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3636
- const { lineMetadata } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3578
+ const { addError } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3579
+ const { addRangeToSet } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3580
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3637
3581
 
3638
3582
  // eslint-disable-next-line jsdoc/valid-types
3639
3583
  /** @type import("./markdownlint").Rule */
@@ -3641,63 +3585,68 @@ module.exports = {
3641
3585
  "names": [ "MD009", "no-trailing-spaces" ],
3642
3586
  "description": "Trailing spaces",
3643
3587
  "tags": [ "whitespace" ],
3644
- "parser": "markdownit",
3588
+ "parser": "micromark",
3645
3589
  "function": function MD009(params, onError) {
3646
3590
  let brSpaces = params.config.br_spaces;
3647
3591
  brSpaces = Number((brSpaces === undefined) ? 2 : brSpaces);
3648
3592
  const listItemEmptyLines = !!params.config.list_item_empty_lines;
3649
3593
  const strict = !!params.config.strict;
3650
- const listItemLineNumbers = [];
3594
+ const codeBlockLineNumbers = new Set();
3595
+ for (const codeBlock of filterByTypesCached([ "codeFenced" ])) {
3596
+ addRangeToSet(codeBlockLineNumbers, codeBlock.startLine + 1, codeBlock.endLine - 1);
3597
+ }
3598
+ for (const codeBlock of filterByTypesCached([ "codeIndented" ])) {
3599
+ addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
3600
+ }
3601
+ const listItemLineNumbers = new Set();
3651
3602
  if (listItemEmptyLines) {
3652
- filterTokens(params, "list_item_open", (token) => {
3653
- for (let i = token.map[0]; i < token.map[1]; i++) {
3654
- listItemLineNumbers.push(i + 1);
3603
+ for (const listBlock of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
3604
+ addRangeToSet(listItemLineNumbers, listBlock.startLine, listBlock.endLine);
3605
+ let trailingIndent = true;
3606
+ for (let i = listBlock.children.length - 1; i >= 0; i--) {
3607
+ const child = listBlock.children[i];
3608
+ switch (child.type) {
3609
+ case "content":
3610
+ trailingIndent = false;
3611
+ break;
3612
+ case "listItemIndent":
3613
+ if (trailingIndent) {
3614
+ listItemLineNumbers.delete(child.startLine);
3615
+ }
3616
+ break;
3617
+ case "listItemPrefix":
3618
+ trailingIndent = true;
3619
+ break;
3620
+ default:
3621
+ break;
3622
+ }
3655
3623
  }
3656
- });
3657
- listItemLineNumbers.sort(numericSortAscending);
3624
+ }
3658
3625
  }
3659
- const paragraphLineNumbers = [];
3660
- const codeInlineLineNumbers = [];
3626
+ const paragraphLineNumbers = new Set();
3627
+ const codeInlineLineNumbers = new Set();
3661
3628
  if (strict) {
3662
- filterTokens(params, "paragraph_open", (token) => {
3663
- for (let i = token.map[0]; i < token.map[1] - 1; i++) {
3664
- paragraphLineNumbers.push(i + 1);
3665
- }
3666
- });
3667
- const addLineNumberRange = (start, end) => {
3668
- for (let i = start; i < end; i++) {
3669
- codeInlineLineNumbers.push(i);
3670
- }
3671
- };
3672
- filterTokens(params, "inline", (token) => {
3673
- let start = 0;
3674
- for (const child of token.children) {
3675
- if (start > 0) {
3676
- addLineNumberRange(start, child.lineNumber);
3677
- start = 0;
3678
- }
3679
- if (child.type === "code_inline") {
3680
- start = child.lineNumber;
3681
- }
3682
- }
3683
- if (start > 0) {
3684
- addLineNumberRange(start, token.map[1]);
3685
- }
3686
- });
3629
+ for (const paragraph of filterByTypesCached([ "paragraph" ])) {
3630
+ addRangeToSet(paragraphLineNumbers, paragraph.startLine, paragraph.endLine - 1);
3631
+ }
3632
+ for (const codeText of filterByTypesCached([ "codeText" ])) {
3633
+ addRangeToSet(codeInlineLineNumbers, codeText.startLine, codeText.endLine - 1);
3634
+ }
3687
3635
  }
3688
3636
  const expected = (brSpaces < 2) ? 0 : brSpaces;
3689
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
3637
+ for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
3638
+ const line = params.lines[lineIndex];
3690
3639
  const lineNumber = lineIndex + 1;
3691
3640
  const trailingSpaces = line.length - line.trimEnd().length;
3692
3641
  if (
3693
3642
  trailingSpaces &&
3694
- !inCode &&
3695
- !includesSorted(listItemLineNumbers, lineNumber) &&
3643
+ !codeBlockLineNumbers.has(lineNumber) &&
3644
+ !listItemLineNumbers.has(lineNumber) &&
3696
3645
  (
3697
3646
  (expected !== trailingSpaces) ||
3698
3647
  (strict &&
3699
- (!includesSorted(paragraphLineNumbers, lineNumber) ||
3700
- includesSorted(codeInlineLineNumbers, lineNumber)))
3648
+ (!paragraphLineNumbers.has(lineNumber) ||
3649
+ codeInlineLineNumbers.has(lineNumber)))
3701
3650
  )
3702
3651
  ) {
3703
3652
  const column = line.length - trailingSpaces + 1;
@@ -3711,9 +3660,10 @@ module.exports = {
3711
3660
  {
3712
3661
  "editColumn": column,
3713
3662
  "deleteCount": trailingSpaces
3714
- });
3663
+ }
3664
+ );
3715
3665
  }
3716
- });
3666
+ }
3717
3667
  }
3718
3668
  };
3719
3669
 
@@ -3731,9 +3681,9 @@ module.exports = {
3731
3681
 
3732
3682
 
3733
3683
 
3734
- const { addError, filterTokens, forEachLine, withinAnyRange } =
3735
- __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3736
- const { codeBlockAndSpanRanges, lineMetadata } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3684
+ const { addError, withinAnyRange } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3685
+ const { getDescendantsByType, getExclusionsForToken } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3686
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3737
3687
 
3738
3688
  const tabRe = /\t+/g;
3739
3689
 
@@ -3743,7 +3693,7 @@ module.exports = {
3743
3693
  "names": [ "MD010", "no-hard-tabs" ],
3744
3694
  "description": "Hard tabs",
3745
3695
  "tags": [ "whitespace", "hard_tab" ],
3746
- "parser": "markdownit",
3696
+ "parser": "micromark",
3747
3697
  "function": function MD010(params, onError) {
3748
3698
  const codeBlocks = params.config.code_blocks;
3749
3699
  const includeCode = (codeBlocks === undefined) ? true : !!codeBlocks;
@@ -3755,39 +3705,54 @@ module.exports = {
3755
3705
  const spaceMultiplier = (spacesPerTab === undefined) ?
3756
3706
  1 :
3757
3707
  Math.max(0, Number(spacesPerTab));
3758
- const exclusions = includeCode ? [] : codeBlockAndSpanRanges();
3759
- filterTokens(params, "fence", (token) => {
3760
- const language = token.info.trim().toLowerCase();
3761
- if (ignoreCodeLanguages.has(language)) {
3762
- for (let i = token.map[0] + 1; i < token.map[1] - 1; i++) {
3763
- exclusions.push([ i, 0, params.lines[i].length ]);
3764
- }
3708
+ const exclusions = [];
3709
+ // eslint-disable-next-line jsdoc/valid-types
3710
+ /** @type import("../helpers/micromark.cjs").TokenType[] */
3711
+ const exclusionTypes = [];
3712
+ if (includeCode) {
3713
+ if (ignoreCodeLanguages.size > 0) {
3714
+ exclusionTypes.push("codeFenced");
3765
3715
  }
3716
+ } else {
3717
+ exclusionTypes.push("codeFenced", "codeIndented", "codeText");
3718
+ }
3719
+ const codeTokens = filterByTypesCached(exclusionTypes).filter((token) => {
3720
+ if ((token.type === "codeFenced") && (ignoreCodeLanguages.size > 0)) {
3721
+ const fenceInfos = getDescendantsByType(token, [ "codeFencedFence", "codeFencedFenceInfo" ]);
3722
+ return fenceInfos.every((fenceInfo) => ignoreCodeLanguages.has(fenceInfo.text.toLowerCase()));
3723
+ }
3724
+ return true;
3766
3725
  });
3767
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
3768
- if (includeCode || !inCode) {
3769
- let match = null;
3770
- while ((match = tabRe.exec(line)) !== null) {
3771
- const { index } = match;
3772
- const column = index + 1;
3773
- const length = match[0].length;
3774
- if (!withinAnyRange(exclusions, lineIndex, index, length)) {
3775
- addError(
3776
- onError,
3777
- lineIndex + 1,
3778
- "Column: " + column,
3779
- undefined,
3780
- [ column, length ],
3781
- {
3782
- "editColumn": column,
3783
- "deleteCount": length,
3784
- "insertText": "".padEnd(length * spaceMultiplier)
3785
- }
3786
- );
3787
- }
3726
+ for (const codeToken of codeTokens) {
3727
+ const exclusionsForToken = getExclusionsForToken(params.lines, codeToken);
3728
+ if (codeToken.type === "codeFenced") {
3729
+ exclusionsForToken.pop();
3730
+ exclusionsForToken.shift();
3731
+ }
3732
+ exclusions.push(...exclusionsForToken);
3733
+ }
3734
+ for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
3735
+ const line = params.lines[lineIndex];
3736
+ let match = null;
3737
+ while ((match = tabRe.exec(line)) !== null) {
3738
+ const column = match.index + 1;
3739
+ const length = match[0].length;
3740
+ if (!withinAnyRange(exclusions, lineIndex + 1, column, length)) {
3741
+ addError(
3742
+ onError,
3743
+ lineIndex + 1,
3744
+ "Column: " + column,
3745
+ undefined,
3746
+ [ column, length ],
3747
+ {
3748
+ "editColumn": column,
3749
+ "deleteCount": length,
3750
+ "insertText": "".padEnd(length * spaceMultiplier)
3751
+ }
3752
+ );
3788
3753
  }
3789
3754
  }
3790
- });
3755
+ }
3791
3756
  }
3792
3757
  };
3793
3758
 
@@ -3805,8 +3770,9 @@ module.exports = {
3805
3770
 
3806
3771
 
3807
3772
 
3808
- const { addError, forEachLine, withinAnyRange } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3809
- const { codeBlockAndSpanRanges, lineMetadata } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3773
+ const { addError, withinAnyRange } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3774
+ const { addRangeToSet, getExclusionsForToken } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3775
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3810
3776
 
3811
3777
  const reversedLinkRe =
3812
3778
  /(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\](?!\()/g;
@@ -3817,11 +3783,18 @@ module.exports = {
3817
3783
  "names": [ "MD011", "no-reversed-links" ],
3818
3784
  "description": "Reversed link syntax",
3819
3785
  "tags": [ "links" ],
3820
- "parser": "none",
3786
+ "parser": "micromark",
3821
3787
  "function": function MD011(params, onError) {
3822
- const exclusions = codeBlockAndSpanRanges();
3823
- forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
3824
- if (!inCode && !onFence) {
3788
+ const codeBlockLineNumbers = new Set();
3789
+ for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
3790
+ addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
3791
+ }
3792
+ const exclusions = [];
3793
+ for (const codeText of filterByTypesCached([ "codeText" ])) {
3794
+ exclusions.push(...getExclusionsForToken(params.lines, codeText));
3795
+ }
3796
+ for (const [ lineIndex, line ] of params.lines.entries()) {
3797
+ if (!codeBlockLineNumbers.has(lineIndex + 1)) {
3825
3798
  let match = null;
3826
3799
  while ((match = reversedLinkRe.exec(line)) !== null) {
3827
3800
  const [ reversedLink, preChar, linkText, linkDestination ] = match;
@@ -3830,7 +3803,7 @@ module.exports = {
3830
3803
  if (
3831
3804
  !linkText.endsWith("\\") &&
3832
3805
  !linkDestination.endsWith("\\") &&
3833
- !withinAnyRange(exclusions, lineIndex, index, length)
3806
+ !withinAnyRange(exclusions, lineIndex + 1, index, length)
3834
3807
  ) {
3835
3808
  addError(
3836
3809
  onError,
@@ -3847,7 +3820,7 @@ module.exports = {
3847
3820
  }
3848
3821
  }
3849
3822
  }
3850
- });
3823
+ }
3851
3824
  }
3852
3825
  };
3853
3826
 
@@ -3865,8 +3838,9 @@ module.exports = {
3865
3838
 
3866
3839
 
3867
3840
 
3868
- const { addErrorDetailIf, forEachLine } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3869
- const { lineMetadata } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3841
+ const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3842
+ const { addRangeToSet } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3843
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3870
3844
 
3871
3845
  // eslint-disable-next-line jsdoc/valid-types
3872
3846
  /** @type import("./markdownlint").Rule */
@@ -3874,11 +3848,17 @@ module.exports = {
3874
3848
  "names": [ "MD012", "no-multiple-blanks" ],
3875
3849
  "description": "Multiple consecutive blank lines",
3876
3850
  "tags": [ "whitespace", "blank_lines" ],
3877
- "parser": "none",
3851
+ "parser": "micromark",
3878
3852
  "function": function MD012(params, onError) {
3879
3853
  const maximum = Number(params.config.maximum || 1);
3854
+ const { lines } = params;
3855
+ const codeBlockLineNumbers = new Set();
3856
+ for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
3857
+ addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
3858
+ }
3880
3859
  let count = 0;
3881
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
3860
+ for (const [ lineIndex, line ] of lines.entries()) {
3861
+ const inCode = codeBlockLineNumbers.has(lineIndex + 1);
3882
3862
  count = (inCode || (line.trim().length > 0)) ? 0 : count + 1;
3883
3863
  if (maximum < count) {
3884
3864
  addErrorDetailIf(
@@ -3886,14 +3866,15 @@ module.exports = {
3886
3866
  lineIndex + 1,
3887
3867
  maximum,
3888
3868
  count,
3889
- null,
3890
- null,
3891
- null,
3869
+ undefined,
3870
+ undefined,
3871
+ undefined,
3892
3872
  {
3893
3873
  "deleteCount": -1
3894
- });
3874
+ }
3875
+ );
3895
3876
  }
3896
- });
3877
+ }
3897
3878
  }
3898
3879
  };
3899
3880
 
@@ -3911,25 +3892,15 @@ module.exports = {
3911
3892
 
3912
3893
 
3913
3894
 
3914
- const { addErrorDetailIf, filterTokens, forEachHeading, forEachLine,
3915
- includesSorted } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3916
- const { lineMetadata, referenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3895
+ const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
3896
+ const { getReferenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3897
+ const { addRangeToSet, getDescendantsByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
3898
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
3917
3899
 
3918
3900
  const longLineRePrefix = "^.{";
3919
3901
  const longLineRePostfixRelaxed = "}.*\\s.*$";
3920
3902
  const longLineRePostfixStrict = "}.+$";
3921
- const linkOrImageOnlyLineRe = /^[es]*(?:lT?L|I)[ES]*$/;
3922
3903
  const sternModeRe = /^(?:[#>\s]*\s)?\S*$/;
3923
- const tokenTypeMap = {
3924
- "em_open": "e",
3925
- "em_close": "E",
3926
- "image": "I",
3927
- "link_open": "l",
3928
- "link_close": "L",
3929
- "strong_open": "s",
3930
- "strong_close": "S",
3931
- "text": "T"
3932
- };
3933
3904
 
3934
3905
  // eslint-disable-next-line jsdoc/valid-types
3935
3906
  /** @type import("./markdownlint").Rule */
@@ -3937,7 +3908,7 @@ module.exports = {
3937
3908
  "names": [ "MD013", "line-length" ],
3938
3909
  "description": "Line length",
3939
3910
  "tags": [ "line_length" ],
3940
- "parser": "markdownit",
3911
+ "parser": "micromark",
3941
3912
  "function": function MD013(params, onError) {
3942
3913
  const lineLength = Number(params.config.line_length || 80);
3943
3914
  const headingLineLength =
@@ -3960,26 +3931,41 @@ module.exports = {
3960
3931
  const includeTables = (tables === undefined) ? true : !!tables;
3961
3932
  const headings = params.config.headings;
3962
3933
  const includeHeadings = (headings === undefined) ? true : !!headings;
3963
- const headingLineNumbers = [];
3964
- forEachHeading(params, (heading) => {
3965
- headingLineNumbers.push(heading.lineNumber);
3966
- });
3967
- const linkOnlyLineNumbers = [];
3968
- filterTokens(params, "inline", (token) => {
3969
- let childTokenTypes = "";
3970
- for (const child of token.children) {
3971
- if (child.type !== "text" || child.content !== "") {
3972
- childTokenTypes += tokenTypeMap[child.type] || "x";
3973
- }
3934
+ const headingLineNumbers = new Set();
3935
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
3936
+ addRangeToSet(headingLineNumbers, heading.startLine, heading.endLine);
3937
+ }
3938
+ const codeBlockLineNumbers = new Set();
3939
+ for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
3940
+ addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine);
3941
+ }
3942
+ const tableLineNumbers = new Set();
3943
+ for (const table of filterByTypesCached([ "table" ])) {
3944
+ addRangeToSet(tableLineNumbers, table.startLine, table.endLine);
3945
+ }
3946
+ const linkLineNumbers = new Set();
3947
+ for (const link of filterByTypesCached([ "autolink", "image", "link", "literalAutolink" ])) {
3948
+ addRangeToSet(linkLineNumbers, link.startLine, link.endLine);
3949
+ }
3950
+ const paragraphDataLineNumbers = new Set();
3951
+ for (const paragraph of filterByTypesCached([ "paragraph" ])) {
3952
+ for (const data of getDescendantsByType(paragraph, [ "data" ])) {
3953
+ addRangeToSet(paragraphDataLineNumbers, data.startLine, data.endLine);
3974
3954
  }
3975
- if (linkOrImageOnlyLineRe.test(childTokenTypes)) {
3976
- linkOnlyLineNumbers.push(token.lineNumber);
3955
+ }
3956
+ const linkOnlyLineNumbers = new Set();
3957
+ for (const lineNumber of linkLineNumbers) {
3958
+ if (!paragraphDataLineNumbers.has(lineNumber)) {
3959
+ linkOnlyLineNumbers.add(lineNumber);
3977
3960
  }
3978
- });
3979
- const { definitionLineIndices } = referenceLinkImageData();
3980
- forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence, inTable) => {
3961
+ }
3962
+ const definitionLineIndices = new Set(getReferenceLinkImageData().definitionLineIndices);
3963
+ for (let lineIndex = 0; lineIndex < params.lines.length; lineIndex++) {
3964
+ const line = params.lines[lineIndex];
3981
3965
  const lineNumber = lineIndex + 1;
3982
- const isHeading = includesSorted(headingLineNumbers, lineNumber);
3966
+ const isHeading = headingLineNumbers.has(lineNumber);
3967
+ const inCode = codeBlockLineNumbers.has(lineNumber);
3968
+ const inTable = tableLineNumbers.has(lineNumber);
3983
3969
  const length = inCode ?
3984
3970
  codeLineLength :
3985
3971
  (isHeading ? headingLineLength : lineLength);
@@ -3989,21 +3975,22 @@ module.exports = {
3989
3975
  if ((includeCodeBlocks || !inCode) &&
3990
3976
  (includeTables || !inTable) &&
3991
3977
  (includeHeadings || !isHeading) &&
3992
- !includesSorted(definitionLineIndices, lineIndex) &&
3978
+ !definitionLineIndices.has(lineIndex) &&
3993
3979
  (strict ||
3994
3980
  (!(stern && sternModeRe.test(line)) &&
3995
- !includesSorted(linkOnlyLineNumbers, lineNumber))) &&
3981
+ !linkOnlyLineNumbers.has(lineNumber))) &&
3996
3982
  lengthRe.test(line)) {
3997
3983
  addErrorDetailIf(
3998
3984
  onError,
3999
3985
  lineNumber,
4000
3986
  length,
4001
3987
  line.length,
4002
- null,
4003
- null,
4004
- [ length + 1, line.length - length ]);
3988
+ undefined,
3989
+ undefined,
3990
+ [ length + 1, line.length - length ]
3991
+ );
4005
3992
  }
4006
- });
3993
+ }
4007
3994
  }
4008
3995
  };
4009
3996
 
@@ -4021,7 +4008,9 @@ module.exports = {
4021
4008
 
4022
4009
 
4023
4010
 
4024
- const { addErrorContext, filterTokens } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4011
+ const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4012
+ const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4013
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4025
4014
 
4026
4015
  const dollarCommandRe = /^(\s*)(\$\s+)/;
4027
4016
 
@@ -4031,46 +4020,41 @@ module.exports = {
4031
4020
  "names": [ "MD014", "commands-show-output" ],
4032
4021
  "description": "Dollar signs used before commands without showing output",
4033
4022
  "tags": [ "code" ],
4034
- "parser": "markdownit",
4023
+ "parser": "micromark",
4035
4024
  "function": function MD014(params, onError) {
4036
- for (const type of [ "code_block", "fence" ]) {
4037
- filterTokens(params, type, (token) => {
4038
- const margin = (token.type === "fence") ? 1 : 0;
4039
- const dollarInstances = [];
4040
- let allDollars = true;
4041
- for (let i = token.map[0] + margin; i < token.map[1] - margin; i++) {
4042
- const line = params.lines[i];
4043
- const lineTrim = line.trim();
4044
- if (lineTrim) {
4045
- const match = dollarCommandRe.exec(line);
4046
- if (match) {
4047
- const column = match[1].length + 1;
4048
- const length = match[2].length;
4049
- dollarInstances.push([ i, lineTrim, column, length ]);
4050
- } else {
4051
- allDollars = false;
4025
+ for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
4026
+ const codeFlowValues = filterByTypes(
4027
+ codeBlock.children,
4028
+ [ "codeFlowValue" ]
4029
+ );
4030
+ const dollarMatches = codeFlowValues.
4031
+ map((codeFlowValue) => ({
4032
+ "result": codeFlowValue.text.match(dollarCommandRe),
4033
+ "startColumn": codeFlowValue.startColumn,
4034
+ "startLine": codeFlowValue.startLine,
4035
+ "text": codeFlowValue.text
4036
+ })).
4037
+ filter((dollarMatch) => dollarMatch.result);
4038
+ if (dollarMatches.length === codeFlowValues.length) {
4039
+ for (const dollarMatch of dollarMatches) {
4040
+ // @ts-ignore
4041
+ const column = dollarMatch.startColumn + dollarMatch.result[1].length;
4042
+ // @ts-ignore
4043
+ const length = dollarMatch.result[2].length;
4044
+ addErrorContext(
4045
+ onError,
4046
+ dollarMatch.startLine,
4047
+ dollarMatch.text,
4048
+ undefined,
4049
+ undefined,
4050
+ [ column, length ],
4051
+ {
4052
+ "editColumn": column,
4053
+ "deleteCount": length
4052
4054
  }
4053
- }
4054
- }
4055
- if (allDollars) {
4056
- for (const instance of dollarInstances) {
4057
- const [ i, lineTrim, column, length ] = instance;
4058
- addErrorContext(
4059
- onError,
4060
- // @ts-ignore
4061
- i + 1,
4062
- lineTrim,
4063
- null,
4064
- null,
4065
- [ column, length ],
4066
- {
4067
- "editColumn": column,
4068
- "deleteCount": length
4069
- }
4070
- );
4071
- }
4055
+ );
4072
4056
  }
4073
- });
4057
+ }
4074
4058
  }
4075
4059
  }
4076
4060
  };
@@ -4089,8 +4073,9 @@ module.exports = {
4089
4073
 
4090
4074
 
4091
4075
 
4092
- const { addErrorContext, forEachLine } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4093
- const { lineMetadata } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4076
+ const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4077
+ const { addRangeToSet } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4078
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4094
4079
 
4095
4080
  // eslint-disable-next-line jsdoc/valid-types
4096
4081
  /** @type import("./markdownlint").Rule */
@@ -4098,21 +4083,28 @@ module.exports = {
4098
4083
  "names": [ "MD018", "no-missing-space-atx" ],
4099
4084
  "description": "No space after hash on atx style heading",
4100
4085
  "tags": [ "headings", "atx", "spaces" ],
4101
- "parser": "none",
4086
+ "parser": "micromark",
4102
4087
  "function": function MD018(params, onError) {
4103
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
4104
- if (!inCode &&
4088
+ const { lines } = params;
4089
+ const ignoreBlockLineNumbers = new Set();
4090
+ for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow" ])) {
4091
+ addRangeToSet(ignoreBlockLineNumbers, ignoreBlock.startLine, ignoreBlock.endLine);
4092
+ }
4093
+ for (const [ lineIndex, line ] of lines.entries()) {
4094
+ if (
4095
+ !ignoreBlockLineNumbers.has(lineIndex + 1) &&
4105
4096
  /^#+[^# \t]/.test(line) &&
4106
4097
  !/#\s*$/.test(line) &&
4107
- !line.startsWith("#️⃣")) {
4098
+ !line.startsWith("#️⃣")
4099
+ ) {
4108
4100
  // @ts-ignore
4109
4101
  const hashCount = /^#+/.exec(line)[0].length;
4110
4102
  addErrorContext(
4111
4103
  onError,
4112
4104
  lineIndex + 1,
4113
4105
  line.trim(),
4114
- null,
4115
- null,
4106
+ undefined,
4107
+ undefined,
4116
4108
  [ 1, hashCount + 1 ],
4117
4109
  {
4118
4110
  "editColumn": hashCount + 1,
@@ -4120,17 +4112,17 @@ module.exports = {
4120
4112
  }
4121
4113
  );
4122
4114
  }
4123
- });
4115
+ }
4124
4116
  }
4125
4117
  };
4126
4118
 
4127
4119
 
4128
4120
  /***/ }),
4129
4121
 
4130
- /***/ "../lib/md019.js":
4131
- /*!***********************!*\
4132
- !*** ../lib/md019.js ***!
4133
- \***********************/
4122
+ /***/ "../lib/md019-md021.js":
4123
+ /*!*****************************!*\
4124
+ !*** ../lib/md019-md021.js ***!
4125
+ \*****************************/
4134
4126
  /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
4135
4127
 
4136
4128
  "use strict";
@@ -4138,44 +4130,82 @@ module.exports = {
4138
4130
 
4139
4131
 
4140
4132
 
4141
- const { addErrorContext, filterTokens, headingStyleFor } =
4142
- __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4133
+ const { addErrorContext } = __webpack_require__(/*! ../helpers/helpers */ "../helpers/helpers.js");
4134
+ const { getHeadingStyle } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4135
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4136
+
4137
+ /**
4138
+ * Validate heading sequence and whitespace length at start or end.
4139
+ *
4140
+ * @param {import("./markdownlint").RuleOnError} onError Error-reporting callback.
4141
+ * @param {import("./markdownlint").MicromarkToken} heading ATX heading token.
4142
+ * @param {number} delta Direction to scan.
4143
+ * @returns {void}
4144
+ */
4145
+ function validateHeadingSpaces(onError, heading, delta) {
4146
+ const { children, startLine, text } = heading;
4147
+ let index = (delta > 0) ? 0 : (children.length - 1);
4148
+ while (
4149
+ children[index] &&
4150
+ (children[index].type !== "atxHeadingSequence")
4151
+ ) {
4152
+ index += delta;
4153
+ }
4154
+ const headingSequence = children[index];
4155
+ const whitespace = children[index + delta];
4156
+ if (
4157
+ (headingSequence?.type === "atxHeadingSequence") &&
4158
+ (whitespace?.type === "whitespace") &&
4159
+ (whitespace.text.length > 1)
4160
+ ) {
4161
+ const column = whitespace.startColumn + 1;
4162
+ const length = whitespace.endColumn - column;
4163
+ addErrorContext(
4164
+ onError,
4165
+ startLine,
4166
+ text.trim(),
4167
+ delta > 0,
4168
+ delta < 0,
4169
+ [ column, length ],
4170
+ {
4171
+ "editColumn": column,
4172
+ "deleteCount": length
4173
+ }
4174
+ );
4175
+ }
4176
+ }
4143
4177
 
4144
4178
  // eslint-disable-next-line jsdoc/valid-types
4145
- /** @type import("./markdownlint").Rule */
4146
- module.exports = {
4147
- "names": [ "MD019", "no-multiple-space-atx" ],
4148
- "description": "Multiple spaces after hash on atx style heading",
4149
- "tags": [ "headings", "atx", "spaces" ],
4150
- "parser": "markdownit",
4151
- "function": function MD019(params, onError) {
4152
- filterTokens(params, "heading_open", (token) => {
4153
- if (headingStyleFor(token) === "atx") {
4154
- const { line, lineNumber } = token;
4155
- const match = /^(#+)([ \t]{2,})\S/.exec(line);
4156
- if (match) {
4157
- const [
4158
- ,
4159
- { "length": hashLength },
4160
- { "length": spacesLength }
4161
- ] = match;
4162
- addErrorContext(
4163
- onError,
4164
- lineNumber,
4165
- line.trim(),
4166
- null,
4167
- null,
4168
- [ 1, hashLength + spacesLength + 1 ],
4169
- {
4170
- "editColumn": hashLength + 1,
4171
- "deleteCount": spacesLength - 1
4172
- }
4173
- );
4174
- }
4179
+ /** @type import("./markdownlint").Rule[] */
4180
+ module.exports = [
4181
+ {
4182
+ "names": [ "MD019", "no-multiple-space-atx" ],
4183
+ "description": "Multiple spaces after hash on atx style heading",
4184
+ "tags": [ "headings", "atx", "spaces" ],
4185
+ "parser": "micromark",
4186
+ "function": function MD019(params, onError) {
4187
+ const atxHeadings = filterByTypesCached([ "atxHeading" ])
4188
+ .filter((heading) => getHeadingStyle(heading) === "atx");
4189
+ for (const atxHeading of atxHeadings) {
4190
+ validateHeadingSpaces(onError, atxHeading, 1);
4175
4191
  }
4176
- });
4192
+ }
4193
+ },
4194
+ {
4195
+ "names": [ "MD021", "no-multiple-space-closed-atx" ],
4196
+ "description": "Multiple spaces inside hashes on closed atx style heading",
4197
+ "tags": [ "headings", "atx_closed", "spaces" ],
4198
+ "parser": "micromark",
4199
+ "function": function MD021(params, onError) {
4200
+ const atxClosedHeadings = filterByTypesCached([ "atxHeading" ])
4201
+ .filter((heading) => getHeadingStyle(heading) === "atx_closed");
4202
+ for (const atxClosedHeading of atxClosedHeadings) {
4203
+ validateHeadingSpaces(onError, atxClosedHeading, 1);
4204
+ validateHeadingSpaces(onError, atxClosedHeading, -1);
4205
+ }
4206
+ }
4177
4207
  }
4178
- };
4208
+ ];
4179
4209
 
4180
4210
 
4181
4211
  /***/ }),
@@ -4191,8 +4221,9 @@ module.exports = {
4191
4221
 
4192
4222
 
4193
4223
 
4194
- const { addErrorContext, forEachLine } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4195
- const { lineMetadata } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4224
+ const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4225
+ const { addRangeToSet } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4226
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4196
4227
 
4197
4228
  // eslint-disable-next-line jsdoc/valid-types
4198
4229
  /** @type import("./markdownlint").Rule */
@@ -4200,10 +4231,15 @@ module.exports = {
4200
4231
  "names": [ "MD020", "no-missing-space-closed-atx" ],
4201
4232
  "description": "No space inside hashes on closed atx style heading",
4202
4233
  "tags": [ "headings", "atx_closed", "spaces" ],
4203
- "parser": "none",
4234
+ "parser": "micromark",
4204
4235
  "function": function MD020(params, onError) {
4205
- forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
4206
- if (!inCode) {
4236
+ const { lines } = params;
4237
+ const ignoreBlockLineNumbers = new Set();
4238
+ for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow" ])) {
4239
+ addRangeToSet(ignoreBlockLineNumbers, ignoreBlock.startLine, ignoreBlock.endLine);
4240
+ }
4241
+ for (const [ lineIndex, line ] of lines.entries()) {
4242
+ if (!ignoreBlockLineNumbers.has(lineIndex + 1)) {
4207
4243
  const match =
4208
4244
  /^(#+)([ \t]*)([^#]*?[^#\\])([ \t]*)((?:\\#)?)(#+)(\s*)$/.exec(line);
4209
4245
  if (match) {
@@ -4220,7 +4256,7 @@ module.exports = {
4220
4256
  const leftHashLength = leftHash.length;
4221
4257
  const rightHashLength = rightHash.length;
4222
4258
  const left = !leftSpaceLength;
4223
- const right = !rightSpaceLength || rightEscape;
4259
+ const right = !rightSpaceLength || !!rightEscape;
4224
4260
  const rightEscapeReplacement = rightEscape ? `${rightEscape} ` : "";
4225
4261
  if (left || right) {
4226
4262
  const range = left ?
@@ -4249,83 +4285,7 @@ module.exports = {
4249
4285
  }
4250
4286
  }
4251
4287
  }
4252
- });
4253
- }
4254
- };
4255
-
4256
-
4257
- /***/ }),
4258
-
4259
- /***/ "../lib/md021.js":
4260
- /*!***********************!*\
4261
- !*** ../lib/md021.js ***!
4262
- \***********************/
4263
- /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
4264
-
4265
- "use strict";
4266
- // @ts-check
4267
-
4268
-
4269
-
4270
- const { addErrorContext, filterTokens, headingStyleFor } =
4271
- __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4272
-
4273
- const closedAtxRe = /^(#+)([ \t]+)([^ \t]|[^ \t].*[^ \t])([ \t]+)(#+)(\s*)$/;
4274
-
4275
- // eslint-disable-next-line jsdoc/valid-types
4276
- /** @type import("./markdownlint").Rule */
4277
- module.exports = {
4278
- "names": [ "MD021", "no-multiple-space-closed-atx" ],
4279
- "description": "Multiple spaces inside hashes on closed atx style heading",
4280
- "tags": [ "headings", "atx_closed", "spaces" ],
4281
- "parser": "markdownit",
4282
- "function": function MD021(params, onError) {
4283
- filterTokens(params, "heading_open", (token) => {
4284
- if (headingStyleFor(token) === "atx_closed") {
4285
- const { line, lineNumber } = token;
4286
- const match = closedAtxRe.exec(line);
4287
- if (match) {
4288
- const [
4289
- ,
4290
- leftHash,
4291
- { "length": leftSpaceLength },
4292
- content,
4293
- { "length": rightSpaceLength },
4294
- rightHash,
4295
- { "length": trailSpaceLength }
4296
- ] = match;
4297
- const left = leftSpaceLength > 1;
4298
- const right = rightSpaceLength > 1;
4299
- if (left || right) {
4300
- const length = line.length;
4301
- const leftHashLength = leftHash.length;
4302
- const rightHashLength = rightHash.length;
4303
- const range = left ?
4304
- [
4305
- 1,
4306
- leftHashLength + leftSpaceLength + 1
4307
- ] :
4308
- [
4309
- length - trailSpaceLength - rightHashLength - rightSpaceLength,
4310
- rightSpaceLength + rightHashLength + 1
4311
- ];
4312
- addErrorContext(
4313
- onError,
4314
- lineNumber,
4315
- line.trim(),
4316
- left,
4317
- right,
4318
- range,
4319
- {
4320
- "editColumn": 1,
4321
- "deleteCount": length,
4322
- "insertText": `${leftHash} ${content} ${rightHash}`
4323
- }
4324
- );
4325
- }
4326
- }
4327
- }
4328
- });
4288
+ }
4329
4289
  }
4330
4290
  };
4331
4291
 
@@ -4343,10 +4303,9 @@ module.exports = {
4343
4303
 
4344
4304
 
4345
4305
 
4346
- const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } =
4347
- __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4348
- const { filterByTypes, getHeadingLevel, inHtmlFlow } =
4349
- __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4306
+ const { addErrorDetailIf, blockquotePrefixRe, isBlankLine } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4307
+ const { getHeadingLevel } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4308
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4350
4309
 
4351
4310
  const defaultLines = 1;
4352
4311
 
@@ -4382,17 +4341,8 @@ module.exports = {
4382
4341
  "function": function MD022(params, onError) {
4383
4342
  const getLinesAbove = getLinesFunction(params.config.lines_above);
4384
4343
  const getLinesBelow = getLinesFunction(params.config.lines_below);
4385
- // eslint-disable-next-line jsdoc/valid-types
4386
- /** @type import("../helpers/micromark.cjs").Token[] */
4387
- const micromarkTokens =
4388
- // @ts-ignore
4389
- params.parsers.micromark.tokens;
4390
4344
  const { lines } = params;
4391
- const headings = filterByTypes(
4392
- micromarkTokens,
4393
- [ "atxHeading", "setextHeading" ]
4394
- ).filter((heading) => !inHtmlFlow(heading));
4395
- for (const heading of headings) {
4345
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
4396
4346
  const { startLine, endLine } = heading;
4397
4347
  const line = lines[startLine - 1].trim();
4398
4348
 
@@ -4414,7 +4364,7 @@ module.exports = {
4414
4364
  actualAbove,
4415
4365
  "Above",
4416
4366
  line,
4417
- null,
4367
+ undefined,
4418
4368
  {
4419
4369
  "insertText": getBlockQuote(
4420
4370
  lines[startLine - 2],
@@ -4442,7 +4392,7 @@ module.exports = {
4442
4392
  actualBelow,
4443
4393
  "Below",
4444
4394
  line,
4445
- null,
4395
+ undefined,
4446
4396
  {
4447
4397
  "lineNumber": endLine + 1,
4448
4398
  "insertText": getBlockQuote(
@@ -4470,9 +4420,8 @@ module.exports = {
4470
4420
 
4471
4421
 
4472
4422
 
4473
- const { addErrorContext, filterTokens } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4474
-
4475
- const spaceBeforeHeadingRe = /^(\s+|[>\s]+\s\s)[^>\s]/;
4423
+ const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4424
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4476
4425
 
4477
4426
  // eslint-disable-next-line jsdoc/valid-types
4478
4427
  /** @type import("./markdownlint").Rule */
@@ -4480,31 +4429,31 @@ module.exports = {
4480
4429
  "names": [ "MD023", "heading-start-left" ],
4481
4430
  "description": "Headings must start at the beginning of the line",
4482
4431
  "tags": [ "headings", "spaces" ],
4483
- "parser": "markdownit",
4432
+ "parser": "micromark",
4484
4433
  "function": function MD023(params, onError) {
4485
- filterTokens(params, "heading_open", function forToken(token) {
4486
- const { lineNumber, line } = token;
4487
- const match = line.match(spaceBeforeHeadingRe);
4488
- if (match) {
4489
- const [ prefixAndFirstChar, prefix ] = match;
4490
- let deleteCount = prefix.length;
4491
- const prefixLengthNoSpace = prefix.trimEnd().length;
4492
- if (prefixLengthNoSpace) {
4493
- deleteCount -= prefixLengthNoSpace - 1;
4494
- }
4434
+ const headings = filterByTypesCached([ "atxHeading", "linePrefix", "setextHeading" ]);
4435
+ for (let i = 0; i < headings.length - 1; i++) {
4436
+ if (
4437
+ (headings[i].type === "linePrefix") &&
4438
+ (headings[i + 1].type !== "linePrefix") &&
4439
+ (headings[i].startLine === headings[i + 1].startLine)
4440
+ ) {
4441
+ const { endColumn, startColumn, startLine } = headings[i];
4442
+ const length = endColumn - startColumn;
4495
4443
  addErrorContext(
4496
4444
  onError,
4497
- lineNumber,
4498
- line,
4499
- null,
4500
- null,
4501
- [ 1, prefixAndFirstChar.length ],
4445
+ startLine,
4446
+ params.lines[startLine - 1],
4447
+ true,
4448
+ false,
4449
+ [ startColumn, length ],
4502
4450
  {
4503
- "editColumn": prefixLengthNoSpace + 1,
4504
- "deleteCount": deleteCount
4505
- });
4451
+ "editColumn": startColumn,
4452
+ "deleteCount": length
4453
+ }
4454
+ );
4506
4455
  }
4507
- });
4456
+ }
4508
4457
  }
4509
4458
  };
4510
4459
 
@@ -4522,7 +4471,9 @@ module.exports = {
4522
4471
 
4523
4472
 
4524
4473
 
4525
- const { addErrorContext, forEachHeading } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4474
+ const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4475
+ const { getHeadingLevel, getHeadingText } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4476
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4526
4477
 
4527
4478
  // eslint-disable-next-line jsdoc/valid-types
4528
4479
  /** @type import("./markdownlint").Rule */
@@ -4530,15 +4481,16 @@ module.exports = {
4530
4481
  "names": [ "MD024", "no-duplicate-heading" ],
4531
4482
  "description": "Multiple headings with the same content",
4532
4483
  "tags": [ "headings" ],
4533
- "parser": "markdownit",
4484
+ "parser": "micromark",
4534
4485
  "function": function MD024(params, onError) {
4535
4486
  const siblingsOnly = !!params.config.siblings_only || false;
4536
4487
  const knownContents = [ null, [] ];
4537
4488
  let lastLevel = 1;
4538
4489
  let knownContent = knownContents[lastLevel];
4539
- forEachHeading(params, (heading, content) => {
4490
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
4491
+ const headingText = getHeadingText(heading);
4540
4492
  if (siblingsOnly) {
4541
- const newLevel = heading.tag.slice(1);
4493
+ const newLevel = getHeadingLevel(heading);
4542
4494
  while (lastLevel < newLevel) {
4543
4495
  lastLevel++;
4544
4496
  knownContents[lastLevel] = [];
@@ -4550,17 +4502,17 @@ module.exports = {
4550
4502
  knownContent = knownContents[newLevel];
4551
4503
  }
4552
4504
  // @ts-ignore
4553
- if (knownContent.includes(content)) {
4505
+ if (knownContent.includes(headingText)) {
4554
4506
  addErrorContext(
4555
4507
  onError,
4556
- heading.lineNumber,
4557
- heading.line.trim()
4508
+ heading.startLine,
4509
+ headingText.trim()
4558
4510
  );
4559
4511
  } else {
4560
4512
  // @ts-ignore
4561
- knownContent.push(content);
4513
+ knownContent.push(headingText);
4562
4514
  }
4563
- });
4515
+ }
4564
4516
  }
4565
4517
  };
4566
4518
 
@@ -4578,8 +4530,9 @@ module.exports = {
4578
4530
 
4579
4531
 
4580
4532
 
4581
- const { addErrorContext, filterTokens, frontMatterHasTitle } =
4582
- __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4533
+ const { addErrorContext, frontMatterHasTitle } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4534
+ const { getHeadingLevel, getHeadingText } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4535
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4583
4536
 
4584
4537
  // eslint-disable-next-line jsdoc/valid-types
4585
4538
  /** @type import("./markdownlint").Rule */
@@ -4587,26 +4540,30 @@ module.exports = {
4587
4540
  "names": [ "MD025", "single-title", "single-h1" ],
4588
4541
  "description": "Multiple top-level headings in the same document",
4589
4542
  "tags": [ "headings" ],
4590
- "parser": "markdownit",
4543
+ "parser": "micromark",
4591
4544
  "function": function MD025(params, onError) {
4592
4545
  const level = Number(params.config.level || 1);
4593
- const tag = "h" + level;
4594
4546
  const foundFrontMatterTitle =
4595
4547
  frontMatterHasTitle(
4596
4548
  params.frontMatterLines,
4597
4549
  params.config.front_matter_title
4598
4550
  );
4599
4551
  let hasTopLevelHeading = false;
4600
- filterTokens(params, "heading_open", function forToken(token) {
4601
- if (token.tag === tag) {
4552
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
4553
+ const headingLevel = getHeadingLevel(heading);
4554
+ if (headingLevel === level) {
4602
4555
  if (hasTopLevelHeading || foundFrontMatterTitle) {
4603
- addErrorContext(onError, token.lineNumber,
4604
- token.line.trim());
4605
- } else if (token.lineNumber === 1) {
4556
+ const headingText = getHeadingText(heading);
4557
+ addErrorContext(
4558
+ onError,
4559
+ heading.startLine,
4560
+ headingText
4561
+ );
4562
+ } else if (heading.startLine === 1) {
4606
4563
  hasTopLevelHeading = true;
4607
4564
  }
4608
4565
  }
4609
- });
4566
+ }
4610
4567
  }
4611
4568
  };
4612
4569
 
@@ -4626,7 +4583,7 @@ module.exports = {
4626
4583
 
4627
4584
  const { addError, allPunctuationNoQuestion, endOfLineGemojiCodeRe,
4628
4585
  endOfLineHtmlEntityRe, escapeForRegExp } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4629
- const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4586
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4630
4587
 
4631
4588
  // eslint-disable-next-line jsdoc/valid-types
4632
4589
  /** @type import("./markdownlint").Rule */
@@ -4642,12 +4599,7 @@ module.exports = {
4642
4599
  );
4643
4600
  const trailingPunctuationRe =
4644
4601
  new RegExp("\\s*[" + escapeForRegExp(punctuation) + "]+$");
4645
- // eslint-disable-next-line jsdoc/valid-types
4646
- /** @type import("../helpers/micromark.cjs").Token[] */
4647
- const micromarkTokens =
4648
- // @ts-ignore
4649
- params.parsers.micromark.tokens;
4650
- const headings = filterByTypes(micromarkTokens, [ "atxHeadingText", "setextHeadingText" ]);
4602
+ const headings = filterByTypesCached([ "atxHeadingText", "setextHeadingText" ]);
4651
4603
  for (const heading of headings) {
4652
4604
  const { endColumn, endLine, text } = heading;
4653
4605
  const match = trailingPunctuationRe.exec(text);
@@ -4690,7 +4642,7 @@ module.exports = {
4690
4642
 
4691
4643
 
4692
4644
  const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4693
- const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4645
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4694
4646
 
4695
4647
  // eslint-disable-next-line jsdoc/valid-types
4696
4648
  /** @type import("./markdownlint").Rule */
@@ -4700,13 +4652,8 @@ module.exports = {
4700
4652
  "tags": ["blockquote", "whitespace", "indentation"],
4701
4653
  "parser": "micromark",
4702
4654
  "function": function MD027(params, onError) {
4703
- // eslint-disable-next-line jsdoc/valid-types
4704
- /** @type import("../helpers/micromark.cjs").Token[] */
4705
- const micromarkTokens =
4706
- // @ts-ignore
4707
- params.parsers.micromark.tokens;
4708
- for (const token of filterByTypes(micromarkTokens, [ "linePrefix" ])) {
4709
- const siblings = token.parent?.children || micromarkTokens;
4655
+ for (const token of filterByTypesCached([ "linePrefix" ])) {
4656
+ const siblings = token.parent?.children || params.parsers.micromark.tokens;
4710
4657
  if (siblings[siblings.indexOf(token) - 1]?.type === "blockQuotePrefix") {
4711
4658
  const { startColumn, startLine, text } = token;
4712
4659
  const { length } = text;
@@ -4715,8 +4662,8 @@ module.exports = {
4715
4662
  onError,
4716
4663
  startLine,
4717
4664
  line,
4718
- null,
4719
- null,
4665
+ undefined,
4666
+ undefined,
4720
4667
  [ startColumn, length ],
4721
4668
  {
4722
4669
  "editColumn": startColumn,
@@ -4743,7 +4690,7 @@ module.exports = {
4743
4690
 
4744
4691
 
4745
4692
  const { addError } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4746
- const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4693
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4747
4694
 
4748
4695
  const ignoreTypes = new Set([ "lineEnding", "listItemIndent", "linePrefix" ]);
4749
4696
 
@@ -4755,14 +4702,9 @@ module.exports = {
4755
4702
  "tags": [ "blockquote", "whitespace" ],
4756
4703
  "parser": "micromark",
4757
4704
  "function": function MD028(params, onError) {
4758
- // eslint-disable-next-line jsdoc/valid-types
4759
- /** @type import("../helpers/micromark.cjs").Token[] */
4760
- const micromarkTokens =
4761
- // @ts-ignore
4762
- params.parsers.micromark.tokens;
4763
- for (const token of filterByTypes(micromarkTokens, [ "blockQuote" ])) {
4705
+ for (const token of filterByTypesCached([ "blockQuote" ])) {
4764
4706
  const errorLineNumbers = [];
4765
- const siblings = token.parent?.children || micromarkTokens;
4707
+ const siblings = token.parent?.children || params.parsers.micromark.tokens;
4766
4708
  for (let i = siblings.indexOf(token) + 1; i < siblings.length; i++) {
4767
4709
  const sibling = siblings[i];
4768
4710
  const { startLine, type } = sibling;
@@ -4800,9 +4742,9 @@ module.exports = {
4800
4742
 
4801
4743
 
4802
4744
 
4803
- const { addErrorDetailIf, listItemMarkerRe, orderedListItemMarkerRe,
4804
- rangeFromRegExp } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4805
- const { flattenedLists } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4745
+ const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4746
+ const { getDescendantsByType, getTokenTextByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4747
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4806
4748
 
4807
4749
  const listStyleExamples = {
4808
4750
  "one": "1/1/1",
@@ -4810,32 +4752,37 @@ const listStyleExamples = {
4810
4752
  "zero": "0/0/0"
4811
4753
  };
4812
4754
 
4755
+ /**
4756
+ * Gets the value of an ordered list item prefix token.
4757
+ *
4758
+ * @param {import("../helpers/micromark.cjs").Token} listItemPrefix List item prefix token.
4759
+ * @returns {number} List item value.
4760
+ */
4761
+ function getOrderedListItemValue(listItemPrefix) {
4762
+ return Number(getTokenTextByType(listItemPrefix.children, "listItemValue"));
4763
+ }
4764
+
4813
4765
  // eslint-disable-next-line jsdoc/valid-types
4814
4766
  /** @type import("./markdownlint").Rule */
4815
4767
  module.exports = {
4816
4768
  "names": [ "MD029", "ol-prefix" ],
4817
4769
  "description": "Ordered list item prefix",
4818
4770
  "tags": [ "ol" ],
4819
- "parser": "none",
4771
+ "parser": "micromark",
4820
4772
  "function": function MD029(params, onError) {
4821
4773
  const style = String(params.config.style || "one_or_ordered");
4822
- const filteredLists = flattenedLists().filter((list) => !list.unordered);
4823
- for (const list of filteredLists) {
4824
- const { items } = list;
4825
- let current = 1;
4774
+ for (const listOrdered of filterByTypesCached([ "listOrdered" ])) {
4775
+ const listItemPrefixes = getDescendantsByType(listOrdered, [ "listItemPrefix" ]);
4776
+ let expected = 1;
4826
4777
  let incrementing = false;
4827
4778
  // Check for incrementing number pattern 1/2/3 or 0/1/2
4828
- if (items.length >= 2) {
4829
- const first = orderedListItemMarkerRe.exec(items[0].line);
4830
- const second = orderedListItemMarkerRe.exec(items[1].line);
4831
- if (first && second) {
4832
- const [ , firstNumber ] = first;
4833
- const [ , secondNumber ] = second;
4834
- if ((secondNumber !== "1") || (firstNumber === "0")) {
4835
- incrementing = true;
4836
- if (firstNumber === "0") {
4837
- current = 0;
4838
- }
4779
+ if (listItemPrefixes.length >= 2) {
4780
+ const firstValue = getOrderedListItemValue(listItemPrefixes[0]);
4781
+ const secondValue = getOrderedListItemValue(listItemPrefixes[1]);
4782
+ if ((secondValue !== 1) || (firstValue === 0)) {
4783
+ incrementing = true;
4784
+ if (firstValue === 0) {
4785
+ expected = 0;
4839
4786
  }
4840
4787
  }
4841
4788
  }
@@ -4843,24 +4790,25 @@ module.exports = {
4843
4790
  let listStyle = style;
4844
4791
  if (listStyle === "one_or_ordered") {
4845
4792
  listStyle = incrementing ? "ordered" : "one";
4846
- }
4847
- // Force expected value for 0/0/0 and 1/1/1 patterns
4848
- if (listStyle === "zero") {
4849
- current = 0;
4793
+ } else if (listStyle === "zero") {
4794
+ expected = 0;
4850
4795
  } else if (listStyle === "one") {
4851
- current = 1;
4796
+ expected = 1;
4852
4797
  }
4853
4798
  // Validate each list item marker
4854
- for (const item of items) {
4855
- const match = orderedListItemMarkerRe.exec(item.line);
4856
- if (match) {
4857
- addErrorDetailIf(onError, item.lineNumber,
4858
- String(current), match[1],
4859
- "Style: " + listStyleExamples[listStyle], null,
4860
- rangeFromRegExp(item.line, listItemMarkerRe));
4861
- if (listStyle === "ordered") {
4862
- current++;
4863
- }
4799
+ for (const listItemPrefix of listItemPrefixes) {
4800
+ const actual = getOrderedListItemValue(listItemPrefix);
4801
+ addErrorDetailIf(
4802
+ onError,
4803
+ listItemPrefix.startLine,
4804
+ expected,
4805
+ actual,
4806
+ "Style: " + listStyleExamples[listStyle],
4807
+ undefined,
4808
+ [ listItemPrefix.startColumn, listItemPrefix.endColumn - listItemPrefix.startColumn ]
4809
+ );
4810
+ if (listStyle === "ordered") {
4811
+ expected++;
4864
4812
  }
4865
4813
  }
4866
4814
  }
@@ -4882,7 +4830,7 @@ module.exports = {
4882
4830
 
4883
4831
 
4884
4832
  const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4885
- const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4833
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4886
4834
 
4887
4835
  // eslint-disable-next-line jsdoc/valid-types
4888
4836
  /** @type import("./markdownlint").Rule */
@@ -4896,13 +4844,7 @@ module.exports = {
4896
4844
  const olSingle = Number(params.config.ol_single || 1);
4897
4845
  const ulMulti = Number(params.config.ul_multi || 1);
4898
4846
  const olMulti = Number(params.config.ol_multi || 1);
4899
- // eslint-disable-next-line jsdoc/valid-types
4900
- /** @type import("../helpers/micromark.cjs").Token[] */
4901
- const micromarkTokens =
4902
- // @ts-ignore
4903
- params.parsers.micromark.tokens;
4904
- const lists = filterByTypes(micromarkTokens, [ "listOrdered", "listUnordered" ]);
4905
- for (const list of lists) {
4847
+ for (const list of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
4906
4848
  const ordered = (list.type === "listOrdered");
4907
4849
  const listItemPrefixes =
4908
4850
  list.children.filter((token) => (token.type === "listItemPrefix"));
@@ -4933,8 +4875,8 @@ module.exports = {
4933
4875
  startLine,
4934
4876
  expectedSpaces,
4935
4877
  actualSpaces,
4936
- null,
4937
- null,
4878
+ undefined,
4879
+ undefined,
4938
4880
  range,
4939
4881
  fixInfo
4940
4882
  );
@@ -4958,43 +4900,63 @@ module.exports = {
4958
4900
 
4959
4901
 
4960
4902
 
4961
- const { addErrorContext, forEachLine, isBlankLine } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4962
- const { lineMetadata } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4903
+ const { addErrorContext, isBlankLine } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4904
+ const { getTokenParentOfType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
4905
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
4963
4906
 
4964
4907
  const codeFencePrefixRe = /^(.*?)[`~]/;
4965
4908
 
4909
+ // eslint-disable-next-line jsdoc/valid-types
4910
+ /** @typedef {readonly string[]} ReadonlyStringArray */
4911
+
4912
+ /**
4913
+ * Adds an error for the top or bottom of a code fence.
4914
+ *
4915
+ * @param {import("./markdownlint").RuleOnError} onError Error-reporting callback.
4916
+ * @param {ReadonlyStringArray} lines Lines of Markdown content.
4917
+ * @param {number} lineNumber Line number.
4918
+ * @param {boolean} top True iff top fence.
4919
+ * @returns {void}
4920
+ */
4921
+ function addError(onError, lines, lineNumber, top) {
4922
+ const line = lines[lineNumber - 1];
4923
+ const [ , prefix ] = line.match(codeFencePrefixRe) || [];
4924
+ const fixInfo = (prefix === undefined) ? null : {
4925
+ "lineNumber": lineNumber + (top ? 0 : 1),
4926
+ "insertText": `${prefix.replace(/[^>]/g, " ").trim()}\n`
4927
+ };
4928
+ addErrorContext(
4929
+ onError,
4930
+ lineNumber,
4931
+ line.trim(),
4932
+ undefined,
4933
+ undefined,
4934
+ undefined,
4935
+ fixInfo
4936
+ );
4937
+ }
4938
+
4966
4939
  // eslint-disable-next-line jsdoc/valid-types
4967
4940
  /** @type import("./markdownlint").Rule */
4968
4941
  module.exports = {
4969
4942
  "names": [ "MD031", "blanks-around-fences" ],
4970
4943
  "description": "Fenced code blocks should be surrounded by blank lines",
4971
4944
  "tags": [ "code", "blank_lines" ],
4972
- "parser": "none",
4945
+ "parser": "micromark",
4973
4946
  "function": function MD031(params, onError) {
4974
4947
  const listItems = params.config.list_items;
4975
4948
  const includeListItems = (listItems === undefined) ? true : !!listItems;
4976
4949
  const { lines } = params;
4977
- forEachLine(lineMetadata(), (line, i, inCode, onFence, inTable, inItem) => {
4978
- const onTopFence = (onFence > 0);
4979
- const onBottomFence = (onFence < 0);
4980
- if ((includeListItems || !inItem) &&
4981
- ((onTopFence && !isBlankLine(lines[i - 1])) ||
4982
- (onBottomFence && !isBlankLine(lines[i + 1])))) {
4983
- const [ , prefix ] = line.match(codeFencePrefixRe) || [];
4984
- const fixInfo = (prefix === undefined) ? null : {
4985
- "lineNumber": i + (onTopFence ? 1 : 2),
4986
- "insertText": `${prefix.replace(/[^>]/g, " ").trim()}\n`
4987
- };
4988
- addErrorContext(
4989
- onError,
4990
- i + 1,
4991
- lines[i].trim(),
4992
- null,
4993
- null,
4994
- null,
4995
- fixInfo);
4950
+ for (const codeBlock of filterByTypesCached([ "codeFenced" ])) {
4951
+ if (includeListItems || !(getTokenParentOfType(codeBlock, [ "listOrdered", "listUnordered" ]))) {
4952
+ if (!isBlankLine(lines[codeBlock.startLine - 2])) {
4953
+ addError(onError, lines, codeBlock.startLine, true);
4954
+ }
4955
+ if (!isBlankLine(lines[codeBlock.endLine]) && !isBlankLine(lines[codeBlock.endLine - 1])) {
4956
+ addError(onError, lines, codeBlock.endLine, false);
4957
+ }
4996
4958
  }
4997
- });
4959
+ }
4998
4960
  }
4999
4961
  };
5000
4962
 
@@ -5012,28 +4974,12 @@ module.exports = {
5012
4974
 
5013
4975
 
5014
4976
 
5015
- const { addErrorContext, blockquotePrefixRe, isBlankLine } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
4977
+ const { addErrorContextForLine, isBlankLine } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5016
4978
  const { filterByPredicate, nonContentTokens } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5017
4979
 
5018
4980
  const isList = (token) => (
5019
4981
  (token.type === "listOrdered") || (token.type === "listUnordered")
5020
4982
  );
5021
- const addBlankLineError = (onError, lines, lineIndex, lineNumber) => {
5022
- const line = lines[lineIndex];
5023
- const quotePrefix = line.match(blockquotePrefixRe)[0].trimEnd();
5024
- addErrorContext(
5025
- onError,
5026
- lineIndex + 1,
5027
- line.trim(),
5028
- null,
5029
- null,
5030
- null,
5031
- {
5032
- lineNumber,
5033
- "insertText": `${quotePrefix}\n`
5034
- }
5035
- );
5036
- };
5037
4983
 
5038
4984
  // eslint-disable-next-line jsdoc/valid-types
5039
4985
  /** @type import("./markdownlint").Rule */
@@ -5043,16 +4989,11 @@ module.exports = {
5043
4989
  "tags": [ "bullet", "ul", "ol", "blank_lines" ],
5044
4990
  "parser": "micromark",
5045
4991
  "function": function MD032(params, onError) {
5046
- // eslint-disable-next-line jsdoc/valid-types
5047
- /** @type import("../helpers/micromark.cjs").Token[] */
5048
- const micromarkTokens =
5049
- // @ts-ignore
5050
- params.parsers.micromark.tokens;
5051
- const { lines } = params;
4992
+ const { lines, parsers } = params;
5052
4993
 
5053
4994
  // For every top-level list...
5054
4995
  const topLevelLists = filterByPredicate(
5055
- micromarkTokens,
4996
+ parsers.micromark.tokens,
5056
4997
  isList,
5057
4998
  (token) => (
5058
4999
  (isList(token) || (token.type === "htmlFlow")) ? [] : token.children
@@ -5063,7 +5004,12 @@ module.exports = {
5063
5004
  // Look for a blank line above the list
5064
5005
  const firstIndex = list.startLine - 1;
5065
5006
  if (!isBlankLine(lines[firstIndex - 1])) {
5066
- addBlankLineError(onError, lines, firstIndex);
5007
+ addErrorContextForLine(
5008
+ onError,
5009
+ // @ts-ignore
5010
+ lines,
5011
+ firstIndex
5012
+ );
5067
5013
  }
5068
5014
 
5069
5015
  // Find the "visual" end of the list
@@ -5079,7 +5025,13 @@ module.exports = {
5079
5025
  // Look for a blank line below the list
5080
5026
  const lastIndex = endLine - 1;
5081
5027
  if (!isBlankLine(lines[lastIndex + 1])) {
5082
- addBlankLineError(onError, lines, lastIndex, lastIndex + 2);
5028
+ addErrorContextForLine(
5029
+ onError,
5030
+ // @ts-ignore
5031
+ lines,
5032
+ lastIndex,
5033
+ lastIndex + 2
5034
+ );
5083
5035
  }
5084
5036
  }
5085
5037
  }
@@ -5100,8 +5052,8 @@ module.exports = {
5100
5052
 
5101
5053
 
5102
5054
  const { addError, nextLinesRe } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5103
- const { filterByTypes, getHtmlTagInfo } =
5104
- __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5055
+ const { getHtmlTagInfo } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5056
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
5105
5057
 
5106
5058
  // eslint-disable-next-line jsdoc/valid-types
5107
5059
  /** @type import("./markdownlint").Rule */
@@ -5114,12 +5066,7 @@ module.exports = {
5114
5066
  let allowedElements = params.config.allowed_elements;
5115
5067
  allowedElements = Array.isArray(allowedElements) ? allowedElements : [];
5116
5068
  allowedElements = allowedElements.map((element) => element.toLowerCase());
5117
- // eslint-disable-next-line jsdoc/valid-types
5118
- /** @type import("../helpers/micromark.cjs").Token[] */
5119
- const micromarkTokens =
5120
- // @ts-ignore
5121
- params.parsers.micromark.tokens;
5122
- for (const token of filterByTypes(micromarkTokens, [ "htmlText" ])) {
5069
+ for (const token of filterByTypesCached([ "htmlText" ], true)) {
5123
5070
  const htmlTagInfo = getHtmlTagInfo(token);
5124
5071
  if (
5125
5072
  htmlTagInfo &&
@@ -5157,8 +5104,8 @@ module.exports = {
5157
5104
 
5158
5105
 
5159
5106
  const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5160
- const { filterByPredicate, filterByTypes, getHtmlTagInfo, inHtmlFlow, parse } =
5161
- __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5107
+ const { filterByPredicate, getHtmlTagInfo, inHtmlFlow, parse } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5108
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
5162
5109
 
5163
5110
  // eslint-disable-next-line jsdoc/valid-types
5164
5111
  /** @type import("./markdownlint").Rule */
@@ -5168,11 +5115,6 @@ module.exports = {
5168
5115
  "tags": [ "links", "url" ],
5169
5116
  "parser": "micromark",
5170
5117
  "function": function MD034(params, onError) {
5171
- // eslint-disable-next-line jsdoc/valid-types
5172
- /** @type import("../helpers/micromark.cjs").Token[] */
5173
- const micromarkTokens =
5174
- // @ts-ignore
5175
- params.parsers.micromark.tokens;
5176
5118
  const literalAutolinks = (tokens) => (
5177
5119
  filterByPredicate(
5178
5120
  tokens,
@@ -5229,7 +5171,7 @@ module.exports = {
5229
5171
  }
5230
5172
  )
5231
5173
  );
5232
- const autoLinks = filterByTypes(micromarkTokens, [ "literalAutolink" ]);
5174
+ const autoLinks = filterByTypesCached([ "literalAutolink" ]);
5233
5175
  if (autoLinks.length > 0) {
5234
5176
  // Re-parse with correct link/image reference definition handling
5235
5177
  const document = params.lines.join("\n");
@@ -5248,8 +5190,8 @@ module.exports = {
5248
5190
  onError,
5249
5191
  token.startLine,
5250
5192
  token.text,
5251
- null,
5252
- null,
5193
+ undefined,
5194
+ undefined,
5253
5195
  range,
5254
5196
  fixInfo
5255
5197
  );
@@ -5273,7 +5215,7 @@ module.exports = {
5273
5215
 
5274
5216
 
5275
5217
  const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5276
- const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5218
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
5277
5219
 
5278
5220
  // eslint-disable-next-line jsdoc/valid-types
5279
5221
  /** @type import("./markdownlint").Rule */
@@ -5284,12 +5226,7 @@ module.exports = {
5284
5226
  "parser": "micromark",
5285
5227
  "function": function MD035(params, onError) {
5286
5228
  let style = String(params.config.style || "consistent").trim();
5287
- // eslint-disable-next-line jsdoc/valid-types
5288
- /** @type import("../helpers/micromark.cjs").Token[] */
5289
- const micromarkTokens =
5290
- // @ts-ignore
5291
- params.parsers.micromark.tokens;
5292
- const thematicBreaks = filterByTypes(micromarkTokens, [ "thematicBreak" ]);
5229
+ const thematicBreaks = filterByTypesCached([ "thematicBreak" ]);
5293
5230
  for (const token of thematicBreaks) {
5294
5231
  const { startLine, text } = token;
5295
5232
  if (style === "consistent") {
@@ -5315,7 +5252,8 @@ module.exports = {
5315
5252
 
5316
5253
 
5317
5254
  const { addErrorContext, allPunctuation } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5318
- const { filterByTypes, matchAndGetTokensByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5255
+ const { matchAndGetTokensByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5256
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
5319
5257
 
5320
5258
  /** @typedef {import("../helpers/micromark.cjs").TokenType} TokenType */
5321
5259
  /** @type {Map<TokenType, TokenType[]>} */
@@ -5336,7 +5274,7 @@ module.exports = {
5336
5274
  punctuation = String((punctuation === undefined) ? allPunctuation : punctuation);
5337
5275
  const punctuationRe = new RegExp("[" + punctuation + "]$");
5338
5276
  const paragraphTokens =
5339
- filterByTypes(params.parsers.micromark.tokens, [ "paragraph" ]).
5277
+ filterByTypesCached([ "paragraph" ]).
5340
5278
  filter((token) =>
5341
5279
  (token.parent?.type === "content") && !token.parent?.parent && (token.children.length === 1)
5342
5280
  );
@@ -5388,18 +5326,13 @@ module.exports = {
5388
5326
  "function": function MD037(params, onError) {
5389
5327
 
5390
5328
  // Initialize variables
5391
- // eslint-disable-next-line jsdoc/valid-types
5392
- /** @type import("../helpers/micromark.cjs").Token[] */
5393
- const micromarkTokens =
5394
- // @ts-ignore
5395
- params.parsers.micromark.tokens;
5396
- const { lines } = params;
5329
+ const { lines, parsers } = params;
5397
5330
  const emphasisTokensByMarker = new Map();
5398
5331
  for (const marker of [ "_", "__", "___", "*", "**", "***" ]) {
5399
5332
  emphasisTokensByMarker.set(marker, []);
5400
5333
  }
5401
5334
  const tokens = filterByPredicate(
5402
- micromarkTokens,
5335
+ parsers.micromark.tokens,
5403
5336
  (token) => token.children.some((child) => child.type === "data")
5404
5337
  );
5405
5338
  for (const token of tokens) {
@@ -5486,8 +5419,8 @@ module.exports = {
5486
5419
 
5487
5420
 
5488
5421
  const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5489
- const { filterByTypes, inHtmlFlow, tokenIfType } =
5490
- __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5422
+ const { tokenIfType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5423
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
5491
5424
 
5492
5425
  const leftSpaceRe = /^\s(?:[^`]|$)/;
5493
5426
  const rightSpaceRe = /[^`]\s$/;
@@ -5510,13 +5443,7 @@ module.exports = {
5510
5443
  "tags": [ "whitespace", "code" ],
5511
5444
  "parser": "micromark",
5512
5445
  "function": function MD038(params, onError) {
5513
- // eslint-disable-next-line jsdoc/valid-types
5514
- /** @type import("../helpers/micromark.cjs").Token[] */
5515
- const micromarkTokens =
5516
- // @ts-ignore
5517
- params.parsers.micromark.tokens;
5518
- const codeTexts = filterByTypes(micromarkTokens, [ "codeText" ])
5519
- .filter((codeText) => !inHtmlFlow(codeText));
5446
+ const codeTexts = filterByTypesCached([ "codeText" ]);
5520
5447
  for (const codeText of codeTexts) {
5521
5448
  const { children } = codeText;
5522
5449
  const first = 0;
@@ -5602,10 +5529,51 @@ module.exports = {
5602
5529
 
5603
5530
 
5604
5531
 
5605
- const { addErrorContext, filterTokens } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5532
+ const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5533
+ const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5534
+ const { getReferenceLinkImageData, filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
5535
+
5536
+ /**
5537
+ * Adds an error for a label space issue.
5538
+ *
5539
+ * @param {import("./markdownlint").RuleOnError} onError Error-reporting callback.
5540
+ * @param {import("../helpers/micromark.cjs").Token} label Label token.
5541
+ * @param {import("../helpers/micromark.cjs").Token} labelText LabelText token.
5542
+ * @param {boolean} isStart True iff error is at the start of the link.
5543
+ */
5544
+ function addLabelSpaceError(onError, label, labelText, isStart) {
5545
+ const match = labelText.text.match(isStart ? /^[^\S\r\n]+/ : /[^\S\r\n]+$/);
5546
+ const range = match ?
5547
+ [
5548
+ (isStart ? (labelText.startColumn) : (labelText.endColumn - match[0].length)),
5549
+ match[0].length
5550
+ ] :
5551
+ undefined;
5552
+ addErrorContext(
5553
+ onError,
5554
+ isStart ? (labelText.startLine + (match ? 0 : 1)) : (labelText.endLine - (match ? 0 : 1)),
5555
+ label.text.replace(/\s+/g, " "),
5556
+ isStart,
5557
+ !isStart,
5558
+ range,
5559
+ range ? {
5560
+ "editColumn": range[0],
5561
+ "deleteCount": range[1]
5562
+ } : undefined
5563
+ );
5564
+ }
5606
5565
 
5607
- const spaceInLinkRe =
5608
- /\[(?:\s[^\]]*|[^\]]*?\s)\](?=(\([^)]*\)|\[[^\]]*\]))/;
5566
+ /**
5567
+ * Determines if a link is a valid link (and not a fake shortcut link due to parser tricks).
5568
+ *
5569
+ * @param {import("../helpers/micromark.cjs").Token} label Label token.
5570
+ * @param {import("../helpers/micromark.cjs").Token} labelText LabelText token.
5571
+ * @param {Map<string, any>} definitions Map of link definitions.
5572
+ * @returns {boolean} True iff the link is valid.
5573
+ */
5574
+ function validLink(label, labelText, definitions) {
5575
+ return (label.parent?.children.length !== 1) || definitions.has(labelText.text.trim());
5576
+ }
5609
5577
 
5610
5578
  // eslint-disable-next-line jsdoc/valid-types
5611
5579
  /** @type import("./markdownlint").Rule */
@@ -5613,60 +5581,31 @@ module.exports = {
5613
5581
  "names": [ "MD039", "no-space-in-links" ],
5614
5582
  "description": "Spaces inside link text",
5615
5583
  "tags": [ "whitespace", "links" ],
5616
- "parser": "markdownit",
5584
+ "parser": "micromark",
5617
5585
  "function": function MD039(params, onError) {
5618
- filterTokens(params, "inline", (token) => {
5619
- const { children } = token;
5620
- let { lineNumber } = token;
5621
- let inLink = false;
5622
- let linkText = "";
5623
- let lineIndex = 0;
5624
- for (const child of children) {
5625
- const { content, markup, type } = child;
5626
- if (type === "link_open") {
5627
- inLink = true;
5628
- linkText = "";
5629
- } else if (type === "link_close") {
5630
- inLink = false;
5631
- const left = linkText.trimStart().length !== linkText.length;
5632
- const right = linkText.trimEnd().length !== linkText.length;
5633
- if (left || right) {
5634
- const line = params.lines[lineNumber - 1];
5635
- let range = null;
5636
- let fixInfo = null;
5637
- const match = line.slice(lineIndex).match(spaceInLinkRe);
5638
- if (match) {
5639
- // @ts-ignore
5640
- const column = match.index + lineIndex + 1;
5641
- const length = match[0].length;
5642
- range = [ column, length ];
5643
- fixInfo = {
5644
- "editColumn": column + 1,
5645
- "deleteCount": length - 2,
5646
- "insertText": linkText.trim()
5647
- };
5648
- lineIndex = column + length - 1;
5649
- }
5650
- addErrorContext(
5651
- onError,
5652
- lineNumber,
5653
- `[${linkText}]`,
5654
- left,
5655
- right,
5656
- range,
5657
- fixInfo
5658
- );
5659
- }
5660
- } else if ((type === "softbreak") || (type === "hardbreak")) {
5661
- lineNumber++;
5662
- lineIndex = 0;
5663
- } else if (inLink) {
5664
- linkText += type.endsWith("_inline") ?
5665
- `${markup}${content}${markup}` :
5666
- (content || markup);
5586
+ const { definitions } = getReferenceLinkImageData();
5587
+ const labels = filterByTypesCached([ "label" ]).
5588
+ filter((label) => label.parent?.type === "link");
5589
+ for (const label of labels) {
5590
+ const labelTexts = filterByTypes(
5591
+ label.children,
5592
+ [ "labelText" ]
5593
+ );
5594
+ for (const labelText of labelTexts) {
5595
+ if (
5596
+ (labelText.text.trimStart().length !== labelText.text.length) &&
5597
+ validLink(label, labelText, definitions)
5598
+ ) {
5599
+ addLabelSpaceError(onError, label, labelText, true);
5600
+ }
5601
+ if (
5602
+ (labelText.text.trimEnd().length !== labelText.text.length) &&
5603
+ validLink(label, labelText, definitions)
5604
+ ) {
5605
+ addLabelSpaceError(onError, label, labelText, false);
5667
5606
  }
5668
5607
  }
5669
- });
5608
+ }
5670
5609
  }
5671
5610
  };
5672
5611
 
@@ -5685,8 +5624,8 @@ module.exports = {
5685
5624
 
5686
5625
 
5687
5626
  const { addError, addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5688
- const { filterByTypes, getTokenTextByType, tokenIfType } =
5689
- __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5627
+ const { getTokenTextByType, tokenIfType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5628
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
5690
5629
 
5691
5630
  // eslint-disable-next-line jsdoc/valid-types
5692
5631
  /** @type import("./markdownlint").Rule */
@@ -5699,12 +5638,7 @@ module.exports = {
5699
5638
  let allowed = params.config.allowed_languages;
5700
5639
  allowed = Array.isArray(allowed) ? allowed : [];
5701
5640
  const languageOnly = !!params.config.language_only;
5702
- // eslint-disable-next-line jsdoc/valid-types
5703
- /** @type import("../helpers/micromark.cjs").Token[] */
5704
- const micromarkTokens =
5705
- // @ts-ignore
5706
- params.parsers.micromark.tokens;
5707
- const fencedCodes = filterByTypes(micromarkTokens, [ "codeFenced" ]);
5641
+ const fencedCodes = filterByTypesCached([ "codeFenced" ]);
5708
5642
  for (const fencedCode of fencedCodes) {
5709
5643
  const openingFence = tokenIfType(fencedCode.children[0], "codeFencedFence");
5710
5644
  if (openingFence) {
@@ -5758,7 +5692,7 @@ module.exports = {
5758
5692
  if ((token.type === "atxHeading") || (token.type === "setextHeading")) {
5759
5693
  isError = (getHeadingLevel(token) !== level);
5760
5694
  } else if (token.type === "htmlFlow") {
5761
- const htmlTexts = filterByTypes(token.children, [ "htmlText" ]);
5695
+ const htmlTexts = filterByTypes(token.children, [ "htmlText" ], true);
5762
5696
  const tagInfo = (htmlTexts.length > 0) && getHtmlTagInfo(htmlTexts[0]);
5763
5697
  isError = !tagInfo || (tagInfo.name.toLowerCase() !== `h${level}`);
5764
5698
  }
@@ -5785,8 +5719,9 @@ module.exports = {
5785
5719
 
5786
5720
 
5787
5721
 
5788
- const { addErrorContext, escapeForRegExp, filterTokens } =
5789
- __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5722
+ const { addErrorContext } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5723
+ const { getDescendantsByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5724
+ const { getReferenceLinkImageData, filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
5790
5725
 
5791
5726
  // eslint-disable-next-line jsdoc/valid-types
5792
5727
  /** @type import("./markdownlint").Rule */
@@ -5794,44 +5729,53 @@ module.exports = {
5794
5729
  "names": [ "MD042", "no-empty-links" ],
5795
5730
  "description": "No empty links",
5796
5731
  "tags": [ "links" ],
5797
- "parser": "markdownit",
5732
+ "parser": "micromark",
5798
5733
  "function": function MD042(params, onError) {
5799
- filterTokens(params, "inline", function forToken(token) {
5800
- let inLink = false;
5801
- let linkText = "";
5802
- let emptyLink = false;
5803
- for (const child of token.children) {
5804
- if (child.type === "link_open") {
5805
- inLink = true;
5806
- linkText = "";
5807
- for (const attr of child.attrs) {
5808
- if (attr[0] === "href" && (!attr[1] || (attr[1] === "#"))) {
5809
- emptyLink = true;
5810
- }
5811
- }
5812
- } else if (child.type === "link_close") {
5813
- inLink = false;
5814
- if (emptyLink) {
5815
- let context = `[${linkText}]`;
5816
- let range = null;
5817
- const match = child.line.match(
5818
- new RegExp(`${escapeForRegExp(context)}\\((?:|#|<>)\\)`)
5819
- );
5820
- if (match) {
5821
- context = match[0];
5822
- // @ts-ignore
5823
- range = [ match.index + 1, match[0].length ];
5824
- }
5825
- addErrorContext(
5826
- onError, child.lineNumber, context, null, null, range
5827
- );
5828
- emptyLink = false;
5829
- }
5830
- } else if (inLink) {
5831
- linkText += child.content;
5832
- }
5734
+ const { definitions } = getReferenceLinkImageData();
5735
+ const isReferenceDefinitionHash = (token) => {
5736
+ const definition = definitions.get(token.text.trim());
5737
+ return (definition && (definition[1] === "#"));
5738
+ };
5739
+ const links = filterByTypesCached([ "link" ]);
5740
+ for (const link of links) {
5741
+ const labelText = getDescendantsByType(link, [ "label", "labelText" ]);
5742
+ const reference = getDescendantsByType(link, [ "reference" ]);
5743
+ const resource = getDescendantsByType(link, [ "resource" ]);
5744
+ const referenceString = getDescendantsByType(reference, [ "referenceString" ]);
5745
+ const resourceDestination = getDescendantsByType(resource, [ "resourceDestination" ]);
5746
+ const resourceDestinationString = [
5747
+ ...getDescendantsByType(resourceDestination, [ "resourceDestinationRaw", "resourceDestinationString" ]),
5748
+ ...getDescendantsByType(resourceDestination, [ "resourceDestinationLiteral", "resourceDestinationString" ])
5749
+ ];
5750
+ const hasLabelText = labelText.length > 0;
5751
+ const hasReference = reference.length > 0;
5752
+ const hasResource = resource.length > 0;
5753
+ const hasReferenceString = referenceString.length > 0;
5754
+ const hasResourceDestinationString = resourceDestinationString.length > 0;
5755
+ let error = false;
5756
+ if (
5757
+ hasLabelText &&
5758
+ ((!hasReference && !hasResource) || (hasReference && !hasReferenceString))
5759
+ ) {
5760
+ error = isReferenceDefinitionHash(labelText[0]);
5761
+ } else if (hasReferenceString && !hasResourceDestinationString) {
5762
+ error = isReferenceDefinitionHash(referenceString[0]);
5763
+ } else if (!hasReferenceString && hasResourceDestinationString) {
5764
+ error = (resourceDestinationString[0].text.trim() === "#");
5765
+ } else if (!hasReferenceString && !hasResourceDestinationString) {
5766
+ error = true;
5833
5767
  }
5834
- });
5768
+ if (error) {
5769
+ addErrorContext(
5770
+ onError,
5771
+ link.startLine,
5772
+ link.text,
5773
+ undefined,
5774
+ undefined,
5775
+ [ link.startColumn, link.endColumn - link.startColumn ]
5776
+ );
5777
+ }
5778
+ }
5835
5779
  }
5836
5780
  };
5837
5781
 
@@ -5849,8 +5793,9 @@ module.exports = {
5849
5793
 
5850
5794
 
5851
5795
 
5852
- const { addErrorContext, addErrorDetailIf, forEachHeading } =
5853
- __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5796
+ const { addErrorContext, addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
5797
+ const { getHeadingLevel, getHeadingText } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
5798
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
5854
5799
 
5855
5800
  // eslint-disable-next-line jsdoc/valid-types
5856
5801
  /** @type import("./markdownlint").Rule */
@@ -5858,7 +5803,7 @@ module.exports = {
5858
5803
  "names": [ "MD043", "required-headings" ],
5859
5804
  "description": "Required heading structure",
5860
5805
  "tags": [ "headings" ],
5861
- "parser": "markdownit",
5806
+ "parser": "micromark",
5862
5807
  "function": function MD043(params, onError) {
5863
5808
  const requiredHeadings = params.config.headings;
5864
5809
  if (!Array.isArray(requiredHeadings)) {
@@ -5866,20 +5811,18 @@ module.exports = {
5866
5811
  return;
5867
5812
  }
5868
5813
  const matchCase = params.config.match_case || false;
5869
- const levels = {};
5870
- for (const level of [ 1, 2, 3, 4, 5, 6 ]) {
5871
- levels["h" + level] = "######".substr(-level);
5872
- }
5873
5814
  let i = 0;
5874
5815
  let matchAny = false;
5875
5816
  let hasError = false;
5876
5817
  let anyHeadings = false;
5877
5818
  const getExpected = () => requiredHeadings[i++] || "[None]";
5878
5819
  const handleCase = (str) => (matchCase ? str : str.toLowerCase());
5879
- forEachHeading(params, (heading, content) => {
5820
+ for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
5880
5821
  if (!hasError) {
5822
+ const headingText = getHeadingText(heading);
5823
+ const headingLevel = getHeadingLevel(heading);
5881
5824
  anyHeadings = true;
5882
- const actual = levels[heading.tag] + " " + content;
5825
+ const actual = `${"".padEnd(headingLevel, "#")} ${headingText}`;
5883
5826
  const expected = getExpected();
5884
5827
  if (expected === "*") {
5885
5828
  const nextExpected = getExpected();
@@ -5894,12 +5837,16 @@ module.exports = {
5894
5837
  } else if (matchAny) {
5895
5838
  i--;
5896
5839
  } else {
5897
- addErrorDetailIf(onError, heading.lineNumber,
5898
- expected, actual);
5840
+ addErrorDetailIf(
5841
+ onError,
5842
+ heading.startLine,
5843
+ expected,
5844
+ actual
5845
+ );
5899
5846
  hasError = true;
5900
5847
  }
5901
5848
  }
5902
- });
5849
+ }
5903
5850
  const extraHeadings = requiredHeadings.length - i;
5904
5851
  if (
5905
5852
  !hasError &&
@@ -5907,8 +5854,11 @@ module.exports = {
5907
5854
  ((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
5908
5855
  (anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
5909
5856
  ) {
5910
- addErrorContext(onError, params.lines.length,
5911
- requiredHeadings[i]);
5857
+ addErrorContext(
5858
+ onError,
5859
+ params.lines.length,
5860
+ requiredHeadings[i]
5861
+ );
5912
5862
  }
5913
5863
  }
5914
5864
  };
@@ -5957,11 +5907,6 @@ module.exports = {
5957
5907
  const htmlElements = params.config.html_elements;
5958
5908
  const includeHtmlElements =
5959
5909
  (htmlElements === undefined) ? true : !!htmlElements;
5960
- // eslint-disable-next-line jsdoc/valid-types
5961
- /** @type import("../helpers/micromark.cjs").Token[] */
5962
- const micromarkTokens =
5963
- // @ts-ignore
5964
- params.parsers.micromark.tokens;
5965
5910
  const scannedTypes = new Set([ "data" ]);
5966
5911
  if (includeCodeBlocks) {
5967
5912
  scannedTypes.add("codeFlowValue");
@@ -5973,7 +5918,7 @@ module.exports = {
5973
5918
  }
5974
5919
  const contentTokens =
5975
5920
  filterByPredicate(
5976
- micromarkTokens,
5921
+ params.parsers.micromark.tokens,
5977
5922
  (token) => scannedTypes.has(token.type),
5978
5923
  (token) => (
5979
5924
  token.children.filter((t) => !ignoredChildTypes.has(t.type))
@@ -6021,8 +5966,8 @@ module.exports = {
6021
5966
  token.startLine,
6022
5967
  name,
6023
5968
  nameMatch,
6024
- null,
6025
- null,
5969
+ undefined,
5970
+ undefined,
6026
5971
  [ column, length ],
6027
5972
  {
6028
5973
  "editColumn": column,
@@ -6055,6 +6000,7 @@ module.exports = {
6055
6000
 
6056
6001
  const { addError, getHtmlAttributeRe, nextLinesRe } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6057
6002
  const { filterByTypes, getHtmlTagInfo } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
6003
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6058
6004
 
6059
6005
  const altRe = getHtmlAttributeRe("alt");
6060
6006
 
@@ -6066,14 +6012,8 @@ module.exports = {
6066
6012
  "tags": [ "accessibility", "images" ],
6067
6013
  "parser": "micromark",
6068
6014
  "function": function MD045(params, onError) {
6069
- // eslint-disable-next-line jsdoc/valid-types
6070
- /** @type import("../helpers/micromark.cjs").Token[] */
6071
- const micromarkTokens =
6072
- // @ts-ignore
6073
- params.parsers.micromark.tokens;
6074
-
6075
6015
  // Process Markdown images
6076
- const images = filterByTypes(micromarkTokens, [ "image" ]);
6016
+ const images = filterByTypesCached([ "image" ]);
6077
6017
  for (const image of images) {
6078
6018
  const labelTexts = filterByTypes(image.children, [ "labelText" ]);
6079
6019
  if (labelTexts.some((labelText) => labelText.text.length === 0)) {
@@ -6091,7 +6031,7 @@ module.exports = {
6091
6031
  }
6092
6032
 
6093
6033
  // Process HTML images
6094
- const htmlTexts = filterByTypes(micromarkTokens, [ "htmlText" ]);
6034
+ const htmlTexts = filterByTypesCached([ "htmlText" ], true);
6095
6035
  for (const htmlText of htmlTexts) {
6096
6036
  const { startColumn, startLine, text } = htmlText;
6097
6037
  const htmlTagInfo = getHtmlTagInfo(htmlText);
@@ -6132,7 +6072,7 @@ module.exports = {
6132
6072
 
6133
6073
 
6134
6074
  const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6135
- const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
6075
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6136
6076
 
6137
6077
  const tokenTypeToStyle = {
6138
6078
  "codeFenced": "fenced",
@@ -6148,13 +6088,7 @@ module.exports = {
6148
6088
  "parser": "micromark",
6149
6089
  "function": function MD046(params, onError) {
6150
6090
  let expectedStyle = String(params.config.style || "consistent");
6151
- // eslint-disable-next-line jsdoc/valid-types
6152
- /** @type import("../helpers/micromark.cjs").Token[] */
6153
- const micromarkTokens =
6154
- // @ts-ignore
6155
- params.parsers.micromark.tokens;
6156
- const codeBlocksAndFences = filterByTypes(micromarkTokens, [ "codeFenced", "codeIndented" ]);
6157
- for (const token of codeBlocksAndFences) {
6091
+ for (const token of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
6158
6092
  const { startLine, type } = token;
6159
6093
  if (expectedStyle === "consistent") {
6160
6094
  expectedStyle = tokenTypeToStyle[type];
@@ -6225,7 +6159,8 @@ module.exports = {
6225
6159
 
6226
6160
 
6227
6161
  const { addErrorDetailIf, fencedCodeBlockStyleFor } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6228
- const { filterByTypes, tokenIfType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
6162
+ const { tokenIfType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
6163
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6229
6164
 
6230
6165
  // eslint-disable-next-line jsdoc/valid-types
6231
6166
  /** @type import("./markdownlint").Rule */
@@ -6236,13 +6171,8 @@ module.exports = {
6236
6171
  "parser": "micromark",
6237
6172
  "function": function MD048(params, onError) {
6238
6173
  const style = String(params.config.style || "consistent");
6239
- // eslint-disable-next-line jsdoc/valid-types
6240
- /** @type import("../helpers/micromark.cjs").Token[] */
6241
- const micromarkTokens =
6242
- // @ts-ignore
6243
- params.parsers.micromark.tokens;
6244
6174
  let expectedStyle = style;
6245
- const codeFenceds = filterByTypes(micromarkTokens, [ "codeFenced" ]);
6175
+ const codeFenceds = filterByTypesCached([ "codeFenced" ]);
6246
6176
  for (const codeFenced of codeFenceds) {
6247
6177
  const codeFencedFence = tokenIfType(codeFenced.children[0], "codeFencedFence");
6248
6178
  if (codeFencedFence) {
@@ -6294,14 +6224,9 @@ const intrawordRe = /^\w$/;
6294
6224
  */
6295
6225
  const impl =
6296
6226
  (params, onError, type, typeSequence, asterisk, underline, style = "consistent") => {
6297
- // eslint-disable-next-line jsdoc/valid-types
6298
- /** @type import("../helpers/micromark.cjs").Token[] */
6299
- const micromarkTokens =
6300
- // @ts-ignore
6301
- params.parsers.micromark.tokens;
6302
- const { lines } = params;
6227
+ const { lines, parsers } = params;
6303
6228
  const emphasisTokens = filterByPredicate(
6304
- micromarkTokens,
6229
+ parsers.micromark.tokens,
6305
6230
  (token) => token.type === type,
6306
6231
  (token) => ((token.type === "htmlFlow") ? [] : token.children)
6307
6232
  );
@@ -6397,10 +6322,9 @@ module.exports = [
6397
6322
 
6398
6323
 
6399
6324
 
6400
- const { addError, addErrorDetailIf, getHtmlAttributeRe } =
6401
- __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6402
- const { filterByPredicate, filterByTypes, getHtmlTagInfo } =
6403
- __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
6325
+ const { addError, addErrorDetailIf, getHtmlAttributeRe } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6326
+ const { filterByPredicate, filterByTypes, getHtmlTagInfo } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
6327
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6404
6328
 
6405
6329
  // Regular expression for identifying HTML anchor names
6406
6330
  const idRe = getHtmlAttributeRe("id");
@@ -6464,15 +6388,10 @@ module.exports = {
6464
6388
  "tags": [ "links" ],
6465
6389
  "parser": "micromark",
6466
6390
  "function": function MD051(params, onError) {
6467
- // eslint-disable-next-line jsdoc/valid-types
6468
- /** @type import("../helpers/micromark.cjs").Token[] */
6469
- const micromarkTokens =
6470
- // @ts-ignore
6471
- params.parsers.micromark.tokens;
6472
6391
  const fragments = new Map();
6473
6392
 
6474
6393
  // Process headings
6475
- const headingTexts = filterByTypes(micromarkTokens, [ "atxHeadingText", "setextHeadingText" ]);
6394
+ const headingTexts = filterByTypesCached([ "atxHeadingText", "setextHeadingText" ]);
6476
6395
  for (const headingText of headingTexts) {
6477
6396
  const fragment = convertHeadingToHTMLFragment(headingText);
6478
6397
  if (fragment !== "#") {
@@ -6492,7 +6411,7 @@ module.exports = {
6492
6411
  }
6493
6412
 
6494
6413
  // Process HTML anchors
6495
- for (const token of filterByTypes(micromarkTokens, [ "htmlText" ])) {
6414
+ for (const token of filterByTypesCached([ "htmlText" ], true)) {
6496
6415
  const htmlTagInfo = getHtmlTagInfo(token);
6497
6416
  if (htmlTagInfo && !htmlTagInfo.close) {
6498
6417
  const anchorMatch = idRe.exec(token.text) ||
@@ -6511,7 +6430,7 @@ module.exports = {
6511
6430
  [ "definition", "definitionDestinationString" ]
6512
6431
  ];
6513
6432
  for (const [ parentType, definitionType ] of parentChilds) {
6514
- const links = filterByTypes(micromarkTokens, [ parentType ]);
6433
+ const links = filterByTypesCached([ parentType ]);
6515
6434
  for (const link of links) {
6516
6435
  const definitions = filterByTypes(link.children, [ definitionType ]);
6517
6436
  for (const definition of definitions) {
@@ -6585,7 +6504,7 @@ module.exports = {
6585
6504
 
6586
6505
 
6587
6506
  const { addError } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6588
- const { referenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6507
+ const { getReferenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6589
6508
 
6590
6509
  // eslint-disable-next-line jsdoc/valid-types
6591
6510
  /** @type import("./markdownlint").Rule */
@@ -6598,7 +6517,7 @@ module.exports = {
6598
6517
  "function": function MD052(params, onError) {
6599
6518
  const { config, lines } = params;
6600
6519
  const shortcutSyntax = config.shortcut_syntax || false;
6601
- const { definitions, references, shortcuts } = referenceLinkImageData();
6520
+ const { definitions, references, shortcuts } = getReferenceLinkImageData();
6602
6521
  const entries = shortcutSyntax ?
6603
6522
  [ ...references.entries(), ...shortcuts.entries() ] :
6604
6523
  references.entries();
@@ -6639,7 +6558,7 @@ module.exports = {
6639
6558
 
6640
6559
  const { addError, ellipsify, linkReferenceDefinitionRe } =
6641
6560
  __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6642
- const { referenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6561
+ const { getReferenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6643
6562
 
6644
6563
  // eslint-disable-next-line jsdoc/valid-types
6645
6564
  /** @type import("./markdownlint").Rule */
@@ -6652,7 +6571,7 @@ module.exports = {
6652
6571
  const ignored = new Set(params.config.ignored_definitions || [ "//" ]);
6653
6572
  const lines = params.lines;
6654
6573
  const { references, shortcuts, definitions, duplicateDefinitions } =
6655
- referenceLinkImageData();
6574
+ getReferenceLinkImageData();
6656
6575
  const singleLineDefinition = (line) => (
6657
6576
  line.replace(linkReferenceDefinitionRe, "").trim().length > 0
6658
6577
  );
@@ -6711,9 +6630,8 @@ module.exports = {
6711
6630
 
6712
6631
 
6713
6632
  const { addErrorContext, nextLinesRe } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6714
- const { filterByTypes, filterByPredicate, getTokenTextByType } =
6715
- __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
6716
- const { referenceLinkImageData } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6633
+ const { filterByPredicate, getTokenTextByType } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
6634
+ const { getReferenceLinkImageData, filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6717
6635
 
6718
6636
  const backslashEscapeRe = /\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g;
6719
6637
  const removeBackslashEscapes = (text) => text.replace(backslashEscapeRe, "$1");
@@ -6748,13 +6666,8 @@ module.exports = {
6748
6666
  // Everything allowed, nothing to check
6749
6667
  return;
6750
6668
  }
6751
- // eslint-disable-next-line jsdoc/valid-types
6752
- /** @type import("../helpers/micromark.cjs").Token[] */
6753
- const micromarkTokens =
6754
- // @ts-ignore
6755
- params.parsers.micromark.tokens;
6756
- const { definitions } = referenceLinkImageData();
6757
- const links = filterByTypes(micromarkTokens, [ "autolink", "image", "link" ]);
6669
+ const { definitions } = getReferenceLinkImageData();
6670
+ const links = filterByTypesCached([ "autolink", "image", "link" ]);
6758
6671
  for (const link of links) {
6759
6672
  let label = null;
6760
6673
  let destination = null;
@@ -6797,7 +6710,8 @@ module.exports = {
6797
6710
  }
6798
6711
  }
6799
6712
  if (isError) {
6800
- let range = null;
6713
+ // eslint-disable-next-line no-undef-init
6714
+ let range = undefined;
6801
6715
  let fixInfo = null;
6802
6716
  if (startLine === endLine) {
6803
6717
  range = [ startColumn, endColumn - startColumn ];
@@ -6827,8 +6741,8 @@ module.exports = {
6827
6741
  onError,
6828
6742
  startLine,
6829
6743
  text.replace(nextLinesRe, ""),
6830
- null,
6831
- null,
6744
+ undefined,
6745
+ undefined,
6832
6746
  range,
6833
6747
  fixInfo
6834
6748
  );
@@ -6853,6 +6767,7 @@ module.exports = {
6853
6767
 
6854
6768
  const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6855
6769
  const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
6770
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6856
6771
 
6857
6772
  const whitespaceTypes = new Set([ "linePrefix", "whitespace" ]);
6858
6773
  const ignoreWhitespace = (tokens) => tokens.filter(
@@ -6876,14 +6791,12 @@ module.exports = {
6876
6791
  ((expectedStyle !== "no_leading_or_trailing") && (expectedStyle !== "trailing_only"));
6877
6792
  let expectedTrailingPipe =
6878
6793
  ((expectedStyle !== "no_leading_or_trailing") && (expectedStyle !== "leading_only"));
6879
- // eslint-disable-next-line jsdoc/valid-types
6880
- /** @type import("../helpers/micromark.cjs").Token[] */
6881
- const micromarkTokens =
6882
- // @ts-ignore
6883
- params.parsers.micromark.tokens;
6884
- const tables = filterByTypes(micromarkTokens, [ "table" ]);
6794
+ const tables = filterByTypesCached([ "table" ]);
6885
6795
  for (const table of tables) {
6886
- const rows = filterByTypes(table.children, [ "tableDelimiterRow", "tableRow" ]);
6796
+ const rows = filterByTypes(
6797
+ table.children,
6798
+ [ "tableDelimiterRow", "tableRow" ]
6799
+ );
6887
6800
  for (const row of rows) {
6888
6801
  // The following uses of first/lastOrNothing lack fallback handling
6889
6802
  // because it seems not to be possible (i.e., 0% coverage)
@@ -6926,7 +6839,7 @@ module.exports = {
6926
6839
  }
6927
6840
  }
6928
6841
  }
6929
- }
6842
+ };
6930
6843
 
6931
6844
 
6932
6845
  /***/ }),
@@ -6944,6 +6857,7 @@ module.exports = {
6944
6857
 
6945
6858
  const { addErrorDetailIf } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6946
6859
  const { filterByTypes } = __webpack_require__(/*! ../helpers/micromark.cjs */ "../helpers/micromark.cjs");
6860
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6947
6861
 
6948
6862
  const makeRange = (start, end) => [ start, end - start + 1 ];
6949
6863
 
@@ -6955,21 +6869,24 @@ module.exports = {
6955
6869
  "tags": [ "table" ],
6956
6870
  "parser": "micromark",
6957
6871
  "function": function MD056(params, onError) {
6958
- // eslint-disable-next-line jsdoc/valid-types
6959
- /** @type import("../helpers/micromark.cjs").Token[] */
6960
- const micromarkTokens =
6961
- // @ts-ignore
6962
- params.parsers.micromark.tokens;
6963
- const tables = filterByTypes(micromarkTokens, [ "table" ]);
6872
+ const tables = filterByTypesCached([ "table" ]);
6964
6873
  for (const table of tables) {
6965
- const rows = filterByTypes(table.children, [ "tableDelimiterRow", "tableRow" ]);
6874
+ const rows = filterByTypes(
6875
+ table.children,
6876
+ [ "tableDelimiterRow", "tableRow" ]
6877
+ );
6966
6878
  let expectedCount = 0;
6967
6879
  for (const row of rows) {
6968
- const cells = filterByTypes(row.children, [ "tableData", "tableDelimiter", "tableHeader" ]);
6880
+ const cells = filterByTypes(
6881
+ row.children,
6882
+ [ "tableData", "tableDelimiter", "tableHeader" ]
6883
+ );
6969
6884
  const actualCount = cells.length;
6970
6885
  expectedCount ||= actualCount;
6971
- let detail = null;
6972
- let range = null;
6886
+ // eslint-disable-next-line no-undef-init
6887
+ let detail = undefined;
6888
+ // eslint-disable-next-line no-undef-init
6889
+ let range = undefined;
6973
6890
  if (actualCount < expectedCount) {
6974
6891
  detail = "Too few cells, row will be missing data";
6975
6892
  range = [ row.endColumn - 1, 1 ];
@@ -6983,7 +6900,7 @@ module.exports = {
6983
6900
  expectedCount,
6984
6901
  actualCount,
6985
6902
  detail,
6986
- null,
6903
+ undefined,
6987
6904
  range
6988
6905
  );
6989
6906
  }
@@ -6992,6 +6909,60 @@ module.exports = {
6992
6909
  }
6993
6910
 
6994
6911
 
6912
+ /***/ }),
6913
+
6914
+ /***/ "../lib/md058.js":
6915
+ /*!***********************!*\
6916
+ !*** ../lib/md058.js ***!
6917
+ \***********************/
6918
+ /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
6919
+
6920
+ "use strict";
6921
+ // @ts-check
6922
+
6923
+
6924
+
6925
+ const { addErrorContextForLine, isBlankLine } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
6926
+ const { filterByTypesCached } = __webpack_require__(/*! ./cache */ "../lib/cache.js");
6927
+
6928
+ // eslint-disable-next-line jsdoc/valid-types
6929
+ /** @type import("./markdownlint").Rule */
6930
+ module.exports = {
6931
+ "names": [ "MD058", "blanks-around-tables" ],
6932
+ "description": "Tables should be surrounded by blank lines",
6933
+ "tags": [ "table" ],
6934
+ "parser": "micromark",
6935
+ "function": function MD058(params, onError) {
6936
+ const { lines } = params;
6937
+ // For every table...
6938
+ const tables = filterByTypesCached([ "table" ]);
6939
+ for (const table of tables) {
6940
+ // Look for a blank line above the table
6941
+ const firstIndex = table.startLine - 1;
6942
+ if (!isBlankLine(lines[firstIndex - 1])) {
6943
+ addErrorContextForLine(
6944
+ onError,
6945
+ // @ts-ignore
6946
+ lines,
6947
+ firstIndex
6948
+ );
6949
+ }
6950
+ // Look for a blank line below the table
6951
+ const lastIndex = table.endLine - 1;
6952
+ if (!isBlankLine(lines[lastIndex + 1])) {
6953
+ addErrorContextForLine(
6954
+ onError,
6955
+ // @ts-ignore
6956
+ lines,
6957
+ lastIndex,
6958
+ lastIndex + 2
6959
+ );
6960
+ }
6961
+ }
6962
+ }
6963
+ };
6964
+
6965
+
6995
6966
  /***/ }),
6996
6967
 
6997
6968
  /***/ "../lib/rules.js":
@@ -7007,6 +6978,11 @@ module.exports = {
7007
6978
 
7008
6979
  const { homepage, version } = __webpack_require__(/*! ./constants */ "../lib/constants.js");
7009
6980
 
6981
+ // @ts-ignore
6982
+ const [ md019, md021 ] = __webpack_require__(/*! ./md019-md021 */ "../lib/md019-md021.js");
6983
+ // @ts-ignore
6984
+ const [ md049, md050 ] = __webpack_require__(/*! ./md049-md050 */ "../lib/md049-md050.js");
6985
+
7010
6986
  const rules = [
7011
6987
  __webpack_require__(/*! ./md001 */ "../lib/md001.js"),
7012
6988
  // md002: Deprecated and removed
@@ -7022,9 +6998,9 @@ const rules = [
7022
6998
  __webpack_require__(/*! ./md013 */ "../lib/md013.js"),
7023
6999
  __webpack_require__(/*! ./md014 */ "../lib/md014.js"),
7024
7000
  __webpack_require__(/*! ./md018 */ "../lib/md018.js"),
7025
- __webpack_require__(/*! ./md019 */ "../lib/md019.js"),
7001
+ md019,
7026
7002
  __webpack_require__(/*! ./md020 */ "../lib/md020.js"),
7027
- __webpack_require__(/*! ./md021 */ "../lib/md021.js"),
7003
+ md021,
7028
7004
  __webpack_require__(/*! ./md022 */ "../lib/md022.js"),
7029
7005
  __webpack_require__(/*! ./md023 */ "../lib/md023.js"),
7030
7006
  __webpack_require__(/*! ./md024 */ "../lib/md024.js"),
@@ -7052,14 +7028,16 @@ const rules = [
7052
7028
  __webpack_require__(/*! ./md046 */ "../lib/md046.js"),
7053
7029
  __webpack_require__(/*! ./md047 */ "../lib/md047.js"),
7054
7030
  __webpack_require__(/*! ./md048 */ "../lib/md048.js"),
7055
- ...__webpack_require__(/*! ./md049-md050 */ "../lib/md049-md050.js"),
7031
+ md049,
7032
+ md050,
7056
7033
  __webpack_require__(/*! ./md051 */ "../lib/md051.js"),
7057
7034
  __webpack_require__(/*! ./md052 */ "../lib/md052.js"),
7058
7035
  __webpack_require__(/*! ./md053 */ "../lib/md053.js"),
7059
7036
  __webpack_require__(/*! ./md054 */ "../lib/md054.js"),
7060
7037
  __webpack_require__(/*! ./md055 */ "../lib/md055.js"),
7061
- __webpack_require__(/*! ./md056 */ "../lib/md056.js")
7038
+ __webpack_require__(/*! ./md056 */ "../lib/md056.js"),
7062
7039
  // md057: See https://github.com/markdownlint/markdownlint
7040
+ __webpack_require__(/*! ./md058 */ "../lib/md058.js")
7063
7041
  ];
7064
7042
  for (const rule of rules) {
7065
7043
  const name = rule.names[0].toLowerCase();