markdownlint 0.26.1 → 0.27.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 +81 -59
- package/README.md +424 -301
- package/demo/markdownlint-browser.js +445 -210
- package/doc/CustomRules.md +101 -50
- package/doc/Prettier.md +14 -10
- package/doc/ReleaseProcess.md +20 -0
- package/doc/Rules.md +464 -296
- package/doc/md001.md +37 -0
- package/doc/md002.md +37 -0
- package/doc/md003.md +59 -0
- package/doc/md004.md +49 -0
- package/doc/md005.md +53 -0
- package/doc/md006.md +46 -0
- package/doc/md007.md +55 -0
- package/doc/md009.md +51 -0
- package/doc/md010.md +56 -0
- package/doc/md011.md +30 -0
- package/doc/md012.md +38 -0
- package/doc/md013.md +60 -0
- package/doc/md014.md +54 -0
- package/doc/md018.md +27 -0
- package/doc/md019.md +28 -0
- package/doc/md020.md +29 -0
- package/doc/md021.md +31 -0
- package/doc/md022.md +46 -0
- package/doc/md023.md +26 -0
- package/doc/md024.md +47 -0
- package/doc/md025.md +49 -0
- package/doc/md026.md +41 -0
- package/doc/md027.md +24 -0
- package/doc/md028.md +40 -0
- package/doc/md029.md +98 -0
- package/doc/md030.md +82 -0
- package/doc/md031.md +50 -0
- package/doc/md032.md +38 -0
- package/doc/md033.md +27 -0
- package/doc/md034.md +44 -0
- package/doc/md035.md +42 -0
- package/doc/md036.md +45 -0
- package/doc/md037.md +37 -0
- package/doc/md038.md +40 -0
- package/doc/md039.md +21 -0
- package/doc/md040.md +52 -0
- package/doc/md041.md +49 -0
- package/doc/md042.md +32 -0
- package/doc/md043.md +72 -0
- package/doc/md044.md +35 -0
- package/doc/md045.md +34 -0
- package/doc/md046.md +40 -0
- package/doc/md047.md +34 -0
- package/doc/md048.md +41 -0
- package/doc/md049.md +32 -0
- package/doc/md050.md +32 -0
- package/doc/md051.md +50 -0
- package/doc/md052.md +33 -0
- package/doc/md053.md +38 -0
- package/helpers/README.md +35 -15
- package/helpers/helpers.js +234 -70
- package/helpers/package.json +2 -2
- package/lib/constants.js +8 -1
- package/lib/markdownlint.js +7 -12
- package/lib/md011.js +2 -2
- package/lib/md013.js +7 -6
- package/lib/md019.js +1 -1
- package/lib/md022.js +15 -3
- package/lib/md023.js +1 -1
- package/lib/md026.js +1 -1
- package/lib/md032.js +4 -5
- package/lib/md033.js +16 -5
- package/lib/md034.js +70 -45
- package/lib/md035.js +1 -1
- package/lib/md037.js +25 -22
- package/lib/md038.js +1 -1
- package/lib/md039.js +1 -1
- package/lib/md040.js +21 -2
- package/lib/md043.js +4 -2
- package/lib/md044.js +4 -3
- package/lib/md051.js +43 -11
- package/lib/md053.js +18 -11
- package/lib/rules.js +1 -1
- package/package.json +31 -21
- package/schema/.markdownlint.jsonc +16 -4
- package/schema/.markdownlint.yaml +13 -3
- package/schema/markdownlint-config-schema.json +45 -5
- package/style/prettier.json +26 -0
package/doc/md051.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# `MD051` - Link fragments should be valid
|
|
2
|
+
|
|
3
|
+
Tags: `links`
|
|
4
|
+
|
|
5
|
+
Aliases: `link-fragments`
|
|
6
|
+
|
|
7
|
+
Fixable: Some violations can be fixed by tooling
|
|
8
|
+
|
|
9
|
+
This rule is triggered when a link fragment does not match any of the fragments
|
|
10
|
+
that are automatically generated for headings in a document:
|
|
11
|
+
|
|
12
|
+
```markdown
|
|
13
|
+
# Title
|
|
14
|
+
|
|
15
|
+
[Link](#fragment)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
To fix this issue, change the link fragment to reference an existing heading:
|
|
19
|
+
|
|
20
|
+
```markdown
|
|
21
|
+
# Title
|
|
22
|
+
|
|
23
|
+
[Link](#title)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Alternatively, an HTML `a` tag with an `id` or a `name` attribute can be used to
|
|
27
|
+
define a fragment:
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+
<a id="bookmark"></a>
|
|
31
|
+
|
|
32
|
+
[Link](#bookmark)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
An `a` tag can be useful in scenarios where a heading is not appropriate or for
|
|
36
|
+
control over the text of the fragment identifier.
|
|
37
|
+
|
|
38
|
+
Rationale: [GitHub section links][github-section-links] are created
|
|
39
|
+
automatically for every heading when Markdown content is displayed on GitHub.
|
|
40
|
+
This makes it easy to link directly to different sections within a document.
|
|
41
|
+
However, section links change if headings are renamed or removed. This rule
|
|
42
|
+
helps identify broken section links within a document.
|
|
43
|
+
|
|
44
|
+
Section links are **not** part of the CommonMark specification. This rule
|
|
45
|
+
enforces the [GitHub heading algorithm][github-heading-algorithm] which is:
|
|
46
|
+
convert heading to lowercase, remove punctuation, convert spaces to dashes,
|
|
47
|
+
append an incrementing integer as needed for uniqueness.
|
|
48
|
+
|
|
49
|
+
[github-section-links]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links
|
|
50
|
+
[github-heading-algorithm]: https://github.com/gjtorikian/html-pipeline/blob/main/lib/html/pipeline/toc_filter.rb
|
package/doc/md052.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# `MD052` - Reference links and images should use a label that is defined
|
|
2
|
+
|
|
3
|
+
Tags: `images`, `links`
|
|
4
|
+
|
|
5
|
+
Aliases: `reference-links-images`
|
|
6
|
+
|
|
7
|
+
Links and images in Markdown can provide the link destination or image source
|
|
8
|
+
at the time of use or can define it elsewhere and use a label for reference.
|
|
9
|
+
The reference format is convenient for keeping paragraph text clutter-free
|
|
10
|
+
and makes it easy to reuse the same URL in multiple places.
|
|
11
|
+
|
|
12
|
+
There are three kinds of reference links and images:
|
|
13
|
+
|
|
14
|
+
```markdown
|
|
15
|
+
Full: [text][label]
|
|
16
|
+
Collapsed: [label][]
|
|
17
|
+
Shortcut: [label]
|
|
18
|
+
|
|
19
|
+
Full: ![text][image]
|
|
20
|
+
Collapsed: ![image][]
|
|
21
|
+
Shortcut: ![image]
|
|
22
|
+
|
|
23
|
+
[label]: https://example.com/label
|
|
24
|
+
[image]: https://example.com/image
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A link or image renders correctly when a corresponding label is defined, but
|
|
28
|
+
the text displays with brackets if the label is not present. This rule warns
|
|
29
|
+
of undefined labels for "full" and "collapsed" reference syntax.
|
|
30
|
+
|
|
31
|
+
> "Shortcut" syntax is ambiguous and a missing label will not generate an
|
|
32
|
+
error. For example, `[shortcut]` could be a shortcut link or the text
|
|
33
|
+
"shortcut" in brackets.
|
package/doc/md053.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# `MD053` - Link and image reference definitions should be needed
|
|
2
|
+
|
|
3
|
+
Tags: `images`, `links`
|
|
4
|
+
|
|
5
|
+
Aliases: `link-image-reference-definitions`
|
|
6
|
+
|
|
7
|
+
Parameters:
|
|
8
|
+
|
|
9
|
+
- `ignored_definitions`: Ignored definitions (`string[]`, default `["//"]`)
|
|
10
|
+
|
|
11
|
+
Fixable: Some violations can be fixed by tooling
|
|
12
|
+
|
|
13
|
+
Links and images in Markdown can provide the link destination or image source
|
|
14
|
+
at the time of use or can define it elsewhere and use a label for reference.
|
|
15
|
+
The reference format is convenient for keeping paragraph text clutter-free
|
|
16
|
+
and makes it easy to reuse the same URL in multiple places.
|
|
17
|
+
|
|
18
|
+
Because link and image reference definitions are located separately from
|
|
19
|
+
where they are used, there are two scenarios where a definition can be
|
|
20
|
+
unnecessary:
|
|
21
|
+
|
|
22
|
+
1. If a label is not referenced by any link or image in a document, that
|
|
23
|
+
definition is unused and can be deleted.
|
|
24
|
+
2. If a label is defined multiple times in a document, the first definition is
|
|
25
|
+
used and the others can be deleted.
|
|
26
|
+
|
|
27
|
+
This rule considers a reference definition to be used if any link or image
|
|
28
|
+
reference has the corresponding label. The "full", "collapsed", and "shortcut"
|
|
29
|
+
formats are all supported.
|
|
30
|
+
|
|
31
|
+
If there are reference definitions that are deliberately unreferenced, they can
|
|
32
|
+
be ignored by setting the `ignored_definitions` parameter. The default value of
|
|
33
|
+
this parameter ignores the following convention for adding non-HTML comments to
|
|
34
|
+
Markdown:
|
|
35
|
+
|
|
36
|
+
```markdown
|
|
37
|
+
[//]: # (This behaves like a comment)
|
|
38
|
+
```
|
package/helpers/README.md
CHANGED
|
@@ -4,23 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
The [Markdown]
|
|
8
|
-
[
|
|
9
|
-
[rules]
|
|
10
|
-
|
|
11
|
-
The internal rules share various helper functions; this package exposes those for reuse by custom rules.
|
|
7
|
+
The [Markdown][markdown] linter [`markdownlint`][markdownlint] offers a variety
|
|
8
|
+
of built-in validation [rules][rules] and supports the creation of [custom
|
|
9
|
+
rules][custom-rules]. The internal rules share various helper functions; this
|
|
10
|
+
package exposes those for reuse by custom rules.
|
|
12
11
|
|
|
13
12
|
## API
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
to be public, are not officially supported, and may
|
|
17
|
-
descriptive comments above each
|
|
18
|
-
annotations. That said, some of what's here will
|
|
19
|
-
duplicating code.
|
|
14
|
+
*Undocumented* - This package exports the internal functions as-is. The APIs
|
|
15
|
+
were not originally meant to be public, are not officially supported, and may
|
|
16
|
+
change from release to release. There are brief descriptive comments above each
|
|
17
|
+
function, but no [JSDoc][jsdoc] annotations. That said, some of what's here will
|
|
18
|
+
be useful to custom rule authors and may avoid duplicating code.
|
|
20
19
|
|
|
21
|
-
##
|
|
20
|
+
## Examples
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
### Using Helpers from a Custom Rule
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
24
25
|
const { forEachLine, getLineMetadata } = require("markdownlint-rule-helpers");
|
|
25
26
|
|
|
26
27
|
module.exports = {
|
|
@@ -42,9 +43,28 @@ module.exports = {
|
|
|
42
43
|
};
|
|
43
44
|
```
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
### Applying Recommended Fixes
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const { "sync": markdownlintSync } = require("markdownlint");
|
|
50
|
+
const markdownlintRuleHelpers = require("markdownlint-rule-helpers");
|
|
51
|
+
|
|
52
|
+
function fixMarkdownlintViolations(content) {
|
|
53
|
+
const fixResults = markdownlintSync({ strings: { content } });
|
|
54
|
+
return markdownlintRuleHelpers.applyFixes(content, fixResults.content);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
See also: [`markdownlint` built-in rule implementations][lib].
|
|
46
59
|
|
|
47
60
|
## Tests
|
|
48
61
|
|
|
49
|
-
|
|
50
|
-
so there are no additional tests here.
|
|
62
|
+
*None* - The entire body of code is tested to 100% coverage by the core
|
|
63
|
+
`markdownlint` project, so there are no additional tests here.
|
|
64
|
+
|
|
65
|
+
[custom-rules]: https://github.com/DavidAnson/markdownlint/blob/main/doc/CustomRules.md
|
|
66
|
+
[jsdoc]: https://en.m.wikipedia.org/wiki/JSDoc
|
|
67
|
+
[lib]: https://github.com/DavidAnson/markdownlint/tree/main/lib
|
|
68
|
+
[markdown]: https://en.wikipedia.org/wiki/Markdown
|
|
69
|
+
[markdownlint]: https://github.com/DavidAnson/markdownlint
|
|
70
|
+
[rules]: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
|
package/helpers/helpers.js
CHANGED
|
@@ -10,32 +10,35 @@ module.exports.newLineRe = newLineRe;
|
|
|
10
10
|
// Regular expression for matching common front matter (YAML and TOML)
|
|
11
11
|
module.exports.frontMatterRe =
|
|
12
12
|
// eslint-disable-next-line max-len
|
|
13
|
-
/((^---\s*$[
|
|
13
|
+
/((^---\s*$[\s\S]*?^---\s*)|(^\+\+\+\s*$[\s\S]*?^(\+\+\+|\.\.\.)\s*)|(^\{\s*$[\s\S]*?^\}\s*))(\r\n|\r|\n|$)/m;
|
|
14
14
|
|
|
15
15
|
// Regular expression for matching the start of inline disable/enable comments
|
|
16
16
|
const inlineCommentStartRe =
|
|
17
17
|
// eslint-disable-next-line max-len
|
|
18
|
-
/(<!--\s*markdownlint-(disable|enable|capture|restore|disable-file|enable-file|disable-line|disable-next-line|configure-file))(?:\s|-->)/
|
|
18
|
+
/(<!--\s*markdownlint-(disable|enable|capture|restore|disable-file|enable-file|disable-line|disable-next-line|configure-file))(?:\s|-->)/gi;
|
|
19
19
|
module.exports.inlineCommentStartRe = inlineCommentStartRe;
|
|
20
20
|
|
|
21
21
|
// Regular expression for matching HTML elements
|
|
22
|
-
const htmlElementRe = /<(([A-Za-z][A-Za-
|
|
22
|
+
const htmlElementRe = /<(([A-Za-z][A-Za-z\d-]*)(?:\s[^`>]*)?)\/?>/g;
|
|
23
23
|
module.exports.htmlElementRe = htmlElementRe;
|
|
24
24
|
|
|
25
25
|
// Regular expressions for range matching
|
|
26
|
-
module.exports.bareUrlRe = /(?:http|ftp)s?:\/\/[^\s\]"']*(?:\/|[^\s\]"'\W])/ig;
|
|
27
26
|
module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
|
|
28
27
|
module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
|
|
29
28
|
|
|
30
29
|
// Regular expression for all instances of emphasis markers
|
|
31
30
|
const emphasisMarkersRe = /[_*]/g;
|
|
32
31
|
|
|
33
|
-
// Regular expression for
|
|
32
|
+
// Regular expression for blockquote prefixes
|
|
33
|
+
const blockquotePrefixRe = /^[>\s]*/;
|
|
34
|
+
module.exports.blockquotePrefixRe = blockquotePrefixRe;
|
|
35
|
+
|
|
36
|
+
// Regular expression for reference links (full, collapsed, and shortcut)
|
|
34
37
|
const referenceLinkRe =
|
|
35
|
-
/!?\\?\[((?:\[[^\]\0]
|
|
38
|
+
/!?\\?\[((?:\[[^\]\0]*\]|[^[\]\0])*)\](?:\[([^\]\0]*)\]|([^(])|$)/g;
|
|
36
39
|
|
|
37
40
|
// Regular expression for link reference definitions
|
|
38
|
-
const linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])]:/;
|
|
41
|
+
const linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])\]:/;
|
|
39
42
|
module.exports.linkReferenceDefinitionRe = linkReferenceDefinitionRe;
|
|
40
43
|
|
|
41
44
|
// All punctuation characters (normal and full-width)
|
|
@@ -138,6 +141,12 @@ module.exports.includesSorted = function includesSorted(array, element) {
|
|
|
138
141
|
// https://spec.commonmark.org/0.29/#html-comment
|
|
139
142
|
const htmlCommentBegin = "<!--";
|
|
140
143
|
const htmlCommentEnd = "-->";
|
|
144
|
+
const safeCommentCharacter = ".";
|
|
145
|
+
const startsWithPipeRe = /^ *\|/;
|
|
146
|
+
const notCrLfRe = /[^\r\n]/g;
|
|
147
|
+
const notSpaceCrLfRe = /[^ \r\n]/g;
|
|
148
|
+
const trailingSpaceRe = / +[\r\n]/g;
|
|
149
|
+
const replaceTrailingSpace = (s) => s.replace(notCrLfRe, safeCommentCharacter);
|
|
141
150
|
module.exports.clearHtmlCommentText = function clearHtmlCommentText(text) {
|
|
142
151
|
let i = 0;
|
|
143
152
|
while ((i = text.indexOf(htmlCommentBegin, i)) !== -1) {
|
|
@@ -148,24 +157,30 @@ module.exports.clearHtmlCommentText = function clearHtmlCommentText(text) {
|
|
|
148
157
|
}
|
|
149
158
|
// If the comment has content...
|
|
150
159
|
if (j > i + htmlCommentBegin.length) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
160
|
+
const content = text.slice(i + htmlCommentBegin.length, j);
|
|
161
|
+
const lastLf = text.lastIndexOf("\n", i) + 1;
|
|
162
|
+
const preText = text.slice(lastLf, i);
|
|
163
|
+
const isBlock = preText.trim().length === 0;
|
|
164
|
+
const couldBeTable = startsWithPipeRe.test(preText);
|
|
165
|
+
const spansTableCells = couldBeTable && content.includes("\n");
|
|
166
|
+
const isValid =
|
|
167
|
+
isBlock ||
|
|
168
|
+
!(
|
|
169
|
+
spansTableCells ||
|
|
170
|
+
content.startsWith(">") ||
|
|
171
|
+
content.startsWith("->") ||
|
|
172
|
+
content.endsWith("-") ||
|
|
173
|
+
content.includes("--")
|
|
174
|
+
);
|
|
175
|
+
// If a valid block/inline comment...
|
|
176
|
+
if (isValid) {
|
|
177
|
+
const clearedContent = content
|
|
178
|
+
.replace(notSpaceCrLfRe, safeCommentCharacter)
|
|
179
|
+
.replace(trailingSpaceRe, replaceTrailingSpace);
|
|
180
|
+
text =
|
|
181
|
+
text.slice(0, i + htmlCommentBegin.length) +
|
|
182
|
+
clearedContent +
|
|
183
|
+
text.slice(j);
|
|
169
184
|
}
|
|
170
185
|
}
|
|
171
186
|
i = j + htmlCommentEnd.length;
|
|
@@ -402,17 +417,22 @@ module.exports.flattenLists = function flattenLists(tokens) {
|
|
|
402
417
|
return flattenedLists;
|
|
403
418
|
};
|
|
404
419
|
|
|
405
|
-
|
|
406
|
-
|
|
420
|
+
/**
|
|
421
|
+
* Calls the provided function for each specified inline child token.
|
|
422
|
+
*
|
|
423
|
+
* @param {Object} params RuleParams instance.
|
|
424
|
+
* @param {string} type Token type identifier.
|
|
425
|
+
* @param {Function} handler Callback function.
|
|
426
|
+
* @returns {void}
|
|
427
|
+
*/
|
|
407
428
|
function forEachInlineChild(params, type, handler) {
|
|
408
|
-
filterTokens(params, "inline",
|
|
409
|
-
for (const child of token.children) {
|
|
410
|
-
|
|
411
|
-
handler(child, token);
|
|
412
|
-
}
|
|
429
|
+
filterTokens(params, "inline", (token) => {
|
|
430
|
+
for (const child of token.children.filter((c) => c.type === type)) {
|
|
431
|
+
handler(child, token);
|
|
413
432
|
}
|
|
414
433
|
});
|
|
415
|
-
}
|
|
434
|
+
}
|
|
435
|
+
module.exports.forEachInlineChild = forEachInlineChild;
|
|
416
436
|
|
|
417
437
|
// Calls the provided function for each heading's content
|
|
418
438
|
module.exports.forEachHeading = function forEachHeading(params, handler) {
|
|
@@ -592,6 +612,7 @@ module.exports.codeBlockAndSpanRanges = (params, lineMetadata) => {
|
|
|
592
612
|
*/
|
|
593
613
|
module.exports.htmlElementRanges = (params, lineMetadata) => {
|
|
594
614
|
const exclusions = [];
|
|
615
|
+
// Match with htmlElementRe
|
|
595
616
|
forEachLine(lineMetadata, (line, lineIndex, inCode) => {
|
|
596
617
|
let match = null;
|
|
597
618
|
// eslint-disable-next-line no-unmodified-loop-condition
|
|
@@ -599,6 +620,31 @@ module.exports.htmlElementRanges = (params, lineMetadata) => {
|
|
|
599
620
|
exclusions.push([ lineIndex, match.index, match[0].length ]);
|
|
600
621
|
}
|
|
601
622
|
});
|
|
623
|
+
// Match with html_inline
|
|
624
|
+
forEachInlineChild(params, "html_inline", (token, parent) => {
|
|
625
|
+
const parentContent = parent.content;
|
|
626
|
+
let tokenContent = token.content;
|
|
627
|
+
const parentIndex = parentContent.indexOf(tokenContent);
|
|
628
|
+
let deltaLines = 0;
|
|
629
|
+
let indent = 0;
|
|
630
|
+
for (let i = parentIndex - 1; i >= 0; i--) {
|
|
631
|
+
if (parentContent[i] === "\n") {
|
|
632
|
+
deltaLines++;
|
|
633
|
+
} else if (deltaLines === 0) {
|
|
634
|
+
indent++;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
let lineIndex = token.lineNumber - 1 + deltaLines;
|
|
638
|
+
do {
|
|
639
|
+
const index = tokenContent.indexOf("\n");
|
|
640
|
+
const length = (index === -1) ? tokenContent.length : index;
|
|
641
|
+
exclusions.push([ lineIndex, indent, length ]);
|
|
642
|
+
tokenContent = tokenContent.slice(length + 1);
|
|
643
|
+
lineIndex++;
|
|
644
|
+
indent = 0;
|
|
645
|
+
} while (tokenContent.length > 0);
|
|
646
|
+
});
|
|
647
|
+
// Return results
|
|
602
648
|
return exclusions;
|
|
603
649
|
};
|
|
604
650
|
|
|
@@ -778,14 +824,15 @@ module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
|
|
|
778
824
|
function getReferenceLinkImageData(lineMetadata) {
|
|
779
825
|
// Initialize return values
|
|
780
826
|
const references = new Map();
|
|
781
|
-
const shortcuts = new
|
|
827
|
+
const shortcuts = new Map();
|
|
782
828
|
const definitions = new Map();
|
|
783
829
|
const duplicateDefinitions = [];
|
|
830
|
+
const definitionLineIndices = [];
|
|
784
831
|
// Define helper functions
|
|
785
832
|
const normalizeLabel = (s) => s.toLowerCase().trim().replace(/\s+/g, " ");
|
|
786
833
|
const exclusions = [];
|
|
787
834
|
const excluded = (match) => withinAnyRange(
|
|
788
|
-
exclusions, 0, match.index, match[0].length
|
|
835
|
+
exclusions, 0, match.index, match[0].length - (match[3] || "").length
|
|
789
836
|
);
|
|
790
837
|
// Convert input to single-line so multi-line links/images are easier
|
|
791
838
|
const lineOffsets = [];
|
|
@@ -794,6 +841,7 @@ function getReferenceLinkImageData(lineMetadata) {
|
|
|
794
841
|
forEachLine(lineMetadata, (line, lineIndex, inCode) => {
|
|
795
842
|
lineOffsets[lineIndex] = currentOffset;
|
|
796
843
|
if (!inCode) {
|
|
844
|
+
line = line.replace(blockquotePrefixRe, "");
|
|
797
845
|
if (line.trim().length === 0) {
|
|
798
846
|
// Allow RegExp to detect the end of a block
|
|
799
847
|
line = "\0";
|
|
@@ -819,7 +867,13 @@ function getReferenceLinkImageData(lineMetadata) {
|
|
|
819
867
|
} else {
|
|
820
868
|
definitions.set(label, lineIndex);
|
|
821
869
|
}
|
|
822
|
-
|
|
870
|
+
const labelLength = linkReferenceDefinitionMatch[0].length;
|
|
871
|
+
exclusions.push([ 0, lineOffsets[lineIndex], labelLength ]);
|
|
872
|
+
const hasDefinition = line.slice(labelLength).trim().length > 0;
|
|
873
|
+
definitionLineIndices.push(lineIndex);
|
|
874
|
+
if (!hasDefinition) {
|
|
875
|
+
definitionLineIndices.push(lineIndex + 1);
|
|
876
|
+
}
|
|
823
877
|
}
|
|
824
878
|
}
|
|
825
879
|
});
|
|
@@ -845,8 +899,7 @@ function getReferenceLinkImageData(lineMetadata) {
|
|
|
845
899
|
!matchString.startsWith("!\\") &&
|
|
846
900
|
!matchText.endsWith("\\") &&
|
|
847
901
|
!(matchLabel || "").endsWith("\\") &&
|
|
848
|
-
(topLevel
|
|
849
|
-
!excluded(referenceLinkMatch)
|
|
902
|
+
!(topLevel && excluded(referenceLinkMatch))
|
|
850
903
|
) {
|
|
851
904
|
const shortcutLink = (matchLabel === undefined);
|
|
852
905
|
const collapsedLink =
|
|
@@ -855,41 +908,44 @@ function getReferenceLinkImageData(lineMetadata) {
|
|
|
855
908
|
(shortcutLink || collapsedLink) ? matchText : matchLabel
|
|
856
909
|
);
|
|
857
910
|
if (label.length > 0) {
|
|
911
|
+
const referenceindex = referenceLinkMatch.index;
|
|
912
|
+
if (topLevel) {
|
|
913
|
+
// Calculate line index
|
|
914
|
+
while (lineOffsets[lineIndex + 1] <= referenceindex) {
|
|
915
|
+
lineIndex++;
|
|
916
|
+
}
|
|
917
|
+
} else {
|
|
918
|
+
// Use provided line index
|
|
919
|
+
lineIndex = contentLineIndex;
|
|
920
|
+
}
|
|
921
|
+
const referenceIndex = referenceindex +
|
|
922
|
+
(topLevel ? -lineOffsets[lineIndex] : contentIndex);
|
|
923
|
+
const referenceDatum = [
|
|
924
|
+
lineIndex,
|
|
925
|
+
referenceIndex,
|
|
926
|
+
matchString.length,
|
|
927
|
+
matchText.length,
|
|
928
|
+
(matchLabel || "").length
|
|
929
|
+
];
|
|
858
930
|
if (shortcutLink) {
|
|
859
|
-
// Track
|
|
860
|
-
shortcuts.
|
|
931
|
+
// Track separately due to ambiguity in "text [text] text"
|
|
932
|
+
const shortcutData = shortcuts.get(label) || [];
|
|
933
|
+
shortcutData.push(referenceDatum);
|
|
934
|
+
shortcuts.set(label, shortcutData);
|
|
861
935
|
} else {
|
|
862
|
-
const referenceindex = referenceLinkMatch.index;
|
|
863
|
-
if (topLevel) {
|
|
864
|
-
// Calculate line index
|
|
865
|
-
while (lineOffsets[lineIndex + 1] <= referenceindex) {
|
|
866
|
-
lineIndex++;
|
|
867
|
-
}
|
|
868
|
-
} else {
|
|
869
|
-
// Use provided line index
|
|
870
|
-
lineIndex = contentLineIndex;
|
|
871
|
-
}
|
|
872
|
-
const referenceIndex = referenceindex +
|
|
873
|
-
(topLevel ? -lineOffsets[lineIndex] : contentIndex);
|
|
874
936
|
// Track reference and location
|
|
875
937
|
const referenceData = references.get(label) || [];
|
|
876
|
-
referenceData.push(
|
|
877
|
-
lineIndex,
|
|
878
|
-
referenceIndex,
|
|
879
|
-
matchString.length
|
|
880
|
-
]);
|
|
938
|
+
referenceData.push(referenceDatum);
|
|
881
939
|
references.set(label, referenceData);
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
);
|
|
892
|
-
}
|
|
940
|
+
}
|
|
941
|
+
// Check for links embedded in brackets
|
|
942
|
+
if (!matchString.startsWith("!")) {
|
|
943
|
+
pendingContents.push({
|
|
944
|
+
"content": matchText,
|
|
945
|
+
"contentLineIndex": lineIndex,
|
|
946
|
+
"contentIndex": referenceIndex + 1,
|
|
947
|
+
"topLevel": false
|
|
948
|
+
});
|
|
893
949
|
}
|
|
894
950
|
}
|
|
895
951
|
}
|
|
@@ -899,7 +955,8 @@ function getReferenceLinkImageData(lineMetadata) {
|
|
|
899
955
|
references,
|
|
900
956
|
shortcuts,
|
|
901
957
|
definitions,
|
|
902
|
-
duplicateDefinitions
|
|
958
|
+
duplicateDefinitions,
|
|
959
|
+
definitionLineIndices
|
|
903
960
|
};
|
|
904
961
|
}
|
|
905
962
|
module.exports.getReferenceLinkImageData = getReferenceLinkImageData;
|
|
@@ -987,7 +1044,7 @@ module.exports.applyFix = applyFix;
|
|
|
987
1044
|
* @returns {string} Corrected content.
|
|
988
1045
|
*/
|
|
989
1046
|
function applyFixes(input, errors) {
|
|
990
|
-
const lineEnding = getPreferredLineEnding(input, require("os"));
|
|
1047
|
+
const lineEnding = getPreferredLineEnding(input, require("node:os"));
|
|
991
1048
|
const lines = input.split(newLineRe);
|
|
992
1049
|
// Normalize fixInfo objects
|
|
993
1050
|
let fixInfos = errors
|
|
@@ -1130,3 +1187,110 @@ function expandTildePath(file, os) {
|
|
|
1130
1187
|
return homedir ? file.replace(/^~($|\/|\\)/, `${homedir}$1`) : file;
|
|
1131
1188
|
}
|
|
1132
1189
|
module.exports.expandTildePath = expandTildePath;
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* RegExp.exec-style implementation of function expressions.
|
|
1193
|
+
*
|
|
1194
|
+
* @param {Function} funcExp Function that takes string and returns
|
|
1195
|
+
* [index, length] or null.
|
|
1196
|
+
* @param {string} input String to search.
|
|
1197
|
+
* @returns {string[] | null} RegExp.exec-style [match] with an index property.
|
|
1198
|
+
*/
|
|
1199
|
+
function funcExpExec(funcExp, input) {
|
|
1200
|
+
// Start or resume match
|
|
1201
|
+
// @ts-ignore
|
|
1202
|
+
const lastIndex = funcExp.lastIndex || 0;
|
|
1203
|
+
const result = funcExp(input.slice(lastIndex));
|
|
1204
|
+
if (result) {
|
|
1205
|
+
// Update lastIndex and return match
|
|
1206
|
+
const [ subIndex, length ] = result;
|
|
1207
|
+
const index = lastIndex + subIndex;
|
|
1208
|
+
// @ts-ignore
|
|
1209
|
+
funcExp.lastIndex = index + length;
|
|
1210
|
+
const match = [ input.slice(index, index + length) ];
|
|
1211
|
+
// @ts-ignore
|
|
1212
|
+
match.index = index;
|
|
1213
|
+
return match;
|
|
1214
|
+
}
|
|
1215
|
+
// Reset lastIndex and return no match
|
|
1216
|
+
// @ts-ignore
|
|
1217
|
+
funcExp.lastIndex = 0;
|
|
1218
|
+
return null;
|
|
1219
|
+
}
|
|
1220
|
+
module.exports.funcExpExec = funcExpExec;
|
|
1221
|
+
|
|
1222
|
+
const urlFeProtocolRe = /(?:http|ftp)s?:\/\//i;
|
|
1223
|
+
const urlFeAutolinkTerminalsRe = / |$/;
|
|
1224
|
+
const urlFeBareTerminalsRe = /[ ,!`'"\]]|$/;
|
|
1225
|
+
const urlFeNonTerminalsRe = "-#/";
|
|
1226
|
+
const urlFePunctuationRe = /\p{Punctuation}/u;
|
|
1227
|
+
const urlFePrefixToPostfix = new Map([
|
|
1228
|
+
[ " ", " " ],
|
|
1229
|
+
[ "`", "`" ],
|
|
1230
|
+
[ "'", "'" ],
|
|
1231
|
+
[ "\"", "\"" ],
|
|
1232
|
+
[ "‘", "’" ],
|
|
1233
|
+
[ "“", "”" ],
|
|
1234
|
+
[ "«", "»" ],
|
|
1235
|
+
[ "*", "*" ],
|
|
1236
|
+
[ "_", "_" ],
|
|
1237
|
+
[ "(", ")" ],
|
|
1238
|
+
[ "[", "]" ],
|
|
1239
|
+
[ "{", "}" ],
|
|
1240
|
+
[ "<", ">" ],
|
|
1241
|
+
[ ">", "<" ]
|
|
1242
|
+
]);
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* Function expression that matches URLs.
|
|
1246
|
+
*
|
|
1247
|
+
* @param {string} input Substring to search for a URL.
|
|
1248
|
+
* @returns {Array | null} [index, length] of URL or null.
|
|
1249
|
+
*/
|
|
1250
|
+
function urlFe(input) {
|
|
1251
|
+
// Find start of URL by searching for protocol
|
|
1252
|
+
const match = input.match(urlFeProtocolRe);
|
|
1253
|
+
if (match) {
|
|
1254
|
+
// Look for matching pre/postfix characters (ex: <...>)
|
|
1255
|
+
const start = match.index || 0;
|
|
1256
|
+
const length = match[0].length;
|
|
1257
|
+
const prefix = input[start - 1] || " ";
|
|
1258
|
+
const postfix = urlFePrefixToPostfix.get(prefix);
|
|
1259
|
+
// @ts-ignore
|
|
1260
|
+
let endPostfix = input.indexOf(postfix, start + length);
|
|
1261
|
+
if (endPostfix === -1) {
|
|
1262
|
+
endPostfix = input.length;
|
|
1263
|
+
}
|
|
1264
|
+
// Look for characters that terminate a URL
|
|
1265
|
+
const terminalsRe =
|
|
1266
|
+
(prefix === "<") ? urlFeAutolinkTerminalsRe : urlFeBareTerminalsRe;
|
|
1267
|
+
const endTerminal = start + input.slice(start).search(terminalsRe);
|
|
1268
|
+
// Determine tentative end of URL
|
|
1269
|
+
let end = Math.min(endPostfix, endTerminal);
|
|
1270
|
+
if (prefix === " ") {
|
|
1271
|
+
// If the URL used " " as pre/postfix characters, trim the end
|
|
1272
|
+
if (input[end - 1] === ")") {
|
|
1273
|
+
// Trim any ")" beyond the last "(...)" pair
|
|
1274
|
+
const lastOpenParen = input.lastIndexOf("(", end - 2);
|
|
1275
|
+
if (lastOpenParen <= start) {
|
|
1276
|
+
end--;
|
|
1277
|
+
} else {
|
|
1278
|
+
const nextCloseParen = input.indexOf(")", lastOpenParen + 1);
|
|
1279
|
+
end = nextCloseParen + 1;
|
|
1280
|
+
}
|
|
1281
|
+
} else {
|
|
1282
|
+
// Trim unwanted punctuation
|
|
1283
|
+
while (
|
|
1284
|
+
!urlFeNonTerminalsRe.includes(input[end - 1]) &&
|
|
1285
|
+
urlFePunctuationRe.test(input[end - 1])
|
|
1286
|
+
) {
|
|
1287
|
+
end--;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
return [ start, end - start ];
|
|
1292
|
+
}
|
|
1293
|
+
// No match
|
|
1294
|
+
return null;
|
|
1295
|
+
}
|
|
1296
|
+
module.exports.urlFe = urlFe;
|
package/helpers/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "markdownlint-rule-helpers",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "A collection of markdownlint helper functions for custom rules",
|
|
5
5
|
"main": "./helpers.js",
|
|
6
6
|
"exports": "./helpers.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"bugs": "https://github.com/DavidAnson/markdownlint/issues",
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
16
|
+
"node": ">=14.18.0"
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
19
|
"markdownlint",
|
package/lib/constants.js
CHANGED
|
@@ -3,5 +3,12 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
module.exports.deprecatedRuleNames = [ "MD002", "MD006" ];
|
|
6
|
+
module.exports.fixableRuleNames = [
|
|
7
|
+
"MD004", "MD005", "MD006", "MD007", "MD009", "MD010",
|
|
8
|
+
"MD011", "MD012", "MD014", "MD018", "MD019", "MD020",
|
|
9
|
+
"MD021", "MD022", "MD023", "MD026", "MD027", "MD030",
|
|
10
|
+
"MD031", "MD032", "MD034", "MD037", "MD038", "MD039",
|
|
11
|
+
"MD044", "MD047", "MD049", "MD050", "MD051", "MD053"
|
|
12
|
+
];
|
|
6
13
|
module.exports.homepage = "https://github.com/DavidAnson/markdownlint";
|
|
7
|
-
module.exports.version = "0.
|
|
14
|
+
module.exports.version = "0.27.0";
|