markdownlint 0.29.0 → 0.31.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.
@@ -4,9 +4,7 @@
4
4
 
5
5
  const micromark = require("./micromark.cjs");
6
6
 
7
- // Regular expression for matching common newline characters
8
- // See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
9
- const newLineRe = /\r\n?|\n/g;
7
+ const { newLineRe } = require("./shared.js");
10
8
  module.exports.newLineRe = newLineRe;
11
9
 
12
10
  // Regular expression for matching common front matter (YAML and TOML)
@@ -20,17 +18,10 @@ const inlineCommentStartRe =
20
18
  /(<!--\s*markdownlint-(disable|enable|capture|restore|disable-file|enable-file|disable-line|disable-next-line|configure-file))(?:\s|-->)/gi;
21
19
  module.exports.inlineCommentStartRe = inlineCommentStartRe;
22
20
 
23
- // Regular expression for matching HTML elements
24
- const htmlElementRe = /<(([A-Za-z][A-Za-z\d-]*)(?:\s[^`>]*)?)\/?>/g;
25
- module.exports.htmlElementRe = htmlElementRe;
26
-
27
21
  // Regular expressions for range matching
28
22
  module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
29
23
  module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
30
24
 
31
- // Regular expression for all instances of emphasis markers
32
- const emphasisMarkersRe = /[_*]/g;
33
-
34
25
  // Regular expression for blockquote prefixes
35
26
  const blockquotePrefixRe = /^[>\s]*/;
36
27
  module.exports.blockquotePrefixRe = blockquotePrefixRe;
@@ -39,6 +30,16 @@ module.exports.blockquotePrefixRe = blockquotePrefixRe;
39
30
  const linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])\]:/;
40
31
  module.exports.linkReferenceDefinitionRe = linkReferenceDefinitionRe;
41
32
 
33
+ // Regular expression for identifying an HTML entity at the end of a line
34
+ module.exports.endOfLineHtmlEntityRe =
35
+ // eslint-disable-next-line max-len
36
+ /&(?:#\d+|#[xX][\da-fA-F]+|[a-zA-Z]{2,31}|blk\d{2}|emsp1[34]|frac\d{2}|sup\d|there4);$/;
37
+
38
+ // Regular expression for identifying a GitHub emoji code at the end of a line
39
+ module.exports.endOfLineGemojiCodeRe =
40
+ // eslint-disable-next-line max-len
41
+ /:(?:[abmovx]|[-+]1|100|1234|(?:1st|2nd|3rd)_place_medal|8ball|clock\d{1,4}|e-mail|non-potable_water|o2|t-rex|u5272|u5408|u55b6|u6307|u6708|u6709|u6e80|u7121|u7533|u7981|u7a7a|[a-z]{2,15}2?|[a-z]{1,14}(?:_[a-z\d]{1,16})+):$/;
42
+
42
43
  // All punctuation characters (normal and full-width)
43
44
  const allPunctuation = ".,;:!?。,;:!?";
44
45
  module.exports.allPunctuation = allPunctuation;
@@ -46,25 +47,82 @@ module.exports.allPunctuation = allPunctuation;
46
47
  // All punctuation characters without question mark (normal and full-width)
47
48
  module.exports.allPunctuationNoQuestion = allPunctuation.replace(/[??]/gu, "");
48
49
 
49
- // Returns true iff the input is a number
50
- module.exports.isNumber = function isNumber(obj) {
50
+ /**
51
+ * Returns true iff the input is a Number.
52
+ *
53
+ * @param {Object} obj Object of unknown type.
54
+ * @returns {boolean} True iff obj is a Number.
55
+ */
56
+ function isNumber(obj) {
51
57
  return typeof obj === "number";
52
- };
58
+ }
59
+ module.exports.isNumber = isNumber;
53
60
 
54
- // Returns true iff the input is a string
55
- module.exports.isString = function isString(obj) {
61
+ /**
62
+ * Returns true iff the input is a String.
63
+ *
64
+ * @param {Object} obj Object of unknown type.
65
+ * @returns {boolean} True iff obj is a String.
66
+ */
67
+ function isString(obj) {
56
68
  return typeof obj === "string";
57
- };
69
+ }
70
+ module.exports.isString = isString;
58
71
 
59
- // Returns true iff the input string is empty
60
- module.exports.isEmptyString = function isEmptyString(str) {
72
+ /**
73
+ * Returns true iff the input String is empty.
74
+ *
75
+ * @param {string} str String of unknown length.
76
+ * @returns {boolean} True iff the input String is empty.
77
+ */
78
+ function isEmptyString(str) {
61
79
  return str.length === 0;
62
- };
80
+ }
81
+ module.exports.isEmptyString = isEmptyString;
63
82
 
64
- // Returns true iff the input is an object
65
- module.exports.isObject = function isObject(obj) {
66
- return (obj !== null) && (typeof obj === "object") && !Array.isArray(obj);
67
- };
83
+ /**
84
+ * Returns true iff the input is an Object.
85
+ *
86
+ * @param {Object} obj Object of unknown type.
87
+ * @returns {boolean} True iff obj is an Object.
88
+ */
89
+ function isObject(obj) {
90
+ return !!obj && (typeof obj === "object") && !Array.isArray(obj);
91
+ }
92
+ module.exports.isObject = isObject;
93
+
94
+ /**
95
+ * Returns true iff the input is a URL.
96
+ *
97
+ * @param {Object} obj Object of unknown type.
98
+ * @returns {boolean} True iff obj is a URL.
99
+ */
100
+ function isUrl(obj) {
101
+ return !!obj && (Object.getPrototypeOf(obj) === URL.prototype);
102
+ }
103
+ module.exports.isUrl = isUrl;
104
+
105
+ /**
106
+ * Clones the input if it is an Array.
107
+ *
108
+ * @param {Object} arr Object of unknown type.
109
+ * @returns {Object} Clone of obj iff obj is an Array.
110
+ */
111
+ function cloneIfArray(arr) {
112
+ return Array.isArray(arr) ? [ ...arr ] : arr;
113
+ }
114
+ module.exports.cloneIfArray = cloneIfArray;
115
+
116
+ /**
117
+ * Clones the input if it is a URL.
118
+ *
119
+ * @param {Object} url Object of unknown type.
120
+ * @returns {Object} Clone of obj iff obj is a URL.
121
+ */
122
+ function cloneIfUrl(url) {
123
+ return isUrl(url) ? new URL(url) : url;
124
+ }
125
+ module.exports.cloneIfUrl = cloneIfUrl;
68
126
 
69
127
  /**
70
128
  * Returns true iff the input line is blank (contains nothing, whitespace, or
@@ -264,12 +322,18 @@ module.exports.unorderedListStyleFor = function unorderedListStyleFor(token) {
264
322
  }
265
323
  };
266
324
 
325
+ /**
326
+ * @callback TokenCallback
327
+ * @param {MarkdownItToken} token Current token.
328
+ * @returns {void}
329
+ */
330
+
267
331
  /**
268
332
  * Calls the provided function for each matching token.
269
333
  *
270
334
  * @param {Object} params RuleParams instance.
271
335
  * @param {string} type Token type identifier.
272
- * @param {Function} handler Callback function.
336
+ * @param {TokenCallback} handler Callback function.
273
337
  * @returns {void}
274
338
  */
275
339
  function filterTokens(params, type, handler) {
@@ -282,24 +346,18 @@ function filterTokens(params, type, handler) {
282
346
  module.exports.filterTokens = filterTokens;
283
347
 
284
348
  /**
285
- * Returns whether a token is a math block (created by markdown-it-texmath).
286
- *
287
- * @param {Object} token MarkdownItToken instance.
288
- * @returns {boolean} True iff token is a math block.
349
+ * @typedef {Array} LineMetadata
289
350
  */
290
- function isMathBlock(token) {
291
- return (
292
- ((token.tag === "$$") || (token.tag === "math")) &&
293
- token.type.startsWith("math_block") &&
294
- !token.type.endsWith("_end")
295
- );
296
- }
297
- module.exports.isMathBlock = isMathBlock;
298
351
 
299
- // Get line metadata array
300
- module.exports.getLineMetadata = function getLineMetadata(params) {
352
+ /**
353
+ * Gets a line metadata array.
354
+ *
355
+ * @param {Object} params RuleParams instance.
356
+ * @returns {LineMetadata} Line metadata.
357
+ */
358
+ function getLineMetadata(params) {
301
359
  const lineMetadata = params.lines.map(
302
- (line, index) => [ line, index, false, 0, false, false, false, false ]
360
+ (line, index) => [ line, index, false, 0, false, false, false ]
303
361
  );
304
362
  filterTokens(params, "fence", (token) => {
305
363
  lineMetadata[token.map[0]][3] = 1;
@@ -328,24 +386,33 @@ module.exports.getLineMetadata = function getLineMetadata(params) {
328
386
  filterTokens(params, "hr", (token) => {
329
387
  lineMetadata[token.map[0]][6] = true;
330
388
  });
331
- for (const token of params.parsers.markdownit.tokens.filter(isMathBlock)) {
332
- for (let i = token.map[0]; i < token.map[1]; i++) {
333
- lineMetadata[i][7] = true;
334
- }
335
- }
336
389
  return lineMetadata;
337
- };
390
+ }
391
+ module.exports.getLineMetadata = getLineMetadata;
392
+
393
+ /**
394
+ * @callback EachLineCallback
395
+ * @param {string} line Line content.
396
+ * @param {number} lineIndex Line index (0-based).
397
+ * @param {boolean} inCode Iff in a code block.
398
+ * @param {number} onFence + if open, - if closed, 0 otherwise.
399
+ * @param {boolean} inTable Iff in a table.
400
+ * @param {boolean} inItem Iff in a list item.
401
+ * @param {boolean} inBreak Iff in semantic break.
402
+ * @returns {void}
403
+ */
338
404
 
339
405
  /**
340
406
  * Calls the provided function for each line.
341
407
  *
342
- * @param {Object} lineMetadata Line metadata object.
343
- * @param {Function} handler Function taking (line, lineIndex, inCode, onFence,
344
- * inTable, inItem, inBreak, inMath).
408
+ * @param {LineMetadata} lineMetadata Line metadata object.
409
+ * @param {EachLineCallback} handler Function taking (line, lineIndex, inCode,
410
+ * onFence, inTable, inItem, inBreak).
345
411
  * @returns {void}
346
412
  */
347
413
  function forEachLine(lineMetadata, handler) {
348
414
  for (const metadata of lineMetadata) {
415
+ // @ts-ignore
349
416
  handler(...metadata);
350
417
  }
351
418
  }
@@ -402,23 +469,6 @@ module.exports.flattenLists = function flattenLists(tokens) {
402
469
  return flattenedLists;
403
470
  };
404
471
 
405
- /**
406
- * Calls the provided function for each specified inline child token.
407
- *
408
- * @param {Object} params RuleParams instance.
409
- * @param {string} type Token type identifier.
410
- * @param {Function} handler Callback function.
411
- * @returns {void}
412
- */
413
- function forEachInlineChild(params, type, handler) {
414
- filterTokens(params, "inline", (token) => {
415
- for (const child of token.children.filter((c) => c.type === type)) {
416
- handler(child, token);
417
- }
418
- });
419
- }
420
- module.exports.forEachInlineChild = forEachInlineChild;
421
-
422
472
  // Calls the provided function for each heading's content
423
473
  module.exports.forEachHeading = function forEachHeading(params, handler) {
424
474
  let heading = null;
@@ -433,12 +483,21 @@ module.exports.forEachHeading = function forEachHeading(params, handler) {
433
483
  }
434
484
  };
435
485
 
486
+ /**
487
+ * @callback InlineCodeSpanCallback
488
+ * @param {string} code Code content.
489
+ * @param {number} lineIndex Line index (0-based).
490
+ * @param {number} columnIndex Column index (0-based).
491
+ * @param {number} ticks Count of backticks.
492
+ * @returns {void}
493
+ */
494
+
436
495
  /**
437
496
  * Calls the provided function for each inline code span's content.
438
497
  *
439
498
  * @param {string} input Markdown content.
440
- * @param {Function} handler Callback function taking (code, lineIndex,
441
- * columnIndex, ticks).
499
+ * @param {InlineCodeSpanCallback} handler Callback function taking (code,
500
+ * lineIndex, columnIndex, ticks).
442
501
  * @returns {void}
443
502
  */
444
503
  function forEachInlineCodeSpan(input, handler) {
@@ -588,51 +647,6 @@ module.exports.codeBlockAndSpanRanges = (params, lineMetadata) => {
588
647
  return exclusions;
589
648
  };
590
649
 
591
- /**
592
- * Returns an array of HTML element ranges.
593
- *
594
- * @param {Object} params RuleParams instance.
595
- * @param {Object} lineMetadata Line metadata object.
596
- * @returns {number[][]} Array of ranges (lineIndex, columnIndex, length).
597
- */
598
- module.exports.htmlElementRanges = (params, lineMetadata) => {
599
- const exclusions = [];
600
- // Match with htmlElementRe
601
- forEachLine(lineMetadata, (line, lineIndex, inCode) => {
602
- let match = null;
603
- // eslint-disable-next-line no-unmodified-loop-condition
604
- while (!inCode && ((match = htmlElementRe.exec(line)) !== null)) {
605
- exclusions.push([ lineIndex, match.index, match[0].length ]);
606
- }
607
- });
608
- // Match with html_inline
609
- forEachInlineChild(params, "html_inline", (token, parent) => {
610
- const parentContent = parent.content;
611
- let tokenContent = token.content;
612
- const parentIndex = parentContent.indexOf(tokenContent);
613
- let deltaLines = 0;
614
- let indent = 0;
615
- for (let i = parentIndex - 1; i >= 0; i--) {
616
- if (parentContent[i] === "\n") {
617
- deltaLines++;
618
- } else if (deltaLines === 0) {
619
- indent++;
620
- }
621
- }
622
- let lineIndex = token.lineNumber - 1 + deltaLines;
623
- do {
624
- const index = tokenContent.indexOf("\n");
625
- const length = (index === -1) ? tokenContent.length : index;
626
- exclusions.push([ lineIndex, indent, length ]);
627
- tokenContent = tokenContent.slice(length + 1);
628
- lineIndex++;
629
- indent = 0;
630
- } while (tokenContent.length > 0);
631
- });
632
- // Return results
633
- return exclusions;
634
- };
635
-
636
650
  /**
637
651
  * Determines whether the specified range is within another range.
638
652
  *
@@ -677,129 +691,6 @@ module.exports.frontMatterHasTitle =
677
691
  frontMatterLines.some((line) => frontMatterTitleRe.test(line));
678
692
  };
679
693
 
680
- /**
681
- * Calls the provided function for each link.
682
- *
683
- * @param {string} line Line of Markdown input.
684
- * @param {Function} handler Function taking (index, link, text, destination).
685
- * @returns {void}
686
- */
687
- function forEachLink(line, handler) {
688
- // Helper to find matching close symbol for link text/destination
689
- const findClosingSymbol = (index) => {
690
- const begin = line[index];
691
- const end = (begin === "[") ? "]" : ")";
692
- let nesting = 0;
693
- let escaping = false;
694
- let pointy = false;
695
- for (let i = index + 1; i < line.length; i++) {
696
- const current = line[i];
697
- if (current === "\\") {
698
- escaping = !escaping;
699
- } else if (!escaping && (current === begin)) {
700
- nesting++;
701
- } else if (!escaping && (current === end)) {
702
- if (nesting > 0) {
703
- nesting--;
704
- } else if (!pointy) {
705
- // Return index after matching close symbol
706
- return i + 1;
707
- }
708
- } else if ((i === index + 1) && (begin === "(") && (current === "<")) {
709
- pointy = true;
710
- } else if (!escaping && pointy && current === ">") {
711
- pointy = false;
712
- nesting = 0;
713
- } else {
714
- escaping = false;
715
- }
716
- }
717
- // No match found
718
- return -1;
719
- };
720
- // Scan line for unescaped "[" character
721
- let escaping = false;
722
- for (let i = 0; i < line.length; i++) {
723
- const current = line[i];
724
- if (current === "\\") {
725
- escaping = !escaping;
726
- } else if (!escaping && (current === "[")) {
727
- // Scan for matching close "]" of link text
728
- const textEnd = findClosingSymbol(i);
729
- if (textEnd !== -1) {
730
- if ((line[textEnd] === "(") || (line[textEnd] === "[")) {
731
- // Scan for matching close ")" or "]" of link destination
732
- const destEnd = findClosingSymbol(textEnd);
733
- if (destEnd !== -1) {
734
- // Call handler with link text and destination
735
- const link = line.slice(i, destEnd);
736
- const text = line.slice(i, textEnd);
737
- const dest = line.slice(textEnd, destEnd);
738
- handler(i, link, text, dest);
739
- i = destEnd;
740
- }
741
- }
742
- if (i < textEnd) {
743
- // Call handler with link text only
744
- const text = line.slice(i, textEnd);
745
- handler(i, text, text);
746
- i = textEnd;
747
- }
748
- }
749
- } else {
750
- escaping = false;
751
- }
752
- }
753
- }
754
- module.exports.forEachLink = forEachLink;
755
-
756
- /**
757
- * Returns a list of emphasis markers in code spans and links.
758
- *
759
- * @param {Object} params RuleParams instance.
760
- * @returns {number[][]} List of markers.
761
- */
762
- function emphasisMarkersInContent(params) {
763
- const { lines } = params;
764
- const byLine = new Array(lines.length);
765
- // Search links
766
- for (const [ tokenLineIndex, tokenLine ] of lines.entries()) {
767
- const inLine = [];
768
- forEachLink(tokenLine, (index, match) => {
769
- let markerMatch = null;
770
- while ((markerMatch = emphasisMarkersRe.exec(match))) {
771
- inLine.push(index + markerMatch.index);
772
- }
773
- });
774
- byLine[tokenLineIndex] = inLine;
775
- }
776
- // Search code spans
777
- filterTokens(params, "inline", (token) => {
778
- const { children, lineNumber, map } = token;
779
- if (children.some((child) => child.type === "code_inline")) {
780
- const tokenLines = lines.slice(map[0], map[1]);
781
- forEachInlineCodeSpan(
782
- tokenLines.join("\n"),
783
- (code, lineIndex, column, tickCount) => {
784
- const codeLines = code.split(newLineRe);
785
- for (const [ codeLineIndex, codeLine ] of codeLines.entries()) {
786
- const byLineIndex = lineNumber - 1 + lineIndex + codeLineIndex;
787
- const inLine = byLine[byLineIndex];
788
- const codeLineOffset = codeLineIndex ? 0 : column - 1 + tickCount;
789
- let match = null;
790
- while ((match = emphasisMarkersRe.exec(codeLine))) {
791
- inLine.push(codeLineOffset + match.index);
792
- }
793
- byLine[byLineIndex] = inLine;
794
- }
795
- }
796
- );
797
- }
798
- });
799
- return byLine;
800
- }
801
- module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
802
-
803
694
  /**
804
695
  * Returns an object with information about reference links and images.
805
696
  *
@@ -1092,3 +983,23 @@ function expandTildePath(file, os) {
1092
983
  return homedir ? file.replace(/^~($|\/|\\)/, `${homedir}$1`) : file;
1093
984
  }
1094
985
  module.exports.expandTildePath = expandTildePath;
986
+
987
+ // Copied from markdownlint.js to avoid TypeScript compiler import() issue.
988
+ /**
989
+ * @typedef {Object} MarkdownItToken
990
+ * @property {string[][]} attrs HTML attributes.
991
+ * @property {boolean} block Block-level token.
992
+ * @property {MarkdownItToken[]} children Child nodes.
993
+ * @property {string} content Tag contents.
994
+ * @property {boolean} hidden Ignore element.
995
+ * @property {string} info Fence info.
996
+ * @property {number} level Nesting level.
997
+ * @property {number[]} map Beginning/ending line numbers.
998
+ * @property {string} markup Markup text.
999
+ * @property {Object} meta Arbitrary data.
1000
+ * @property {number} nesting Level change.
1001
+ * @property {string} tag HTML tag name.
1002
+ * @property {string} type Token type.
1003
+ * @property {number} lineNumber Line number (1-based).
1004
+ * @property {string} line Line content.
1005
+ */