markdownlint 0.24.0 → 0.25.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.
- package/CONTRIBUTING.md +13 -3
- package/README.md +17 -7
- package/demo/markdownlint-browser.js +584 -367
- package/doc/CustomRules.md +15 -4
- package/doc/Rules.md +86 -13
- package/helpers/helpers.js +168 -50
- package/helpers/package.json +1 -1
- package/lib/cache.js +9 -9
- package/lib/constants.js +7 -0
- package/lib/markdownlint.d.ts +6 -2
- package/lib/markdownlint.js +216 -213
- package/lib/md007.js +4 -2
- package/lib/md010.js +21 -16
- package/lib/md011.js +2 -2
- package/lib/md033.js +12 -8
- package/lib/md035.js +4 -4
- package/lib/md037.js +5 -3
- package/lib/md039.js +2 -1
- package/lib/md043.js +0 -1
- package/lib/md044.js +2 -2
- package/lib/md049.js +45 -0
- package/lib/md050.js +45 -0
- package/lib/rules.js +4 -4
- package/package.json +25 -23
- package/schema/.markdownlint.jsonc +15 -1
- package/schema/.markdownlint.yaml +12 -0
- package/schema/markdownlint-config-schema.json +95 -1
package/doc/CustomRules.md
CHANGED
|
@@ -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
|
-
- `
|
|
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
|
-
- `
|
|
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/
|
|
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/
|
|
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
|
|
package/doc/Rules.md
CHANGED
|
@@ -292,7 +292,11 @@ Tags: bullet, ul, indentation
|
|
|
292
292
|
|
|
293
293
|
Aliases: ul-indent
|
|
294
294
|
|
|
295
|
-
|
|
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
|
|
@@ -418,12 +424,14 @@ Some text
|
|
|
418
424
|
* Spaces used to indent the list item instead
|
|
419
425
|
```
|
|
420
426
|
|
|
421
|
-
You have the option to exclude this rule for code blocks. To do so,
|
|
422
|
-
`code_blocks` parameter to `false`. Code blocks are included
|
|
423
|
-
handling of tabs by tools
|
|
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).
|
|
424
431
|
|
|
425
|
-
|
|
426
|
-
|
|
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.
|
|
427
435
|
|
|
428
436
|
Rationale: Hard tabs are often rendered inconsistently by different editors and
|
|
429
437
|
can be harder to work with than spaces.
|
|
@@ -1331,20 +1339,20 @@ For more information, see <https://www.example.com/>.
|
|
|
1331
1339
|
```
|
|
1332
1340
|
|
|
1333
1341
|
Note: To use a bare URL without it being converted into a link, enclose it in
|
|
1334
|
-
a code block, otherwise in some markdown parsers it
|
|
1342
|
+
a code block, otherwise in some markdown parsers it *will* be converted:
|
|
1335
1343
|
|
|
1336
1344
|
```markdown
|
|
1337
1345
|
`https://www.example.com`
|
|
1338
1346
|
```
|
|
1339
1347
|
|
|
1340
|
-
Note: The following scenario does
|
|
1348
|
+
Note: The following scenario does *not* trigger this rule to avoid conflicts
|
|
1341
1349
|
with `MD011`/`no-reversed-links`:
|
|
1342
1350
|
|
|
1343
1351
|
```markdown
|
|
1344
1352
|
[https://www.example.com]
|
|
1345
1353
|
```
|
|
1346
1354
|
|
|
1347
|
-
The use of quotes around a bare link will
|
|
1355
|
+
The use of quotes around a bare link will *not* trigger this rule, either:
|
|
1348
1356
|
|
|
1349
1357
|
```markdown
|
|
1350
1358
|
"https://www.example.com"
|
|
@@ -1362,8 +1370,8 @@ Tags: hr
|
|
|
1362
1370
|
|
|
1363
1371
|
Aliases: hr-style
|
|
1364
1372
|
|
|
1365
|
-
Parameters: style ("consistent", "---", "***", or other string specifying
|
|
1366
|
-
horizontal rule; default "consistent")
|
|
1373
|
+
Parameters: style ("consistent", "---", "***", "___", or other string specifying
|
|
1374
|
+
the horizontal rule; default "consistent")
|
|
1367
1375
|
|
|
1368
1376
|
This rule is triggered when inconsistent styles of horizontal rules are used
|
|
1369
1377
|
in the document:
|
|
@@ -1762,7 +1770,8 @@ the proper capitalization, specify the desired letter case in the `names` array:
|
|
|
1762
1770
|
]
|
|
1763
1771
|
```
|
|
1764
1772
|
|
|
1765
|
-
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.
|
|
1766
1775
|
|
|
1767
1776
|
Rationale: Incorrect capitalization of proper names is usually a mistake.
|
|
1768
1777
|
|
|
@@ -1911,3 +1920,67 @@ The configured list style can be a specific symbol to use (backtick, tilde), or
|
|
|
1911
1920
|
can require that usage be consistent within the document.
|
|
1912
1921
|
|
|
1913
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/helpers.js
CHANGED
|
@@ -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;
|
|
@@ -63,12 +61,20 @@ module.exports.isObject = function isObject(obj) {
|
|
|
63
61
|
};
|
|
64
62
|
|
|
65
63
|
// Returns true iff the input line is blank (no content)
|
|
66
|
-
// Example: Contains nothing, whitespace, or
|
|
67
|
-
const blankLineRe = />|(?:<!--.*?-->)/g;
|
|
64
|
+
// Example: Contains nothing, whitespace, or comment (unclosed start/end okay)
|
|
68
65
|
module.exports.isBlankLine = function isBlankLine(line) {
|
|
69
66
|
// Call to String.replace follows best practices and is not a security check
|
|
70
67
|
// False-positive for js/incomplete-multi-character-sanitization
|
|
71
|
-
return
|
|
68
|
+
return (
|
|
69
|
+
!line ||
|
|
70
|
+
!line.trim() ||
|
|
71
|
+
!line
|
|
72
|
+
.replace(/<!--.*?-->/g, "")
|
|
73
|
+
.replace(/<!--.*$/g, "")
|
|
74
|
+
.replace(/^.*-->/g, "")
|
|
75
|
+
.replace(/>/g, "")
|
|
76
|
+
.trim()
|
|
77
|
+
);
|
|
72
78
|
};
|
|
73
79
|
|
|
74
80
|
/**
|
|
@@ -181,6 +187,22 @@ module.exports.fencedCodeBlockStyleFor =
|
|
|
181
187
|
}
|
|
182
188
|
};
|
|
183
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
|
+
|
|
184
206
|
/**
|
|
185
207
|
* Return the number of characters of indent for a token.
|
|
186
208
|
*
|
|
@@ -247,11 +269,12 @@ module.exports.filterTokens = filterTokens;
|
|
|
247
269
|
*/
|
|
248
270
|
function isMathBlock(token) {
|
|
249
271
|
return (
|
|
250
|
-
(token.tag === "math") &&
|
|
272
|
+
((token.tag === "$$") || (token.tag === "math")) &&
|
|
251
273
|
token.type.startsWith("math_block") &&
|
|
252
274
|
!token.type.endsWith("_end")
|
|
253
275
|
);
|
|
254
276
|
}
|
|
277
|
+
module.exports.isMathBlock = isMathBlock;
|
|
255
278
|
|
|
256
279
|
// Get line metadata array
|
|
257
280
|
module.exports.getLineMetadata = function getLineMetadata(params) {
|
|
@@ -293,14 +316,20 @@ module.exports.getLineMetadata = function getLineMetadata(params) {
|
|
|
293
316
|
return lineMetadata;
|
|
294
317
|
};
|
|
295
318
|
|
|
296
|
-
|
|
297
|
-
|
|
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) {
|
|
298
328
|
lineMetadata.forEach(function forMetadata(metadata) {
|
|
299
|
-
// Parameters:
|
|
300
|
-
// line, lineIndex, inCode, onFence, inTable, inItem, inBreak, inMath
|
|
301
329
|
handler(...metadata);
|
|
302
330
|
});
|
|
303
|
-
}
|
|
331
|
+
}
|
|
332
|
+
module.exports.forEachLine = forEachLine;
|
|
304
333
|
|
|
305
334
|
// Returns (nested) lists as a flat array (in order)
|
|
306
335
|
module.exports.flattenLists = function flattenLists(tokens) {
|
|
@@ -311,10 +340,6 @@ module.exports.flattenLists = function flattenLists(tokens) {
|
|
|
311
340
|
const nestingStack = [];
|
|
312
341
|
let lastWithMap = { "map": [ 0, 1 ] };
|
|
313
342
|
tokens.forEach((token) => {
|
|
314
|
-
if (isMathBlock(token) && token.map[1]) {
|
|
315
|
-
// markdown-it-texmath plugin does not account for math_block_end
|
|
316
|
-
token.map[1]++;
|
|
317
|
-
}
|
|
318
343
|
if ((token.type === "bullet_list_open") ||
|
|
319
344
|
(token.type === "ordered_list_open")) {
|
|
320
345
|
// Save current context and start a new one
|
|
@@ -386,7 +411,8 @@ module.exports.forEachHeading = function forEachHeading(params, handler) {
|
|
|
386
411
|
* Calls the provided function for each inline code span's content.
|
|
387
412
|
*
|
|
388
413
|
* @param {string} input Markdown content.
|
|
389
|
-
* @param {Function} handler Callback function
|
|
414
|
+
* @param {Function} handler Callback function taking (code, lineIndex,
|
|
415
|
+
* columnIndex, ticks).
|
|
390
416
|
* @returns {void}
|
|
391
417
|
*/
|
|
392
418
|
function forEachInlineCodeSpan(input, handler) {
|
|
@@ -521,26 +547,39 @@ module.exports.addErrorContext = function addErrorContext(
|
|
|
521
547
|
};
|
|
522
548
|
|
|
523
549
|
/**
|
|
524
|
-
* Returns an array of code span ranges.
|
|
550
|
+
* Returns an array of code block and span content ranges.
|
|
525
551
|
*
|
|
526
|
-
* @param {
|
|
527
|
-
* @
|
|
552
|
+
* @param {Object} params RuleParams instance.
|
|
553
|
+
* @param {Object} lineMetadata Line metadata object.
|
|
554
|
+
* @returns {number[][]} Array of ranges (lineIndex, columnIndex, length).
|
|
528
555
|
*/
|
|
529
|
-
module.exports.
|
|
556
|
+
module.exports.codeBlockAndSpanRanges = (params, lineMetadata) => {
|
|
530
557
|
const exclusions = [];
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
(
|
|
534
|
-
|
|
535
|
-
// eslint-disable-next-line unicorn/no-for-loop
|
|
536
|
-
for (let i = 0; i < codeLines.length; i++) {
|
|
537
|
-
exclusions.push(
|
|
538
|
-
[ lineIndex + i, columnIndex, codeLines[i].length ]
|
|
539
|
-
);
|
|
540
|
-
columnIndex = 0;
|
|
541
|
-
}
|
|
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 ]);
|
|
542
562
|
}
|
|
543
|
-
);
|
|
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
|
+
});
|
|
544
583
|
return exclusions;
|
|
545
584
|
};
|
|
546
585
|
|
|
@@ -596,6 +635,18 @@ module.exports.frontMatterHasTitle =
|
|
|
596
635
|
function emphasisMarkersInContent(params) {
|
|
597
636
|
const { lines } = params;
|
|
598
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
|
+
});
|
|
599
650
|
// Search code spans
|
|
600
651
|
filterTokens(params, "inline", (token) => {
|
|
601
652
|
const { children, lineNumber, map } = token;
|
|
@@ -606,31 +657,19 @@ function emphasisMarkersInContent(params) {
|
|
|
606
657
|
(code, lineIndex, column, tickCount) => {
|
|
607
658
|
const codeLines = code.split(newLineRe);
|
|
608
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;
|
|
609
663
|
let match = null;
|
|
610
664
|
while ((match = emphasisMarkersRe.exec(codeLine))) {
|
|
611
|
-
const byLineIndex = lineNumber - 1 + lineIndex + codeLineIndex;
|
|
612
|
-
const inLine = byLine[byLineIndex] || [];
|
|
613
|
-
const codeLineOffset = codeLineIndex ? 0 : column - 1 + tickCount;
|
|
614
665
|
inLine.push(codeLineOffset + match.index);
|
|
615
|
-
byLine[byLineIndex] = inLine;
|
|
616
666
|
}
|
|
667
|
+
byLine[byLineIndex] = inLine;
|
|
617
668
|
});
|
|
618
669
|
}
|
|
619
670
|
);
|
|
620
671
|
}
|
|
621
672
|
});
|
|
622
|
-
// Search links
|
|
623
|
-
lines.forEach((tokenLine, tokenLineIndex) => {
|
|
624
|
-
let linkMatch = null;
|
|
625
|
-
while ((linkMatch = linkRe.exec(tokenLine))) {
|
|
626
|
-
let markerMatch = null;
|
|
627
|
-
while ((markerMatch = emphasisMarkersRe.exec(linkMatch[0]))) {
|
|
628
|
-
const inLine = byLine[tokenLineIndex] || [];
|
|
629
|
-
inLine.push(linkMatch.index + markerMatch.index);
|
|
630
|
-
byLine[tokenLineIndex] = inLine;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
});
|
|
634
673
|
return byLine;
|
|
635
674
|
}
|
|
636
675
|
module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
|
|
@@ -639,9 +678,10 @@ module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
|
|
|
639
678
|
* Gets the most common line ending, falling back to the platform default.
|
|
640
679
|
*
|
|
641
680
|
* @param {string} input Markdown content to analyze.
|
|
681
|
+
* @param {string} [platform] Platform identifier (process.platform).
|
|
642
682
|
* @returns {string} Preferred line ending.
|
|
643
683
|
*/
|
|
644
|
-
function getPreferredLineEnding(input) {
|
|
684
|
+
function getPreferredLineEnding(input, platform) {
|
|
645
685
|
let cr = 0;
|
|
646
686
|
let lf = 0;
|
|
647
687
|
let crlf = 0;
|
|
@@ -662,7 +702,8 @@ function getPreferredLineEnding(input) {
|
|
|
662
702
|
});
|
|
663
703
|
let preferredLineEnding = null;
|
|
664
704
|
if (!cr && !lf && !crlf) {
|
|
665
|
-
preferredLineEnding =
|
|
705
|
+
preferredLineEnding =
|
|
706
|
+
((platform || process.platform) === "win32") ? "\r\n" : "\n";
|
|
666
707
|
} else if ((lf >= crlf) && (lf >= cr)) {
|
|
667
708
|
preferredLineEnding = "\n";
|
|
668
709
|
} else if (crlf >= cr) {
|
|
@@ -777,3 +818,80 @@ module.exports.applyFixes = function applyFixes(input, errors) {
|
|
|
777
818
|
// Return corrected input
|
|
778
819
|
return lines.filter((line) => line !== null).join(lineEnding);
|
|
779
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;
|
package/helpers/package.json
CHANGED
package/lib/cache.js
CHANGED
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
let
|
|
6
|
-
module.exports.
|
|
5
|
+
let codeBlockAndSpanRanges = null;
|
|
6
|
+
module.exports.codeBlockAndSpanRanges = (value) => {
|
|
7
7
|
if (value) {
|
|
8
|
-
|
|
8
|
+
codeBlockAndSpanRanges = value;
|
|
9
9
|
}
|
|
10
|
-
return
|
|
10
|
+
return codeBlockAndSpanRanges;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
let
|
|
14
|
-
module.exports.
|
|
13
|
+
let flattenedLists = null;
|
|
14
|
+
module.exports.flattenedLists = (value) => {
|
|
15
15
|
if (value) {
|
|
16
|
-
|
|
16
|
+
flattenedLists = value;
|
|
17
17
|
}
|
|
18
|
-
return
|
|
18
|
+
return flattenedLists;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
let lineMetadata = null;
|
|
@@ -27,7 +27,7 @@ module.exports.lineMetadata = (value) => {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
module.exports.clear = () => {
|
|
30
|
+
codeBlockAndSpanRanges = null;
|
|
30
31
|
flattenedLists = null;
|
|
31
|
-
inlineCodeSpanRanges = null;
|
|
32
32
|
lineMetadata = null;
|
|
33
33
|
};
|
package/lib/constants.js
ADDED
package/lib/markdownlint.d.ts
CHANGED
|
@@ -206,9 +206,9 @@ type RuleOnErrorInfo = {
|
|
|
206
206
|
*/
|
|
207
207
|
lineNumber: number;
|
|
208
208
|
/**
|
|
209
|
-
*
|
|
209
|
+
* Detail about the error.
|
|
210
210
|
*/
|
|
211
|
-
|
|
211
|
+
detail?: string;
|
|
212
212
|
/**
|
|
213
213
|
* Context for the error.
|
|
214
214
|
*/
|
|
@@ -263,6 +263,10 @@ type Rule = {
|
|
|
263
263
|
* Rule tag(s).
|
|
264
264
|
*/
|
|
265
265
|
tags: string[];
|
|
266
|
+
/**
|
|
267
|
+
* True if asynchronous.
|
|
268
|
+
*/
|
|
269
|
+
asynchronous?: boolean;
|
|
266
270
|
/**
|
|
267
271
|
* Rule implementation.
|
|
268
272
|
*/
|