markdownlint 0.28.2 → 0.30.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/CHANGELOG.md +17 -1
- package/CONTRIBUTING.md +6 -0
- package/README.md +8 -1
- package/demo/markdownlint-browser.js +896 -836
- package/doc/CustomRules.md +2 -0
- package/doc/Rules.md +33 -17
- package/doc/md022.md +12 -6
- package/doc/md026.md +1 -2
- package/doc/md049.md +2 -2
- package/doc/md050.md +2 -2
- package/doc/md051.md +17 -6
- package/helpers/helpers.js +83 -290
- package/helpers/micromark.cjs +163 -21
- package/helpers/package.json +2 -2
- package/helpers/shared.js +7 -0
- package/lib/cache.js +0 -2
- package/lib/constants.js +1 -1
- package/lib/markdownlint.d.ts +4 -0
- package/lib/markdownlint.js +21 -7
- package/lib/md022.js +80 -40
- package/lib/md026.js +19 -12
- package/lib/md032.js +57 -31
- package/lib/md033.js +19 -45
- package/lib/md034.js +30 -23
- package/lib/md037.js +74 -157
- package/lib/md038.js +1 -2
- package/lib/md044.js +34 -4
- package/lib/md045.js +17 -5
- package/lib/md049-md050.js +61 -48
- package/lib/md051.js +90 -68
- package/package.json +31 -29
- package/schema/.markdownlint.jsonc +3 -3
- package/schema/.markdownlint.yaml +3 -3
- package/schema/markdownlint-config-schema.json +19 -7
package/doc/CustomRules.md
CHANGED
|
@@ -82,6 +82,8 @@ properties:
|
|
|
82
82
|
error.
|
|
83
83
|
- `context` is an optional `String` with relevant text surrounding the error
|
|
84
84
|
location.
|
|
85
|
+
- `information` is an optional (absolute) `URL` of a link to override the
|
|
86
|
+
same-named value provided by the rule definition. (Uncommon)
|
|
85
87
|
- `range` is an optional `Array` with two `Number` values identifying the
|
|
86
88
|
1-based column and length of the error.
|
|
87
89
|
- `fixInfo` is an optional `Object` with information about how to fix the
|
package/doc/Rules.md
CHANGED
|
@@ -806,8 +806,8 @@ Aliases: `blanks-around-headers`, `blanks-around-headings`
|
|
|
806
806
|
|
|
807
807
|
Parameters:
|
|
808
808
|
|
|
809
|
-
- `lines_above`: Blank lines above heading (`integer`, default `1`)
|
|
810
|
-
- `lines_below`: Blank lines below heading (`integer`, default `1`)
|
|
809
|
+
- `lines_above`: Blank lines above heading (`integer|integer[]`, default `1`)
|
|
810
|
+
- `lines_below`: Blank lines below heading (`integer|integer[]`, default `1`)
|
|
811
811
|
|
|
812
812
|
Fixable: Some violations can be fixed by tooling
|
|
813
813
|
|
|
@@ -836,10 +836,16 @@ Some more text
|
|
|
836
836
|
```
|
|
837
837
|
|
|
838
838
|
The `lines_above` and `lines_below` parameters can be used to specify a
|
|
839
|
-
different number of blank lines (including 0) above or below each heading.
|
|
839
|
+
different number of blank lines (including `0`) above or below each heading.
|
|
840
|
+
If the value `-1` is used for either parameter, any number of blank lines is
|
|
841
|
+
allowed. To customize the number of lines above or below each heading level
|
|
842
|
+
individually, specify a `number[]` where values correspond to heading levels
|
|
843
|
+
1-6 (in order).
|
|
840
844
|
|
|
841
|
-
|
|
842
|
-
blank line, [MD012/no-multiple-blanks](md012.md) should also be customized.
|
|
845
|
+
Notes: If `lines_above` or `lines_below` are configured to require more than one
|
|
846
|
+
blank line, [MD012/no-multiple-blanks](md012.md) should also be customized. This
|
|
847
|
+
rule checks for *at least* as many blank lines as specified; any extra blank
|
|
848
|
+
lines are ignored.
|
|
843
849
|
|
|
844
850
|
Rationale: Aside from aesthetic reasons, some parsers, including `kramdown`,
|
|
845
851
|
will not parse headings that don't have a blank line before, and will parse them
|
|
@@ -986,8 +992,7 @@ Aliases: `no-trailing-punctuation`
|
|
|
986
992
|
|
|
987
993
|
Parameters:
|
|
988
994
|
|
|
989
|
-
- `punctuation`: Punctuation characters
|
|
990
|
-
(`string`, default `.,;:!。,;:!`)
|
|
995
|
+
- `punctuation`: Punctuation characters (`string`, default `.,;:!。,;:!`)
|
|
991
996
|
|
|
992
997
|
Fixable: Some violations can be fixed by tooling
|
|
993
998
|
|
|
@@ -2079,8 +2084,8 @@ Aliases: `emphasis-style`
|
|
|
2079
2084
|
|
|
2080
2085
|
Parameters:
|
|
2081
2086
|
|
|
2082
|
-
- `style`: Emphasis style
|
|
2083
|
-
|
|
2087
|
+
- `style`: Emphasis style (`string`, default `consistent`, values `asterisk` /
|
|
2088
|
+
`consistent` / `underscore`)
|
|
2084
2089
|
|
|
2085
2090
|
Fixable: Some violations can be fixed by tooling
|
|
2086
2091
|
|
|
@@ -2114,8 +2119,8 @@ Aliases: `strong-style`
|
|
|
2114
2119
|
|
|
2115
2120
|
Parameters:
|
|
2116
2121
|
|
|
2117
|
-
- `style`: Strong style
|
|
2118
|
-
|
|
2122
|
+
- `style`: Strong style (`string`, default `consistent`, values `asterisk` /
|
|
2123
|
+
`consistent` / `underscore`)
|
|
2119
2124
|
|
|
2120
2125
|
Fixable: Some violations can be fixed by tooling
|
|
2121
2126
|
|
|
@@ -2153,21 +2158,32 @@ This rule is triggered when a link fragment does not match any of the fragments
|
|
|
2153
2158
|
that are automatically generated for headings in a document:
|
|
2154
2159
|
|
|
2155
2160
|
```markdown
|
|
2156
|
-
#
|
|
2161
|
+
# Heading Name
|
|
2157
2162
|
|
|
2158
2163
|
[Link](#fragment)
|
|
2159
2164
|
```
|
|
2160
2165
|
|
|
2161
|
-
To fix this issue, change the link fragment to reference an existing heading
|
|
2166
|
+
To fix this issue, change the link fragment to reference an existing heading's
|
|
2167
|
+
generated name (see below):
|
|
2162
2168
|
|
|
2163
2169
|
```markdown
|
|
2164
|
-
#
|
|
2170
|
+
# Heading Name
|
|
2171
|
+
|
|
2172
|
+
[Link](#heading-name)
|
|
2173
|
+
```
|
|
2174
|
+
|
|
2175
|
+
Alternatively, some platforms allow the syntax `{#named-anchor}` to be used
|
|
2176
|
+
within a heading to provide a specific name (consisting of only lower-case
|
|
2177
|
+
letters, numbers, `-`, and `_`):
|
|
2178
|
+
|
|
2179
|
+
```markdown
|
|
2180
|
+
# Heading Name {#custom-name}
|
|
2165
2181
|
|
|
2166
|
-
[Link](#
|
|
2182
|
+
[Link](#custom-name)
|
|
2167
2183
|
```
|
|
2168
2184
|
|
|
2169
|
-
Alternatively,
|
|
2170
|
-
define a fragment:
|
|
2185
|
+
Alternatively, any HTML tag with an `id` attribute or an `a` tag with a `name`
|
|
2186
|
+
attribute can be used to define a fragment:
|
|
2171
2187
|
|
|
2172
2188
|
```markdown
|
|
2173
2189
|
<a id="bookmark"></a>
|
package/doc/md022.md
CHANGED
|
@@ -6,8 +6,8 @@ Aliases: `blanks-around-headers`, `blanks-around-headings`
|
|
|
6
6
|
|
|
7
7
|
Parameters:
|
|
8
8
|
|
|
9
|
-
- `lines_above`: Blank lines above heading (`integer`, default `1`)
|
|
10
|
-
- `lines_below`: Blank lines below heading (`integer`, default `1`)
|
|
9
|
+
- `lines_above`: Blank lines above heading (`integer|integer[]`, default `1`)
|
|
10
|
+
- `lines_below`: Blank lines below heading (`integer|integer[]`, default `1`)
|
|
11
11
|
|
|
12
12
|
Fixable: Some violations can be fixed by tooling
|
|
13
13
|
|
|
@@ -36,10 +36,16 @@ Some more text
|
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
The `lines_above` and `lines_below` parameters can be used to specify a
|
|
39
|
-
different number of blank lines (including 0) above or below each heading.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
different number of blank lines (including `0`) above or below each heading.
|
|
40
|
+
If the value `-1` is used for either parameter, any number of blank lines is
|
|
41
|
+
allowed. To customize the number of lines above or below each heading level
|
|
42
|
+
individually, specify a `number[]` where values correspond to heading levels
|
|
43
|
+
1-6 (in order).
|
|
44
|
+
|
|
45
|
+
Notes: If `lines_above` or `lines_below` are configured to require more than one
|
|
46
|
+
blank line, [MD012/no-multiple-blanks](md012.md) should also be customized. This
|
|
47
|
+
rule checks for *at least* as many blank lines as specified; any extra blank
|
|
48
|
+
lines are ignored.
|
|
43
49
|
|
|
44
50
|
Rationale: Aside from aesthetic reasons, some parsers, including `kramdown`,
|
|
45
51
|
will not parse headings that don't have a blank line before, and will parse them
|
package/doc/md026.md
CHANGED
|
@@ -6,8 +6,7 @@ Aliases: `no-trailing-punctuation`
|
|
|
6
6
|
|
|
7
7
|
Parameters:
|
|
8
8
|
|
|
9
|
-
- `punctuation`: Punctuation characters
|
|
10
|
-
(`string`, default `.,;:!。,;:!`)
|
|
9
|
+
- `punctuation`: Punctuation characters (`string`, default `.,;:!。,;:!`)
|
|
11
10
|
|
|
12
11
|
Fixable: Some violations can be fixed by tooling
|
|
13
12
|
|
package/doc/md049.md
CHANGED
|
@@ -6,8 +6,8 @@ Aliases: `emphasis-style`
|
|
|
6
6
|
|
|
7
7
|
Parameters:
|
|
8
8
|
|
|
9
|
-
- `style`: Emphasis style
|
|
10
|
-
|
|
9
|
+
- `style`: Emphasis style (`string`, default `consistent`, values `asterisk` /
|
|
10
|
+
`consistent` / `underscore`)
|
|
11
11
|
|
|
12
12
|
Fixable: Some violations can be fixed by tooling
|
|
13
13
|
|
package/doc/md050.md
CHANGED
|
@@ -6,8 +6,8 @@ Aliases: `strong-style`
|
|
|
6
6
|
|
|
7
7
|
Parameters:
|
|
8
8
|
|
|
9
|
-
- `style`: Strong style
|
|
10
|
-
|
|
9
|
+
- `style`: Strong style (`string`, default `consistent`, values `asterisk` /
|
|
10
|
+
`consistent` / `underscore`)
|
|
11
11
|
|
|
12
12
|
Fixable: Some violations can be fixed by tooling
|
|
13
13
|
|
package/doc/md051.md
CHANGED
|
@@ -10,21 +10,32 @@ This rule is triggered when a link fragment does not match any of the fragments
|
|
|
10
10
|
that are automatically generated for headings in a document:
|
|
11
11
|
|
|
12
12
|
```markdown
|
|
13
|
-
#
|
|
13
|
+
# Heading Name
|
|
14
14
|
|
|
15
15
|
[Link](#fragment)
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
To fix this issue, change the link fragment to reference an existing heading
|
|
18
|
+
To fix this issue, change the link fragment to reference an existing heading's
|
|
19
|
+
generated name (see below):
|
|
19
20
|
|
|
20
21
|
```markdown
|
|
21
|
-
#
|
|
22
|
+
# Heading Name
|
|
22
23
|
|
|
23
|
-
[Link](#
|
|
24
|
+
[Link](#heading-name)
|
|
24
25
|
```
|
|
25
26
|
|
|
26
|
-
Alternatively,
|
|
27
|
-
|
|
27
|
+
Alternatively, some platforms allow the syntax `{#named-anchor}` to be used
|
|
28
|
+
within a heading to provide a specific name (consisting of only lower-case
|
|
29
|
+
letters, numbers, `-`, and `_`):
|
|
30
|
+
|
|
31
|
+
```markdown
|
|
32
|
+
# Heading Name {#custom-name}
|
|
33
|
+
|
|
34
|
+
[Link](#custom-name)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Alternatively, any HTML tag with an `id` attribute or an `a` tag with a `name`
|
|
38
|
+
attribute can be used to define a fragment:
|
|
28
39
|
|
|
29
40
|
```markdown
|
|
30
41
|
<a id="bookmark"></a>
|
package/helpers/helpers.js
CHANGED
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const micromark = require("./micromark.cjs");
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
@@ -281,25 +339,10 @@ function filterTokens(params, type, handler) {
|
|
|
281
339
|
}
|
|
282
340
|
module.exports.filterTokens = filterTokens;
|
|
283
341
|
|
|
284
|
-
/**
|
|
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.
|
|
289
|
-
*/
|
|
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
|
-
|
|
299
342
|
// Get line metadata array
|
|
300
343
|
module.exports.getLineMetadata = function getLineMetadata(params) {
|
|
301
344
|
const lineMetadata = params.lines.map(
|
|
302
|
-
(line, index) => [ line, index, false, 0, false, false, false
|
|
345
|
+
(line, index) => [ line, index, false, 0, false, false, false ]
|
|
303
346
|
);
|
|
304
347
|
filterTokens(params, "fence", (token) => {
|
|
305
348
|
lineMetadata[token.map[0]][3] = 1;
|
|
@@ -328,11 +371,6 @@ module.exports.getLineMetadata = function getLineMetadata(params) {
|
|
|
328
371
|
filterTokens(params, "hr", (token) => {
|
|
329
372
|
lineMetadata[token.map[0]][6] = true;
|
|
330
373
|
});
|
|
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
374
|
return lineMetadata;
|
|
337
375
|
};
|
|
338
376
|
|
|
@@ -341,7 +379,7 @@ module.exports.getLineMetadata = function getLineMetadata(params) {
|
|
|
341
379
|
*
|
|
342
380
|
* @param {Object} lineMetadata Line metadata object.
|
|
343
381
|
* @param {Function} handler Function taking (line, lineIndex, inCode, onFence,
|
|
344
|
-
* inTable, inItem, inBreak
|
|
382
|
+
* inTable, inItem, inBreak).
|
|
345
383
|
* @returns {void}
|
|
346
384
|
*/
|
|
347
385
|
function forEachLine(lineMetadata, handler) {
|
|
@@ -402,23 +440,6 @@ module.exports.flattenLists = function flattenLists(tokens) {
|
|
|
402
440
|
return flattenedLists;
|
|
403
441
|
};
|
|
404
442
|
|
|
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
443
|
// Calls the provided function for each heading's content
|
|
423
444
|
module.exports.forEachHeading = function forEachHeading(params, handler) {
|
|
424
445
|
let heading = null;
|
|
@@ -588,51 +609,6 @@ module.exports.codeBlockAndSpanRanges = (params, lineMetadata) => {
|
|
|
588
609
|
return exclusions;
|
|
589
610
|
};
|
|
590
611
|
|
|
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
612
|
/**
|
|
637
613
|
* Determines whether the specified range is within another range.
|
|
638
614
|
*
|
|
@@ -677,129 +653,6 @@ module.exports.frontMatterHasTitle =
|
|
|
677
653
|
frontMatterLines.some((line) => frontMatterTitleRe.test(line));
|
|
678
654
|
};
|
|
679
655
|
|
|
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
656
|
/**
|
|
804
657
|
* Returns an object with information about reference links and images.
|
|
805
658
|
*
|
|
@@ -1080,66 +933,6 @@ function applyFixes(input, errors) {
|
|
|
1080
933
|
}
|
|
1081
934
|
module.exports.applyFixes = applyFixes;
|
|
1082
935
|
|
|
1083
|
-
/**
|
|
1084
|
-
* Gets the range and fixInfo values for reporting an error if the expected
|
|
1085
|
-
* text is found on the specified line.
|
|
1086
|
-
*
|
|
1087
|
-
* @param {string[]} lines Lines of Markdown content.
|
|
1088
|
-
* @param {number} lineIndex Line index to check.
|
|
1089
|
-
* @param {string} search Text to search for.
|
|
1090
|
-
* @param {string} replace Text to replace with.
|
|
1091
|
-
* @param {number} [instance] Instance on the line (1-based).
|
|
1092
|
-
* @returns {Object} Range and fixInfo wrapper.
|
|
1093
|
-
*/
|
|
1094
|
-
module.exports.getRangeAndFixInfoIfFound =
|
|
1095
|
-
(lines, lineIndex, search, replace, instance = 1) => {
|
|
1096
|
-
let range = null;
|
|
1097
|
-
let fixInfo = null;
|
|
1098
|
-
let searchIndex = -1;
|
|
1099
|
-
while (instance > 0) {
|
|
1100
|
-
searchIndex = lines[lineIndex].indexOf(search, searchIndex + 1);
|
|
1101
|
-
instance--;
|
|
1102
|
-
}
|
|
1103
|
-
if (searchIndex !== -1) {
|
|
1104
|
-
const column = searchIndex + 1;
|
|
1105
|
-
const length = search.length;
|
|
1106
|
-
range = [ column, length ];
|
|
1107
|
-
fixInfo = {
|
|
1108
|
-
"editColumn": column,
|
|
1109
|
-
"deleteCount": length,
|
|
1110
|
-
"insertText": replace
|
|
1111
|
-
};
|
|
1112
|
-
}
|
|
1113
|
-
return {
|
|
1114
|
-
range,
|
|
1115
|
-
fixInfo
|
|
1116
|
-
};
|
|
1117
|
-
};
|
|
1118
|
-
|
|
1119
|
-
/**
|
|
1120
|
-
* Gets the next (subsequent) child token if it is of the expected type.
|
|
1121
|
-
*
|
|
1122
|
-
* @param {Object} parentToken Parent token.
|
|
1123
|
-
* @param {Object} childToken Child token basis.
|
|
1124
|
-
* @param {string} nextType Token type of next token.
|
|
1125
|
-
* @param {string} nextNextType Token type of next-next token.
|
|
1126
|
-
* @returns {Object} Next token.
|
|
1127
|
-
*/
|
|
1128
|
-
function getNextChildToken(parentToken, childToken, nextType, nextNextType) {
|
|
1129
|
-
const { children } = parentToken;
|
|
1130
|
-
const index = children.indexOf(childToken);
|
|
1131
|
-
if (
|
|
1132
|
-
(index !== -1) &&
|
|
1133
|
-
(children.length > index + 2) &&
|
|
1134
|
-
(children[index + 1].type === nextType) &&
|
|
1135
|
-
(children[index + 2].type === nextNextType)
|
|
1136
|
-
) {
|
|
1137
|
-
return children[index + 1];
|
|
1138
|
-
}
|
|
1139
|
-
return null;
|
|
1140
|
-
}
|
|
1141
|
-
module.exports.getNextChildToken = getNextChildToken;
|
|
1142
|
-
|
|
1143
936
|
/**
|
|
1144
937
|
* Expands a path with a tilde to an absolute path.
|
|
1145
938
|
*
|