markdownlint 0.33.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 (82) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/CONTRIBUTING.md +4 -3
  3. package/README.md +25 -10
  4. package/demo/markdownlint-browser.js +3286 -2119
  5. package/doc/CustomRules.md +25 -13
  6. package/doc/Rules.md +106 -7
  7. package/doc/md013.md +8 -7
  8. package/doc/md034.md +5 -0
  9. package/doc/md044.md +10 -0
  10. package/doc/md049.md +3 -0
  11. package/doc/md050.md +3 -0
  12. package/doc/md051.md +26 -0
  13. package/doc/md058.md +48 -0
  14. package/helpers/README.md +3 -27
  15. package/helpers/helpers.js +60 -292
  16. package/helpers/micromark.cjs +153 -36
  17. package/helpers/package.json +2 -2
  18. package/lib/cache.js +62 -13
  19. package/lib/configuration-strict.d.ts +1185 -0
  20. package/lib/configuration.d.ts +3 -1173
  21. package/lib/constants.js +3 -2
  22. package/lib/markdownlint.d.ts +143 -61
  23. package/lib/markdownlint.js +151 -54
  24. package/lib/md001.js +17 -8
  25. package/lib/md003.js +16 -8
  26. package/lib/md004.js +57 -47
  27. package/lib/md005.js +7 -8
  28. package/lib/md007.js +13 -12
  29. package/lib/md009.js +52 -43
  30. package/lib/md010.js +50 -32
  31. package/lib/md011.js +18 -7
  32. package/lib/md012.js +19 -8
  33. package/lib/md013.js +46 -37
  34. package/lib/md014.js +37 -36
  35. package/lib/md018.js +20 -8
  36. package/lib/md019-md021.js +80 -0
  37. package/lib/md020.js +15 -6
  38. package/lib/md022.js +10 -12
  39. package/lib/md023.js +24 -22
  40. package/lib/md024.js +14 -8
  41. package/lib/md025.js +17 -9
  42. package/lib/md026.js +5 -6
  43. package/lib/md027.js +24 -43
  44. package/lib/md028.js +26 -14
  45. package/lib/md029.js +42 -33
  46. package/lib/md030.js +7 -8
  47. package/lib/md031.js +45 -22
  48. package/lib/md032.js +18 -30
  49. package/lib/md033.js +6 -4
  50. package/lib/md034.js +30 -9
  51. package/lib/md035.js +5 -3
  52. package/lib/md036.js +34 -39
  53. package/lib/md037.js +3 -0
  54. package/lib/md038.js +6 -5
  55. package/lib/md039.js +68 -52
  56. package/lib/md040.js +21 -21
  57. package/lib/md041.js +21 -26
  58. package/lib/md042.js +50 -36
  59. package/lib/md043.js +22 -13
  60. package/lib/md044.js +5 -2
  61. package/lib/md045.js +6 -4
  62. package/lib/md046.js +9 -8
  63. package/lib/md047.js +5 -2
  64. package/lib/md048.js +22 -13
  65. package/lib/md049-md050.js +19 -5
  66. package/lib/md051.js +18 -20
  67. package/lib/md052.js +5 -2
  68. package/lib/md053.js +5 -2
  69. package/lib/md054.js +12 -12
  70. package/lib/md055.js +10 -3
  71. package/lib/md056.js +18 -6
  72. package/lib/md058.js +43 -0
  73. package/lib/rules.js +11 -4
  74. package/package.json +22 -25
  75. package/schema/.markdownlint.jsonc +54 -51
  76. package/schema/.markdownlint.yaml +53 -50
  77. package/schema/ValidatingConfiguration.md +6 -8
  78. package/schema/markdownlint-config-schema-strict.json +1819 -0
  79. package/schema/markdownlint-config-schema.json +115 -105
  80. package/style/prettier.json +1 -0
  81. package/lib/md019.js +0 -39
  82. package/lib/md021.js +0 -62
@@ -18,10 +18,6 @@ const inlineCommentStartRe =
18
18
  /(<!--\s*markdownlint-(disable|enable|capture|restore|disable-file|enable-file|disable-line|disable-next-line|configure-file))(?:\s|-->)/gi;
19
19
  module.exports.inlineCommentStartRe = inlineCommentStartRe;
20
20
 
21
- // Regular expressions for range matching
22
- module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
23
- module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
24
-
25
21
  // Regular expression for blockquote prefixes
26
22
  const blockquotePrefixRe = /^[>\s]*/;
27
23
  module.exports.blockquotePrefixRe = blockquotePrefixRe;
@@ -143,7 +139,6 @@ function isBlankLine(line) {
143
139
  const startComment = "<!--";
144
140
  const endComment = "-->";
145
141
  const removeComments = (s) => {
146
- // eslint-disable-next-line no-constant-condition
147
142
  while (true) {
148
143
  const start = s.indexOf(startComment);
149
144
  const end = s.indexOf(endComment);
@@ -170,35 +165,6 @@ function isBlankLine(line) {
170
165
  }
171
166
  module.exports.isBlankLine = isBlankLine;
172
167
 
173
- /**
174
- * Compare function for Array.prototype.sort for ascending order of numbers.
175
- *
176
- * @param {number} a First number.
177
- * @param {number} b Second number.
178
- * @returns {number} Positive value if a>b, negative value if b<a, 0 otherwise.
179
- */
180
- module.exports.numericSortAscending = function numericSortAscending(a, b) {
181
- return a - b;
182
- };
183
-
184
- // Returns true iff the sorted array contains the specified element
185
- module.exports.includesSorted = function includesSorted(array, element) {
186
- let left = 0;
187
- let right = array.length - 1;
188
- while (left <= right) {
189
- // eslint-disable-next-line no-bitwise
190
- const mid = (left + right) >> 1;
191
- if (array[mid] < element) {
192
- left = mid + 1;
193
- } else if (array[mid] > element) {
194
- right = mid - 1;
195
- } else {
196
- return true;
197
- }
198
- }
199
- return false;
200
- };
201
-
202
168
  // Replaces the content of properly-formatted CommonMark comments with "."
203
169
  // This preserves the line/column information for the rest of the document
204
170
  // https://spec.commonmark.org/0.29/#html-blocks
@@ -277,7 +243,7 @@ module.exports.fencedCodeBlockStyleFor =
277
243
  * Return the string representation of a emphasis or strong markup character.
278
244
  *
279
245
  * @param {string} markup Emphasis or strong string.
280
- * @returns {string} String representation.
246
+ * @returns {"asterisk" | "underscore"} String representation.
281
247
  */
282
248
  module.exports.emphasisOrStrongStyleFor =
283
249
  function emphasisOrStrongStyleFor(markup) {
@@ -289,208 +255,6 @@ module.exports.emphasisOrStrongStyleFor =
289
255
  }
290
256
  };
291
257
 
292
- /**
293
- * Return the number of characters of indent for a token.
294
- *
295
- * @param {Object} token MarkdownItToken instance.
296
- * @returns {number} Characters of indent.
297
- */
298
- function indentFor(token) {
299
- const line = token.line.replace(/^[\s>]*(> |>)/, "");
300
- return line.length - line.trimStart().length;
301
- }
302
- module.exports.indentFor = indentFor;
303
-
304
- // Returns the heading style for a heading token
305
- module.exports.headingStyleFor = function headingStyleFor(token) {
306
- if ((token.map[1] - token.map[0]) === 1) {
307
- if (/[^\\]#\s*$/.test(token.line)) {
308
- return "atx_closed";
309
- }
310
- return "atx";
311
- }
312
- return "setext";
313
- };
314
-
315
- /**
316
- * Return the string representation of an unordered list marker.
317
- *
318
- * @param {Object} token MarkdownItToken instance.
319
- * @returns {string} String representation.
320
- */
321
- module.exports.unorderedListStyleFor = function unorderedListStyleFor(token) {
322
- switch (token.markup) {
323
- case "-":
324
- return "dash";
325
- case "+":
326
- return "plus";
327
- // case "*":
328
- default:
329
- return "asterisk";
330
- }
331
- };
332
-
333
- /**
334
- * @callback TokenCallback
335
- * @param {MarkdownItToken} token Current token.
336
- * @returns {void}
337
- */
338
-
339
- /**
340
- * Calls the provided function for each matching token.
341
- *
342
- * @param {Object} params RuleParams instance.
343
- * @param {string} type Token type identifier.
344
- * @param {TokenCallback} handler Callback function.
345
- * @returns {void}
346
- */
347
- function filterTokens(params, type, handler) {
348
- for (const token of params.parsers.markdownit.tokens) {
349
- if (token.type === type) {
350
- handler(token);
351
- }
352
- }
353
- }
354
- module.exports.filterTokens = filterTokens;
355
-
356
- /**
357
- * @typedef {Array} LineMetadata
358
- */
359
-
360
- /**
361
- * Gets a line metadata array.
362
- *
363
- * @param {Object} params RuleParams instance.
364
- * @returns {LineMetadata} Line metadata.
365
- */
366
- function getLineMetadata(params) {
367
- const lineMetadata = params.lines.map(
368
- (line, index) => [ line, index, false, 0, false, false, false ]
369
- );
370
- filterTokens(params, "fence", (token) => {
371
- lineMetadata[token.map[0]][3] = 1;
372
- lineMetadata[token.map[1] - 1][3] = -1;
373
- for (let i = token.map[0] + 1; i < token.map[1] - 1; i++) {
374
- lineMetadata[i][2] = true;
375
- }
376
- });
377
- filterTokens(params, "code_block", (token) => {
378
- for (let i = token.map[0]; i < token.map[1]; i++) {
379
- lineMetadata[i][2] = true;
380
- }
381
- });
382
- filterTokens(params, "table_open", (token) => {
383
- for (let i = token.map[0]; i < token.map[1]; i++) {
384
- lineMetadata[i][4] = true;
385
- }
386
- });
387
- filterTokens(params, "list_item_open", (token) => {
388
- let count = 1;
389
- for (let i = token.map[0]; i < token.map[1]; i++) {
390
- lineMetadata[i][5] = count;
391
- count++;
392
- }
393
- });
394
- filterTokens(params, "hr", (token) => {
395
- lineMetadata[token.map[0]][6] = true;
396
- });
397
- return lineMetadata;
398
- }
399
- module.exports.getLineMetadata = getLineMetadata;
400
-
401
- /**
402
- * @callback EachLineCallback
403
- * @param {string} line Line content.
404
- * @param {number} lineIndex Line index (0-based).
405
- * @param {boolean} inCode Iff in a code block.
406
- * @param {number} onFence + if open, - if closed, 0 otherwise.
407
- * @param {boolean} inTable Iff in a table.
408
- * @param {boolean} inItem Iff in a list item.
409
- * @param {boolean} inBreak Iff in semantic break.
410
- * @returns {void}
411
- */
412
-
413
- /**
414
- * Calls the provided function for each line.
415
- *
416
- * @param {LineMetadata} lineMetadata Line metadata object.
417
- * @param {EachLineCallback} handler Function taking (line, lineIndex, inCode,
418
- * onFence, inTable, inItem, inBreak).
419
- * @returns {void}
420
- */
421
- function forEachLine(lineMetadata, handler) {
422
- for (const metadata of lineMetadata) {
423
- // @ts-ignore
424
- handler(...metadata);
425
- }
426
- }
427
- module.exports.forEachLine = forEachLine;
428
-
429
- // Returns (nested) lists as a flat array (in order)
430
- module.exports.flattenLists = function flattenLists(tokens) {
431
- const flattenedLists = [];
432
- const stack = [];
433
- let current = null;
434
- let nesting = 0;
435
- const nestingStack = [];
436
- let lastWithMap = { "map": [ 0, 1 ] };
437
- for (const token of tokens) {
438
- if ((token.type === "bullet_list_open") ||
439
- (token.type === "ordered_list_open")) {
440
- // Save current context and start a new one
441
- stack.push(current);
442
- current = {
443
- "unordered": (token.type === "bullet_list_open"),
444
- "parentsUnordered": !current ||
445
- (current.unordered && current.parentsUnordered),
446
- "open": token,
447
- "indent": indentFor(token),
448
- "parentIndent": (current && current.indent) || 0,
449
- "items": [],
450
- "nesting": nesting,
451
- "lastLineIndex": -1,
452
- "insert": flattenedLists.length
453
- };
454
- nesting++;
455
- } else if ((token.type === "bullet_list_close") ||
456
- (token.type === "ordered_list_close")) {
457
- // Finalize current context and restore previous
458
- current.lastLineIndex = lastWithMap.map[1];
459
- flattenedLists.splice(current.insert, 0, current);
460
- delete current.insert;
461
- current = stack.pop();
462
- nesting--;
463
- } else if (token.type === "list_item_open") {
464
- // Add list item
465
- current.items.push(token);
466
- } else if (token.type === "blockquote_open") {
467
- nestingStack.push(nesting);
468
- nesting = 0;
469
- } else if (token.type === "blockquote_close") {
470
- nesting = nestingStack.pop() || 0;
471
- }
472
- if (token.map) {
473
- // Track last token with map
474
- lastWithMap = token;
475
- }
476
- }
477
- return flattenedLists;
478
- };
479
-
480
- // Calls the provided function for each heading's content
481
- module.exports.forEachHeading = function forEachHeading(params, handler) {
482
- let heading = null;
483
- for (const token of params.parsers.markdownit.tokens) {
484
- if (token.type === "heading_open") {
485
- heading = token;
486
- } else if (token.type === "heading_close") {
487
- heading = null;
488
- } else if ((token.type === "inline") && heading) {
489
- handler(heading, token.content, token);
490
- }
491
- }
492
- };
493
-
494
258
  /**
495
259
  * @callback InlineCodeSpanCallback
496
260
  * @param {string} code Code content.
@@ -596,8 +360,20 @@ function addError(onError, lineNumber, detail, context, range, fixInfo) {
596
360
  }
597
361
  module.exports.addError = addError;
598
362
 
599
- // Adds an error object with details conditionally via the onError callback
600
- module.exports.addErrorDetailIf = function addErrorDetailIf(
363
+ /**
364
+ * Adds an error object with details conditionally via the onError callback.
365
+ *
366
+ * @param {Object} onError RuleOnError instance.
367
+ * @param {number} lineNumber Line number.
368
+ * @param {Object} expected Expected value.
369
+ * @param {Object} actual Actual value.
370
+ * @param {string} [detail] Error details.
371
+ * @param {string} [context] Error context.
372
+ * @param {number[]} [range] Column and length of error.
373
+ * @param {Object} [fixInfo] RuleOnErrorFixInfo instance.
374
+ * @returns {void}
375
+ */
376
+ function addErrorDetailIf(
601
377
  onError, lineNumber, expected, actual, detail, context, range, fixInfo) {
602
378
  if (expected !== actual) {
603
379
  addError(
@@ -609,51 +385,55 @@ module.exports.addErrorDetailIf = function addErrorDetailIf(
609
385
  range,
610
386
  fixInfo);
611
387
  }
612
- };
388
+ }
389
+ module.exports.addErrorDetailIf = addErrorDetailIf;
613
390
 
614
- // Adds an error object with context via the onError callback
615
- module.exports.addErrorContext = function addErrorContext(
616
- onError, lineNumber, context, left, right, range, fixInfo) {
617
- context = ellipsify(context, left, right);
391
+ /**
392
+ * Adds an error object with context via the onError callback.
393
+ *
394
+ * @param {Object} onError RuleOnError instance.
395
+ * @param {number} lineNumber Line number.
396
+ * @param {string} context Error context.
397
+ * @param {boolean} [start] True iff the start of the text is important.
398
+ * @param {boolean} [end] True iff the end of the text is important.
399
+ * @param {number[]} [range] Column and length of error.
400
+ * @param {Object} [fixInfo] RuleOnErrorFixInfo instance.
401
+ * @returns {void}
402
+ */
403
+ function addErrorContext(
404
+ onError, lineNumber, context, start, end, range, fixInfo) {
405
+ context = ellipsify(context, start, end);
618
406
  addError(onError, lineNumber, undefined, context, range, fixInfo);
619
- };
407
+ }
408
+ module.exports.addErrorContext = addErrorContext;
620
409
 
621
410
  /**
622
- * Returns an array of code block and span content ranges.
411
+ * Adds an error object with context for a construct missing a blank line.
623
412
  *
624
- * @param {Object} params RuleParams instance.
625
- * @param {Object} lineMetadata Line metadata object.
626
- * @returns {number[][]} Array of ranges (lineIndex, columnIndex, length).
413
+ * @param {Object} onError RuleOnError instance.
414
+ * @param {string[]} lines Lines of Markdown content.
415
+ * @param {number} lineIndex Line index of line.
416
+ * @param {number} [lineNumber] Line number for override.
417
+ * @returns {void}
627
418
  */
628
- module.exports.codeBlockAndSpanRanges = (params, lineMetadata) => {
629
- const exclusions = [];
630
- // Add code block ranges (excludes fences)
631
- forEachLine(lineMetadata, (line, lineIndex, inCode, onFence) => {
632
- if (inCode && !onFence) {
633
- exclusions.push([ lineIndex, 0, line.length ]);
634
- }
635
- });
636
- // Add code span ranges (excludes ticks)
637
- filterTokens(params, "inline", (token) => {
638
- if (token.children.some((child) => child.type === "code_inline")) {
639
- const tokenLines = params.lines.slice(token.map[0], token.map[1]);
640
- forEachInlineCodeSpan(
641
- tokenLines.join("\n"),
642
- (code, lineIndex, columnIndex) => {
643
- const codeLines = code.split(newLineRe);
644
- for (const [ i, line ] of codeLines.entries()) {
645
- exclusions.push([
646
- token.lineNumber - 1 + lineIndex + i,
647
- i ? 0 : columnIndex,
648
- line.length
649
- ]);
650
- }
651
- }
652
- );
419
+ function addErrorContextForLine(onError, lines, lineIndex, lineNumber) {
420
+ const line = lines[lineIndex];
421
+ // @ts-ignore
422
+ const quotePrefix = line.match(blockquotePrefixRe)[0].trimEnd();
423
+ addErrorContext(
424
+ onError,
425
+ lineIndex + 1,
426
+ line.trim(),
427
+ undefined,
428
+ undefined,
429
+ undefined,
430
+ {
431
+ lineNumber,
432
+ "insertText": `${quotePrefix}\n`
653
433
  }
654
- });
655
- return exclusions;
656
- };
434
+ );
435
+ }
436
+ module.exports.addErrorContextForLine = addErrorContextForLine;
657
437
 
658
438
  /**
659
439
  * Determines whether the specified range is within another range.
@@ -673,18 +453,6 @@ const withinAnyRange = (ranges, lineIndex, index, length) => (
673
453
  );
674
454
  module.exports.withinAnyRange = withinAnyRange;
675
455
 
676
- // Returns a range object for a line by applying a RegExp
677
- module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
678
- let range = null;
679
- const match = line.match(regexp);
680
- if (match) {
681
- const column = match.index + 1;
682
- const length = match[0].length;
683
- range = [ column, length ];
684
- }
685
- return range;
686
- };
687
-
688
456
  // Determines if the front matter includes a title
689
457
  module.exports.frontMatterHasTitle =
690
458
  function frontMatterHasTitle(frontMatterLines, frontMatterTitlePattern) {
@@ -702,10 +470,10 @@ module.exports.frontMatterHasTitle =
702
470
  /**
703
471
  * Returns an object with information about reference links and images.
704
472
  *
705
- * @param {Object} params RuleParams instance.
473
+ * @param {import("../helpers/micromark.cjs").Token[]} tokens Micromark tokens.
706
474
  * @returns {Object} Reference link/image data.
707
475
  */
708
- function getReferenceLinkImageData(params) {
476
+ function getReferenceLinkImageData(tokens) {
709
477
  const normalizeReference = (s) => s.toLowerCase().trim().replace(/\s+/g, " ");
710
478
  const definitions = new Map();
711
479
  const definitionLineIndices = [];
@@ -714,7 +482,7 @@ function getReferenceLinkImageData(params) {
714
482
  const shortcuts = new Map();
715
483
  const filteredTokens =
716
484
  micromark.filterByTypes(
717
- params.parsers.micromark.tokens,
485
+ tokens,
718
486
  [
719
487
  // definitionLineIndices
720
488
  "definition", "gfmFootnoteDefinition",