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.
- package/CONTRIBUTING.md +13 -3
- package/LICENSE +1 -1
- package/README.md +53 -16
- package/demo/markdownlint-browser.js +769 -461
- package/doc/CustomRules.md +15 -4
- package/doc/Prettier.md +23 -0
- package/doc/Rules.md +100 -12
- package/helpers/LICENSE +1 -1
- package/helpers/helpers.js +199 -39
- package/helpers/package.json +1 -1
- package/lib/cache.js +14 -5
- package/lib/constants.js +7 -0
- package/lib/markdownlint.d.ts +30 -10
- package/lib/markdownlint.js +326 -247
- package/lib/md007.js +4 -2
- package/lib/md010.js +25 -16
- package/lib/md011.js +31 -27
- package/lib/md033.js +12 -8
- package/lib/md035.js +4 -4
- package/lib/md037.js +19 -5
- package/lib/md039.js +2 -1
- package/lib/md043.js +8 -4
- package/lib/md044.js +54 -71
- package/lib/md049.js +45 -0
- package/lib/md050.js +45 -0
- package/lib/rules.js +4 -4
- package/package.json +44 -34
- package/schema/.markdownlint.jsonc +19 -3
- package/schema/.markdownlint.yaml +15 -1
- package/schema/markdownlint-config-schema.json +108 -4
- package/schema/build-config-example.js +0 -53
- package/schema/build-config-schema.js +0 -420
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/Prettier.md
ADDED
|
@@ -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
|
-
|
|
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,
|
|
420
|
-
`code_blocks` parameter to `false`. Code blocks are included
|
|
421
|
-
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).
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
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;
|
|
@@ -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 =
|
|
30
|
+
const linkRe = /(\[(?:[^[\]]|\[[^\]]*\])*\])(\(\S*\)|\[\S*\])?/g;
|
|
31
|
+
module.exports.linkRe = linkRe;
|
|
33
32
|
|
|
34
|
-
//
|
|
35
|
-
module.exports.
|
|
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
|
|
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
|
|
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
|
-
|
|
296
|
-
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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;
|
package/helpers/package.json
CHANGED
package/lib/cache.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
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
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
|
-
|
|
30
|
+
codeBlockAndSpanRanges = null;
|
|
23
31
|
flattenedLists = null;
|
|
32
|
+
lineMetadata = null;
|
|
24
33
|
};
|