markdownlint 0.23.0 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,7 +41,8 @@ A rule is implemented as an `Object` with one optional and four required propert
41
41
  - `description` is a required `String` value that describes the rule in output messages.
42
42
  - `information` is an optional (absolute) `URL` of a link to more information about the rule.
43
43
  - `tags` is a required `Array` of `String` values that groups related rules for easier customization.
44
- - `function` is a required synchronous `Function` that implements the rule and is passed two parameters:
44
+ - `asynchronous` is an optional `Boolean` value that indicates whether the rule returns a `Promise` and runs asynchronously.
45
+ - `function` is a required `Function` that implements the rule and is passed two parameters:
45
46
  - `params` is an `Object` with properties that describe the content being analyzed:
46
47
  - `name` is a `String` that identifies the input file/string.
47
48
  - `tokens` is an `Array` of [`markdown-it` `Token` objects](https://markdown-it.github.io/markdown-it/#Token)
@@ -51,7 +52,7 @@ A rule is implemented as an `Object` with one optional and four required propert
51
52
  - `config` is an `Object` corresponding to the rule's entry in `options.config` (if present).
52
53
  - `onError` is a function that takes a single `Object` parameter with one required and four optional properties:
53
54
  - `lineNumber` is a required `Number` specifying the 1-based line number of the error.
54
- - `details` is an optional `String` with information about what caused the error.
55
+ - `detail` is an optional `String` with information about what caused the error.
55
56
  - `context` is an optional `String` with relevant text surrounding the error location.
56
57
  - `range` is an optional `Array` with two `Number` values identifying the 1-based column and length of the error.
57
58
  - `fixInfo` is an optional `Object` with information about how to fix the error (all properties are optional, but
@@ -66,15 +67,25 @@ A rule is implemented as an `Object` with one optional and four required propert
66
67
  The collection of helper functions shared by the built-in rules is available for use by custom rules in the
67
68
  [markdownlint-rule-helpers package](https://www.npmjs.com/package/markdownlint-rule-helpers).
68
69
 
70
+ ### Asynchronous Rules
71
+
72
+ If a rule needs to perform asynchronous operations (such as fetching a network resource), it can specify the value `true` for its `asynchronous` property.
73
+ Asynchronous rules should return a `Promise` from their `function` implementation that is resolved when the rule completes.
74
+ (The value passed to `resolve(...)` is ignored.)
75
+ Linting violations from asynchronous rules are reported via the `onError` function just like for synchronous rules.
76
+
77
+ **Note**: Asynchronous rules cannot be referenced in a synchronous calling context (i.e., `markdownlint.sync(...)`).
78
+ Attempting to do so throws an exception.
79
+
69
80
  ## Examples
70
81
 
71
82
  - [Simple rules used by the project's test cases](../test/rules)
72
83
  - [Code for all `markdownlint` built-in rules](../lib)
73
84
  - [Package configuration for publishing to npm](../test/rules/npm)
74
85
  - Packages should export a single rule object or an `Array` of rule objects
75
- - [Custom rules from the Microsoft/vscode-docs-authoring repository](https://github.com/microsoft/vscode-docs-authoring/tree/master/packages/docs-linting/markdownlint-custom-rules)
86
+ - [Custom rules from the Microsoft/vscode-docs-authoring repository](https://github.com/microsoft/vscode-docs-authoring/tree/main/packages/docs-linting/markdownlint-custom-rules)
76
87
  - [Custom rules from the axibase/docs-util repository](https://github.com/axibase/docs-util/tree/master/linting-rules)
77
- - [Custom rules from the webhintio/hint repository](https://github.com/webhintio/hint/blob/master/scripts/lint-markdown.js)
88
+ - [Custom rules from the webhintio/hint repository](https://github.com/webhintio/hint/blob/main/scripts/lint-markdown.js)
78
89
 
79
90
  ## References
80
91
 
@@ -0,0 +1,23 @@
1
+ # Using markdownlint with Prettier
2
+
3
+ [`Prettier`](https://prettier.io) is a popular code formatter. For the most part,
4
+ it works seamlessly with `markdownlint`. Special situations are documented
5
+ below.
6
+
7
+ ## List item indentation
8
+
9
+ The default settings of `markdownlint` and `Prettier` are compatible and don't
10
+ result in any linting violations. If `Prettier` is used with `--tab-width` set
11
+ to `4`, the following `markdownlint` configuration can be used:
12
+
13
+ ```json
14
+ {
15
+ "MD007": {
16
+ "indent": 4
17
+ },
18
+ "MD030": {
19
+ "ul_single": 3,
20
+ "ul_multi": 3
21
+ }
22
+ }
23
+ ```
package/doc/Rules.md CHANGED
@@ -292,7 +292,11 @@ Tags: bullet, ul, indentation
292
292
 
293
293
  Aliases: ul-indent
294
294
 
295
- Parameters: indent, start_indented (number; default 2, boolean; default false)
295
+ <!-- markdownlint-disable line-length -->
296
+
297
+ Parameters: indent, start_indented, start_indent (number; default 2, boolean; default false, number; defaults to indent)
298
+
299
+ <!-- markdownlint-restore -->
296
300
 
297
301
  Fixable: Most violations can be fixed by tooling
298
302
 
@@ -319,7 +323,9 @@ rule).
319
323
 
320
324
  The `start_indented` parameter allows the first level of lists to be indented by
321
325
  the configured number of spaces rather than starting at zero (the inverse of
322
- MD006).
326
+ MD006). The `start_indent` parameter allows the first level of lists to be indented
327
+ by a different number of spaces than the rest (ignored when `start_indented` is not
328
+ set).
323
329
 
324
330
  Rationale: Indenting by 2 spaces allows the content of a nested list to be in
325
331
  line with the start of the content of the parent list when a single space is
@@ -329,6 +335,8 @@ issue for multi-markdown parsers, which require 4-space indents. More informatio
329
335
  <https://cirosantilli.com/markdown-style-guide#indentation-of-content-inside-lists>
330
336
  and <http://support.markedapp.com/discussions/problems/21-sub-lists-not-indenting>.
331
337
 
338
+ Note: See [Prettier.md](Prettier.md) for compatibility information.
339
+
332
340
  <a name="md009"></a>
333
341
 
334
342
  ## MD009 - Trailing spaces
@@ -388,7 +396,7 @@ Tags: whitespace, hard_tab
388
396
 
389
397
  Aliases: no-hard-tabs
390
398
 
391
- Parameters: code_blocks (boolean; default true)
399
+ Parameters: code_blocks, spaces_per_tab (boolean; default true, number; default 1)
392
400
 
393
401
  Fixable: Most violations can be fixed by tooling
394
402
 
@@ -416,9 +424,14 @@ Some text
416
424
  * Spaces used to indent the list item instead
417
425
  ```
418
426
 
419
- You have the option to exclude this rule for code blocks. To do so, set the
420
- `code_blocks` parameter to `false`. Code blocks are included by default since
421
- handling of tabs by tools is often inconsistent (ex: using 4 vs. 8 spaces).
427
+ You have the option to exclude this rule for code blocks and spans. To do so,
428
+ set the `code_blocks` parameter to `false`. Code blocks and spans are included
429
+ by default since handling of tabs by Markdown tools can be inconsistent (e.g.,
430
+ using 4 vs. 8 spaces).
431
+
432
+ By default, violations of this rule are fixed by replacing the tab with 1 space
433
+ character. To use a different number of spaces, set the `spaces_per_tab`
434
+ parameter to the desired value.
422
435
 
423
436
  Rationale: Hard tabs are often rendered inconsistently by different editors and
424
437
  can be harder to work with than spaces.
@@ -1180,6 +1193,8 @@ for your selected document style.
1180
1193
 
1181
1194
  Rationale: Violations of this rule can lead to improperly rendered content.
1182
1195
 
1196
+ Note: See [Prettier.md](Prettier.md) for compatibility information.
1197
+
1183
1198
  <a name="md031"></a>
1184
1199
 
1185
1200
  ## MD031 - Fenced code blocks should be surrounded by blank lines
@@ -1324,20 +1339,20 @@ For more information, see <https://www.example.com/>.
1324
1339
  ```
1325
1340
 
1326
1341
  Note: To use a bare URL without it being converted into a link, enclose it in
1327
- a code block, otherwise in some markdown parsers it _will_ be converted:
1342
+ a code block, otherwise in some markdown parsers it *will* be converted:
1328
1343
 
1329
1344
  ```markdown
1330
1345
  `https://www.example.com`
1331
1346
  ```
1332
1347
 
1333
- Note: The following scenario does _not_ trigger this rule to avoid conflicts
1348
+ Note: The following scenario does *not* trigger this rule to avoid conflicts
1334
1349
  with `MD011`/`no-reversed-links`:
1335
1350
 
1336
1351
  ```markdown
1337
1352
  [https://www.example.com]
1338
1353
  ```
1339
1354
 
1340
- The use of quotes around a bare link will _not_ trigger this rule, either:
1355
+ The use of quotes around a bare link will *not* trigger this rule, either:
1341
1356
 
1342
1357
  ```markdown
1343
1358
  "https://www.example.com"
@@ -1355,8 +1370,8 @@ Tags: hr
1355
1370
 
1356
1371
  Aliases: hr-style
1357
1372
 
1358
- Parameters: style ("consistent", "---", "***", or other string specifying the
1359
- horizontal rule; default "consistent")
1373
+ Parameters: style ("consistent", "---", "***", "___", or other string specifying
1374
+ the horizontal rule; default "consistent")
1360
1375
 
1361
1376
  This rule is triggered when inconsistent styles of horizontal rules are used
1362
1377
  in the document:
@@ -1569,6 +1584,14 @@ echo Hello world
1569
1584
  ```
1570
1585
  ````
1571
1586
 
1587
+ To display a code block without syntax highlighting, use:
1588
+
1589
+ ````markdown
1590
+ ```text
1591
+ Plain text in a code block
1592
+ ```
1593
+ ````
1594
+
1572
1595
  Rationale: Specifying a language improves content rendering by using the
1573
1596
  correct syntax highlighting for code. More information:
1574
1597
  <https://cirosantilli.com/markdown-style-guide#option-code-fenced>.
@@ -1747,7 +1770,8 @@ the proper capitalization, specify the desired letter case in the `names` array:
1747
1770
  ]
1748
1771
  ```
1749
1772
 
1750
- Set the `code_blocks` parameter to `false` to disable this rule for code blocks.
1773
+ Set the `code_blocks` parameter to `false` to disable this rule for code blocks
1774
+ and spans.
1751
1775
 
1752
1776
  Rationale: Incorrect capitalization of proper names is usually a mistake.
1753
1777
 
@@ -1896,3 +1920,67 @@ The configured list style can be a specific symbol to use (backtick, tilde), or
1896
1920
  can require that usage be consistent within the document.
1897
1921
 
1898
1922
  Rationale: Consistent formatting makes it easier to understand a document.
1923
+
1924
+ <a name="md049"></a>
1925
+
1926
+ ## MD049 - Emphasis style should be consistent
1927
+
1928
+ Tags: emphasis
1929
+
1930
+ Aliases: emphasis-style
1931
+
1932
+ Parameters: style ("consistent", "asterisk", "underscore"; default "consistent")
1933
+
1934
+ Fixable: Most violations can be fixed by tooling
1935
+
1936
+ This rule is triggered when the symbols used in the document for emphasis do not
1937
+ match the configured emphasis style:
1938
+
1939
+ ```markdown
1940
+ *Text*
1941
+ _Text_
1942
+ ```
1943
+
1944
+ To fix this issue, use the configured emphasis style throughout the document:
1945
+
1946
+ ```markdown
1947
+ *Text*
1948
+ *Text*
1949
+ ```
1950
+
1951
+ The configured emphasis style can be a specific symbol to use ("asterisk",
1952
+ "underscore"), or can require that usage be consistent within the document.
1953
+
1954
+ Rationale: Consistent formatting makes it easier to understand a document.
1955
+
1956
+ <a name="md050"></a>
1957
+
1958
+ ## MD050 - Strong style should be consistent
1959
+
1960
+ Tags: emphasis
1961
+
1962
+ Aliases: strong-style
1963
+
1964
+ Parameters: style ("consistent", "asterisk", "underscore"; default "consistent")
1965
+
1966
+ Fixable: Most violations can be fixed by tooling
1967
+
1968
+ This rule is triggered when the symbols used in the document for strong do not
1969
+ match the configured strong style:
1970
+
1971
+ ```markdown
1972
+ **Text**
1973
+ __Text__
1974
+ ```
1975
+
1976
+ To fix this issue, use the configured strong style throughout the document:
1977
+
1978
+ ```markdown
1979
+ **Text**
1980
+ **Text**
1981
+ ```
1982
+
1983
+ The configured strong style can be a specific symbol to use ("asterisk",
1984
+ "underscore"), or can require that usage be consistent within the document.
1985
+
1986
+ Rationale: Consistent formatting makes it easier to understand a document.
package/helpers/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015-2021 David Anson
3
+ Copyright (c) 2015-2022 David Anson
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -2,8 +2,6 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const os = require("os");
6
-
7
5
  // Regular expression for matching common newline characters
8
6
  // See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
9
7
  const newLineRe = /\r\n?|\n/g;
@@ -29,10 +27,11 @@ module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
29
27
  const emphasisMarkersRe = /[_*]/g;
30
28
 
31
29
  // Regular expression for inline links and shortcut reference links
32
- const linkRe = /\[(?:[^[\]]|\[[^\]]*\])*\](?:\(\S*\))?/g;
30
+ const linkRe = /(\[(?:[^[\]]|\[[^\]]*\])*\])(\(\S*\)|\[\S*\])?/g;
31
+ module.exports.linkRe = linkRe;
33
32
 
34
- // readFile options for reading with the UTF-8 encoding
35
- module.exports.utf8Encoding = "utf8";
33
+ // Regular expression for link reference definition lines
34
+ module.exports.linkReferenceRe = /^ {0,3}\[[^\]]+]:\s.*$/;
36
35
 
37
36
  // All punctuation characters (normal and full-width)
38
37
  const allPunctuation = ".,;:!?。,;:!?";
@@ -62,12 +61,20 @@ module.exports.isObject = function isObject(obj) {
62
61
  };
63
62
 
64
63
  // Returns true iff the input line is blank (no content)
65
- // Example: Contains nothing, whitespace, or comments
66
- const blankLineRe = />|(?:<!--.*?-->)/g;
64
+ // Example: Contains nothing, whitespace, or comment (unclosed start/end okay)
67
65
  module.exports.isBlankLine = function isBlankLine(line) {
68
66
  // Call to String.replace follows best practices and is not a security check
69
67
  // False-positive for js/incomplete-multi-character-sanitization
70
- return !line || !line.trim() || !line.replace(blankLineRe, "").trim();
68
+ return (
69
+ !line ||
70
+ !line.trim() ||
71
+ !line
72
+ .replace(/<!--.*?-->/g, "")
73
+ .replace(/<!--.*$/g, "")
74
+ .replace(/^.*-->/g, "")
75
+ .replace(/>/g, "")
76
+ .trim()
77
+ );
71
78
  };
72
79
 
73
80
  /**
@@ -180,6 +187,22 @@ module.exports.fencedCodeBlockStyleFor =
180
187
  }
181
188
  };
182
189
 
190
+ /**
191
+ * Return the string representation of a emphasis or strong markup character.
192
+ *
193
+ * @param {string} markup Emphasis or strong string.
194
+ * @returns {string} String representation.
195
+ */
196
+ module.exports.emphasisOrStrongStyleFor =
197
+ function emphasisOrStrongStyleFor(markup) {
198
+ switch (markup[0]) {
199
+ case "*":
200
+ return "asterisk";
201
+ default:
202
+ return "underscore";
203
+ }
204
+ };
205
+
183
206
  /**
184
207
  * Return the number of characters of indent for a token.
185
208
  *
@@ -246,11 +269,12 @@ module.exports.filterTokens = filterTokens;
246
269
  */
247
270
  function isMathBlock(token) {
248
271
  return (
249
- (token.tag === "math") &&
272
+ ((token.tag === "$$") || (token.tag === "math")) &&
250
273
  token.type.startsWith("math_block") &&
251
274
  !token.type.endsWith("_end")
252
275
  );
253
276
  }
277
+ module.exports.isMathBlock = isMathBlock;
254
278
 
255
279
  // Get line metadata array
256
280
  module.exports.getLineMetadata = function getLineMetadata(params) {
@@ -292,28 +316,30 @@ module.exports.getLineMetadata = function getLineMetadata(params) {
292
316
  return lineMetadata;
293
317
  };
294
318
 
295
- // Calls the provided function for each line (with context)
296
- module.exports.forEachLine = function forEachLine(lineMetadata, handler) {
319
+ /**
320
+ * Calls the provided function for each line.
321
+ *
322
+ * @param {Object} lineMetadata Line metadata object.
323
+ * @param {Function} handler Function taking (line, lineIndex, inCode, onFence,
324
+ * inTable, inItem, inBreak, inMath).
325
+ * @returns {void}
326
+ */
327
+ function forEachLine(lineMetadata, handler) {
297
328
  lineMetadata.forEach(function forMetadata(metadata) {
298
- // Parameters:
299
- // line, lineIndex, inCode, onFence, inTable, inItem, inBreak, inMath
300
329
  handler(...metadata);
301
330
  });
302
- };
331
+ }
332
+ module.exports.forEachLine = forEachLine;
303
333
 
304
334
  // Returns (nested) lists as a flat array (in order)
305
- module.exports.flattenLists = function flattenLists(params) {
335
+ module.exports.flattenLists = function flattenLists(tokens) {
306
336
  const flattenedLists = [];
307
337
  const stack = [];
308
338
  let current = null;
309
339
  let nesting = 0;
310
340
  const nestingStack = [];
311
341
  let lastWithMap = { "map": [ 0, 1 ] };
312
- params.tokens.forEach((token) => {
313
- if (isMathBlock(token) && token.map[1]) {
314
- // markdown-it-texmath plugin does not account for math_block_end
315
- token.map[1]++;
316
- }
342
+ tokens.forEach((token) => {
317
343
  if ((token.type === "bullet_list_open") ||
318
344
  (token.type === "ordered_list_open")) {
319
345
  // Save current context and start a new one
@@ -385,7 +411,8 @@ module.exports.forEachHeading = function forEachHeading(params, handler) {
385
411
  * Calls the provided function for each inline code span's content.
386
412
  *
387
413
  * @param {string} input Markdown content.
388
- * @param {Function} handler Callback function.
414
+ * @param {Function} handler Callback function taking (code, lineIndex,
415
+ * columnIndex, ticks).
389
416
  * @returns {void}
390
417
  */
391
418
  function forEachInlineCodeSpan(input, handler) {
@@ -519,6 +546,60 @@ module.exports.addErrorContext = function addErrorContext(
519
546
  addError(onError, lineNumber, null, context, range, fixInfo);
520
547
  };
521
548
 
549
+ /**
550
+ * Returns an array of code block and span content ranges.
551
+ *
552
+ * @param {Object} params RuleParams instance.
553
+ * @param {Object} lineMetadata Line metadata object.
554
+ * @returns {number[][]} Array of ranges (lineIndex, columnIndex, length).
555
+ */
556
+ module.exports.codeBlockAndSpanRanges = (params, lineMetadata) => {
557
+ const exclusions = [];
558
+ // Add code block ranges (excludes fences)
559
+ forEachLine(lineMetadata, (line, lineIndex, inCode, onFence) => {
560
+ if (inCode && !onFence) {
561
+ exclusions.push([ lineIndex, 0, line.length ]);
562
+ }
563
+ });
564
+ // Add code span ranges (excludes ticks)
565
+ filterTokens(params, "inline", (token) => {
566
+ if (token.children.some((child) => child.type === "code_inline")) {
567
+ const tokenLines = params.lines.slice(token.map[0], token.map[1]);
568
+ forEachInlineCodeSpan(
569
+ tokenLines.join("\n"),
570
+ (code, lineIndex, columnIndex) => {
571
+ const codeLines = code.split(newLineRe);
572
+ for (const [ i, line ] of codeLines.entries()) {
573
+ exclusions.push([
574
+ token.lineNumber - 1 + lineIndex + i,
575
+ i ? 0 : columnIndex,
576
+ line.length
577
+ ]);
578
+ }
579
+ }
580
+ );
581
+ }
582
+ });
583
+ return exclusions;
584
+ };
585
+
586
+ /**
587
+ * Determines whether the specified range overlaps another range.
588
+ *
589
+ * @param {number[][]} ranges Array of ranges (line, index, length).
590
+ * @param {number} lineIndex Line index to check.
591
+ * @param {number} index Index to check.
592
+ * @param {number} length Length to check.
593
+ * @returns {boolean} True iff the specified range overlaps.
594
+ */
595
+ module.exports.overlapsAnyRange = (ranges, lineIndex, index, length) => (
596
+ !ranges.every((span) => (
597
+ (lineIndex !== span[0]) ||
598
+ (index + length < span[1]) ||
599
+ (index > span[1] + span[2])
600
+ ))
601
+ );
602
+
522
603
  // Returns a range object for a line by applying a RegExp
523
604
  module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
524
605
  let range = null;
@@ -554,6 +635,18 @@ module.exports.frontMatterHasTitle =
554
635
  function emphasisMarkersInContent(params) {
555
636
  const { lines } = params;
556
637
  const byLine = new Array(lines.length);
638
+ // Search links
639
+ lines.forEach((tokenLine, tokenLineIndex) => {
640
+ const inLine = [];
641
+ let linkMatch = null;
642
+ while ((linkMatch = linkRe.exec(tokenLine))) {
643
+ let markerMatch = null;
644
+ while ((markerMatch = emphasisMarkersRe.exec(linkMatch[0]))) {
645
+ inLine.push(linkMatch.index + markerMatch.index);
646
+ }
647
+ }
648
+ byLine[tokenLineIndex] = inLine;
649
+ });
557
650
  // Search code spans
558
651
  filterTokens(params, "inline", (token) => {
559
652
  const { children, lineNumber, map } = token;
@@ -564,31 +657,19 @@ function emphasisMarkersInContent(params) {
564
657
  (code, lineIndex, column, tickCount) => {
565
658
  const codeLines = code.split(newLineRe);
566
659
  codeLines.forEach((codeLine, codeLineIndex) => {
660
+ const byLineIndex = lineNumber - 1 + lineIndex + codeLineIndex;
661
+ const inLine = byLine[byLineIndex];
662
+ const codeLineOffset = codeLineIndex ? 0 : column - 1 + tickCount;
567
663
  let match = null;
568
664
  while ((match = emphasisMarkersRe.exec(codeLine))) {
569
- const byLineIndex = lineNumber - 1 + lineIndex + codeLineIndex;
570
- const inLine = byLine[byLineIndex] || [];
571
- const codeLineOffset = codeLineIndex ? 0 : column - 1 + tickCount;
572
665
  inLine.push(codeLineOffset + match.index);
573
- byLine[byLineIndex] = inLine;
574
666
  }
667
+ byLine[byLineIndex] = inLine;
575
668
  });
576
669
  }
577
670
  );
578
671
  }
579
672
  });
580
- // Search links
581
- lines.forEach((tokenLine, tokenLineIndex) => {
582
- let linkMatch = null;
583
- while ((linkMatch = linkRe.exec(tokenLine))) {
584
- let markerMatch = null;
585
- while ((markerMatch = emphasisMarkersRe.exec(linkMatch[0]))) {
586
- const inLine = byLine[tokenLineIndex] || [];
587
- inLine.push(linkMatch.index + markerMatch.index);
588
- byLine[tokenLineIndex] = inLine;
589
- }
590
- }
591
- });
592
673
  return byLine;
593
674
  }
594
675
  module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
@@ -597,9 +678,10 @@ module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
597
678
  * Gets the most common line ending, falling back to the platform default.
598
679
  *
599
680
  * @param {string} input Markdown content to analyze.
681
+ * @param {string} [platform] Platform identifier (process.platform).
600
682
  * @returns {string} Preferred line ending.
601
683
  */
602
- function getPreferredLineEnding(input) {
684
+ function getPreferredLineEnding(input, platform) {
603
685
  let cr = 0;
604
686
  let lf = 0;
605
687
  let crlf = 0;
@@ -620,7 +702,8 @@ function getPreferredLineEnding(input) {
620
702
  });
621
703
  let preferredLineEnding = null;
622
704
  if (!cr && !lf && !crlf) {
623
- preferredLineEnding = os.EOL;
705
+ preferredLineEnding =
706
+ ((platform || process.platform) === "win32") ? "\r\n" : "\n";
624
707
  } else if ((lf >= crlf) && (lf >= cr)) {
625
708
  preferredLineEnding = "\n";
626
709
  } else if (crlf >= cr) {
@@ -735,3 +818,80 @@ module.exports.applyFixes = function applyFixes(input, errors) {
735
818
  // Return corrected input
736
819
  return lines.filter((line) => line !== null).join(lineEnding);
737
820
  };
821
+
822
+ /**
823
+ * Gets the range and fixInfo values for reporting an error if the expected
824
+ * text is found on the specified line.
825
+ *
826
+ * @param {string[]} lines Lines of Markdown content.
827
+ * @param {number} lineIndex Line index to check.
828
+ * @param {string} search Text to search for.
829
+ * @param {string} replace Text to replace with.
830
+ * @returns {Object} Range and fixInfo wrapper.
831
+ */
832
+ function getRangeAndFixInfoIfFound(lines, lineIndex, search, replace) {
833
+ let range = null;
834
+ let fixInfo = null;
835
+ const searchIndex = lines[lineIndex].indexOf(search);
836
+ if (searchIndex !== -1) {
837
+ const column = searchIndex + 1;
838
+ const length = search.length;
839
+ range = [ column, length ];
840
+ fixInfo = {
841
+ "editColumn": column,
842
+ "deleteCount": length,
843
+ "insertText": replace
844
+ };
845
+ }
846
+ return {
847
+ range,
848
+ fixInfo
849
+ };
850
+ }
851
+ module.exports.getRangeAndFixInfoIfFound = getRangeAndFixInfoIfFound;
852
+
853
+ /**
854
+ * Gets the next (subsequent) child token if it is of the expected type.
855
+ *
856
+ * @param {Object} parentToken Parent token.
857
+ * @param {Object} childToken Child token basis.
858
+ * @param {string} nextType Token type of next token.
859
+ * @param {string} nextNextType Token type of next-next token.
860
+ * @returns {Object} Next token.
861
+ */
862
+ function getNextChildToken(parentToken, childToken, nextType, nextNextType) {
863
+ const { children } = parentToken;
864
+ const index = children.indexOf(childToken);
865
+ if (
866
+ (index !== -1) &&
867
+ (children.length > index + 2) &&
868
+ (children[index + 1].type === nextType) &&
869
+ (children[index + 2].type === nextNextType)
870
+ ) {
871
+ return children[index + 1];
872
+ }
873
+ return null;
874
+ }
875
+ module.exports.getNextChildToken = getNextChildToken;
876
+
877
+ /**
878
+ * Calls Object.freeze() on an object and its children.
879
+ *
880
+ * @param {Object} obj Object to deep freeze.
881
+ * @returns {Object} Object passed to the function.
882
+ */
883
+ function deepFreeze(obj) {
884
+ const pending = [ obj ];
885
+ let current = null;
886
+ while ((current = pending.shift())) {
887
+ Object.freeze(current);
888
+ for (const name of Object.getOwnPropertyNames(current)) {
889
+ const value = current[name];
890
+ if (value && (typeof value === "object")) {
891
+ pending.push(value);
892
+ }
893
+ }
894
+ }
895
+ return obj;
896
+ }
897
+ module.exports.deepFreeze = deepFreeze;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdownlint-rule-helpers",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "A collection of markdownlint helper functions for custom rules",
5
5
  "main": "helpers.js",
6
6
  "author": "David Anson (https://dlaa.me/)",
package/lib/cache.js CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- let lineMetadata = null;
6
- module.exports.lineMetadata = (value) => {
5
+ let codeBlockAndSpanRanges = null;
6
+ module.exports.codeBlockAndSpanRanges = (value) => {
7
7
  if (value) {
8
- lineMetadata = value;
8
+ codeBlockAndSpanRanges = value;
9
9
  }
10
- return lineMetadata;
10
+ return codeBlockAndSpanRanges;
11
11
  };
12
12
 
13
13
  let flattenedLists = null;
@@ -18,7 +18,16 @@ module.exports.flattenedLists = (value) => {
18
18
  return flattenedLists;
19
19
  };
20
20
 
21
+ let lineMetadata = null;
22
+ module.exports.lineMetadata = (value) => {
23
+ if (value) {
24
+ lineMetadata = value;
25
+ }
26
+ return lineMetadata;
27
+ };
28
+
21
29
  module.exports.clear = () => {
22
- lineMetadata = null;
30
+ codeBlockAndSpanRanges = null;
23
31
  flattenedLists = null;
32
+ lineMetadata = null;
24
33
  };
@@ -0,0 +1,7 @@
1
+ // @ts-check
2
+
3
+ "use strict";
4
+
5
+ module.exports.deprecatedRuleNames = [ "MD002", "MD006" ];
6
+ module.exports.homepage = "https://github.com/DavidAnson/markdownlint";
7
+ module.exports.version = "0.25.1";