markdownlint 0.21.1 → 0.24.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 +1 -0
- package/LICENSE +1 -1
- package/README.md +140 -80
- package/demo/markdownlint-browser.js +1209 -1701
- package/doc/Prettier.md +23 -0
- package/doc/Rules.md +121 -76
- package/helpers/LICENSE +1 -1
- package/helpers/helpers.js +126 -37
- package/helpers/package.json +1 -1
- package/lib/cache.js +18 -9
- package/lib/markdownlint.d.ts +28 -12
- package/lib/markdownlint.js +185 -62
- package/lib/md002.js +1 -1
- package/lib/md004.js +46 -13
- package/lib/md006.js +1 -1
- package/lib/md009.js +25 -23
- package/lib/md010.js +5 -1
- package/lib/md011.js +31 -27
- package/lib/md018.js +1 -1
- package/lib/md019.js +1 -1
- package/lib/md020.js +1 -1
- package/lib/md021.js +1 -1
- package/lib/md023.js +1 -1
- package/lib/md025.js +1 -1
- package/lib/md026.js +7 -4
- package/lib/md028.js +1 -9
- package/lib/md032.js +2 -2
- package/lib/md037.js +35 -9
- package/lib/md039.js +2 -2
- package/lib/md041.js +14 -3
- package/lib/md043.js +27 -11
- package/lib/md044.js +54 -71
- package/lib/rules.js +2 -1
- package/package.json +39 -32
- package/schema/.markdownlint.jsonc +252 -0
- package/schema/.markdownlint.yaml +227 -0
- package/schema/markdownlint-config-schema.json +36 -23
- package/schema/build-config-schema.js +0 -409
package/lib/md011.js
CHANGED
|
@@ -2,40 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addError,
|
|
6
|
-
|
|
5
|
+
const { addError, forEachLine, overlapsAnyRange } = require("../helpers");
|
|
6
|
+
const { inlineCodeSpanRanges, lineMetadata } = require("./cache");
|
|
7
7
|
|
|
8
|
-
const reversedLinkRe =
|
|
8
|
+
const reversedLinkRe =
|
|
9
|
+
/(^|[^\\])\(([^)]+)\)\[([^\]^][^\]]*)](?!\()/g;
|
|
9
10
|
|
|
10
11
|
module.exports = {
|
|
11
12
|
"names": [ "MD011", "no-reversed-links" ],
|
|
12
13
|
"description": "Reversed link syntax",
|
|
13
14
|
"tags": [ "links" ],
|
|
14
15
|
"function": function MD011(params, onError) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
16
|
+
const exclusions = inlineCodeSpanRanges();
|
|
17
|
+
forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
|
|
18
|
+
if (!inCode && !onFence) {
|
|
19
|
+
let match = null;
|
|
20
|
+
while ((match = reversedLinkRe.exec(line)) !== null) {
|
|
21
|
+
const [ reversedLink, preChar, linkText, linkDestination ] = match;
|
|
22
|
+
const index = match.index + preChar.length;
|
|
23
|
+
const length = match[0].length - preChar.length;
|
|
24
|
+
if (
|
|
25
|
+
!linkText.endsWith("\\") &&
|
|
26
|
+
!linkDestination.endsWith("\\") &&
|
|
27
|
+
!overlapsAnyRange(exclusions, lineIndex, index, length)
|
|
28
|
+
) {
|
|
29
|
+
addError(
|
|
30
|
+
onError,
|
|
31
|
+
lineIndex + 1,
|
|
32
|
+
reversedLink.slice(preChar.length),
|
|
33
|
+
null,
|
|
34
|
+
[ index + 1, length ],
|
|
35
|
+
{
|
|
36
|
+
"editColumn": index + 1,
|
|
37
|
+
"deleteCount": length,
|
|
38
|
+
"insertText": `[${linkText}](${linkDestination})`
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
39
43
|
}
|
|
40
44
|
});
|
|
41
45
|
}
|
package/lib/md018.js
CHANGED
|
@@ -12,7 +12,7 @@ module.exports = {
|
|
|
12
12
|
"function": function MD018(params, onError) {
|
|
13
13
|
forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
|
|
14
14
|
if (!inCode &&
|
|
15
|
-
/^#+[
|
|
15
|
+
/^#+[^# \t]/.test(line) &&
|
|
16
16
|
!/#\s*$/.test(line) &&
|
|
17
17
|
!line.startsWith("#️⃣")) {
|
|
18
18
|
const hashCount = /^#+/.exec(line)[0].length;
|
package/lib/md019.js
CHANGED
|
@@ -13,7 +13,7 @@ module.exports = {
|
|
|
13
13
|
filterTokens(params, "heading_open", (token) => {
|
|
14
14
|
if (headingStyleFor(token) === "atx") {
|
|
15
15
|
const { line, lineNumber } = token;
|
|
16
|
-
const match = /^(#+)(\
|
|
16
|
+
const match = /^(#+)([ \t]{2,})(?:\S)/.exec(line);
|
|
17
17
|
if (match) {
|
|
18
18
|
const [
|
|
19
19
|
,
|
package/lib/md020.js
CHANGED
|
@@ -13,7 +13,7 @@ module.exports = {
|
|
|
13
13
|
forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
|
|
14
14
|
if (!inCode) {
|
|
15
15
|
const match =
|
|
16
|
-
/^(#+)(\
|
|
16
|
+
/^(#+)([ \t]*)([^#]*?[^#\\])([ \t]*)((?:\\#)?)(#+)(\s*)$/.exec(line);
|
|
17
17
|
if (match) {
|
|
18
18
|
const [
|
|
19
19
|
,
|
package/lib/md021.js
CHANGED
|
@@ -13,7 +13,7 @@ module.exports = {
|
|
|
13
13
|
filterTokens(params, "heading_open", (token) => {
|
|
14
14
|
if (headingStyleFor(token) === "atx_closed") {
|
|
15
15
|
const { line, lineNumber } = token;
|
|
16
|
-
const match = /^(#+)(\
|
|
16
|
+
const match = /^(#+)([ \t]+)([^#]+?)([ \t]+)(#+)(\s*)$/.exec(line);
|
|
17
17
|
if (match) {
|
|
18
18
|
const [
|
|
19
19
|
,
|
package/lib/md023.js
CHANGED
|
@@ -17,7 +17,7 @@ module.exports = {
|
|
|
17
17
|
if (match) {
|
|
18
18
|
const [ prefixAndFirstChar, prefix ] = match;
|
|
19
19
|
let deleteCount = prefix.length;
|
|
20
|
-
const prefixLengthNoSpace = prefix.
|
|
20
|
+
const prefixLengthNoSpace = prefix.trimEnd().length;
|
|
21
21
|
if (prefixLengthNoSpace) {
|
|
22
22
|
deleteCount -= prefixLengthNoSpace - 1;
|
|
23
23
|
}
|
package/lib/md025.js
CHANGED
|
@@ -7,7 +7,7 @@ const { addErrorContext, filterTokens, frontMatterHasTitle } =
|
|
|
7
7
|
|
|
8
8
|
module.exports = {
|
|
9
9
|
"names": [ "MD025", "single-title", "single-h1" ],
|
|
10
|
-
"description": "Multiple top
|
|
10
|
+
"description": "Multiple top-level headings in the same document",
|
|
11
11
|
"tags": [ "headings", "headers" ],
|
|
12
12
|
"function": function MD025(params, onError) {
|
|
13
13
|
const level = Number(params.config.level || 1);
|
package/lib/md026.js
CHANGED
|
@@ -2,24 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addError,
|
|
5
|
+
const { addError, allPunctuationNoQuestion, escapeForRegExp, forEachHeading } =
|
|
6
6
|
require("../helpers");
|
|
7
7
|
|
|
8
|
+
const endOfLineHtmlEntityRe = /&#?[0-9a-zA-Z]+;$/;
|
|
9
|
+
|
|
8
10
|
module.exports = {
|
|
9
11
|
"names": [ "MD026", "no-trailing-punctuation" ],
|
|
10
12
|
"description": "Trailing punctuation in heading",
|
|
11
13
|
"tags": [ "headings", "headers" ],
|
|
12
14
|
"function": function MD026(params, onError) {
|
|
13
15
|
let punctuation = params.config.punctuation;
|
|
14
|
-
punctuation =
|
|
15
|
-
|
|
16
|
+
punctuation = String(
|
|
17
|
+
(punctuation === undefined) ? allPunctuationNoQuestion : punctuation
|
|
18
|
+
);
|
|
16
19
|
const trailingPunctuationRe =
|
|
17
20
|
new RegExp("\\s*[" + escapeForRegExp(punctuation) + "]+$");
|
|
18
21
|
forEachHeading(params, (heading) => {
|
|
19
22
|
const { line, lineNumber } = heading;
|
|
20
23
|
const trimmedLine = line.replace(/[\s#]*$/, "");
|
|
21
24
|
const match = trailingPunctuationRe.exec(trimmedLine);
|
|
22
|
-
if (match) {
|
|
25
|
+
if (match && !endOfLineHtmlEntityRe.test(trimmedLine)) {
|
|
23
26
|
const fullMatch = match[0];
|
|
24
27
|
const column = match.index + 1;
|
|
25
28
|
const length = fullMatch.length;
|
package/lib/md028.js
CHANGED
|
@@ -18,15 +18,7 @@ module.exports = {
|
|
|
18
18
|
let lineNumber = prevLineNumber;
|
|
19
19
|
lineNumber < token.lineNumber;
|
|
20
20
|
lineNumber++) {
|
|
21
|
-
addError(
|
|
22
|
-
onError,
|
|
23
|
-
lineNumber,
|
|
24
|
-
null,
|
|
25
|
-
null,
|
|
26
|
-
null,
|
|
27
|
-
{
|
|
28
|
-
"deleteCount": -1
|
|
29
|
-
});
|
|
21
|
+
addError(onError, lineNumber);
|
|
30
22
|
}
|
|
31
23
|
}
|
|
32
24
|
prevToken = token;
|
package/lib/md032.js
CHANGED
|
@@ -17,7 +17,7 @@ module.exports = {
|
|
|
17
17
|
const firstIndex = list.open.map[0];
|
|
18
18
|
if (!isBlankLine(lines[firstIndex - 1])) {
|
|
19
19
|
const line = lines[firstIndex];
|
|
20
|
-
const quotePrefix = line.match(quotePrefixRe)[0].
|
|
20
|
+
const quotePrefix = line.match(quotePrefixRe)[0].trimEnd();
|
|
21
21
|
addErrorContext(
|
|
22
22
|
onError,
|
|
23
23
|
firstIndex + 1,
|
|
@@ -32,7 +32,7 @@ module.exports = {
|
|
|
32
32
|
const lastIndex = list.lastLineIndex - 1;
|
|
33
33
|
if (!isBlankLine(lines[lastIndex + 1])) {
|
|
34
34
|
const line = lines[lastIndex];
|
|
35
|
-
const quotePrefix = line.match(quotePrefixRe)[0].
|
|
35
|
+
const quotePrefix = line.match(quotePrefixRe)[0].trimEnd();
|
|
36
36
|
addErrorContext(
|
|
37
37
|
onError,
|
|
38
38
|
lastIndex + 1,
|
package/lib/md037.js
CHANGED
|
@@ -10,6 +10,7 @@ const emphasisRe = /(^|[^\\]|\\\\)(?:(\*\*?\*?)|(__?_?))/g;
|
|
|
10
10
|
const asteriskListItemMarkerRe = /^([\s>]*)\*(\s+)/;
|
|
11
11
|
const leftSpaceRe = /^\s+/;
|
|
12
12
|
const rightSpaceRe = /\s+$/;
|
|
13
|
+
const tablePipeRe = /\|/;
|
|
13
14
|
|
|
14
15
|
module.exports = {
|
|
15
16
|
"names": [ "MD037", "no-space-in-emphasis" ],
|
|
@@ -28,18 +29,23 @@ module.exports = {
|
|
|
28
29
|
pendingError = null;
|
|
29
30
|
}
|
|
30
31
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
31
|
-
function handleRunEnd(
|
|
32
|
+
function handleRunEnd(
|
|
33
|
+
line, lineIndex, contextLength, match, matchIndex, inTable
|
|
34
|
+
) {
|
|
32
35
|
// Close current run
|
|
33
36
|
let content = line.substring(emphasisIndex, matchIndex);
|
|
34
37
|
if (!emphasisLength) {
|
|
35
|
-
content = content.
|
|
38
|
+
content = content.trimStart();
|
|
36
39
|
}
|
|
37
40
|
if (!match) {
|
|
38
|
-
content = content.
|
|
41
|
+
content = content.trimEnd();
|
|
39
42
|
}
|
|
40
43
|
const leftSpace = leftSpaceRe.test(content);
|
|
41
44
|
const rightSpace = rightSpaceRe.test(content);
|
|
42
|
-
if (
|
|
45
|
+
if (
|
|
46
|
+
(leftSpace || rightSpace) &&
|
|
47
|
+
(!inTable || !tablePipeRe.test(content))
|
|
48
|
+
) {
|
|
43
49
|
// Report the violation
|
|
44
50
|
const contextStart = emphasisIndex - emphasisLength;
|
|
45
51
|
const contextEnd = matchIndex + contextLength;
|
|
@@ -70,13 +76,25 @@ module.exports = {
|
|
|
70
76
|
resetRunTracking();
|
|
71
77
|
forEachLine(
|
|
72
78
|
lineMetadata(),
|
|
73
|
-
(line, lineIndex, inCode, onFence, inTable, inItem, onBreak) => {
|
|
79
|
+
(line, lineIndex, inCode, onFence, inTable, inItem, onBreak, inMath) => {
|
|
74
80
|
const onItemStart = (inItem === 1);
|
|
75
|
-
if (
|
|
81
|
+
if (
|
|
82
|
+
inCode ||
|
|
83
|
+
onFence ||
|
|
84
|
+
inTable ||
|
|
85
|
+
onBreak ||
|
|
86
|
+
onItemStart ||
|
|
87
|
+
isBlankLine(line)
|
|
88
|
+
) {
|
|
76
89
|
// Emphasis resets when leaving a block
|
|
77
90
|
resetRunTracking();
|
|
78
91
|
}
|
|
79
|
-
if (
|
|
92
|
+
if (
|
|
93
|
+
inCode ||
|
|
94
|
+
onFence ||
|
|
95
|
+
onBreak ||
|
|
96
|
+
inMath
|
|
97
|
+
) {
|
|
80
98
|
// Emphasis has no meaning here
|
|
81
99
|
return;
|
|
82
100
|
}
|
|
@@ -106,12 +124,20 @@ module.exports = {
|
|
|
106
124
|
if (matchLength === effectiveEmphasisLength) {
|
|
107
125
|
// Ending an existing run, report any pending error
|
|
108
126
|
if (pendingError) {
|
|
127
|
+
// @ts-ignore
|
|
109
128
|
addErrorContext(...pendingError);
|
|
110
129
|
pendingError = null;
|
|
111
130
|
}
|
|
112
131
|
const error = handleRunEnd(
|
|
113
|
-
line,
|
|
132
|
+
line,
|
|
133
|
+
lineIndex,
|
|
134
|
+
effectiveEmphasisLength,
|
|
135
|
+
match,
|
|
136
|
+
matchIndex,
|
|
137
|
+
inTable
|
|
138
|
+
);
|
|
114
139
|
if (error) {
|
|
140
|
+
// @ts-ignore
|
|
115
141
|
addErrorContext(...error);
|
|
116
142
|
}
|
|
117
143
|
// Reset
|
|
@@ -139,7 +165,7 @@ module.exports = {
|
|
|
139
165
|
}
|
|
140
166
|
if (emphasisIndex !== -1) {
|
|
141
167
|
pendingError = pendingError ||
|
|
142
|
-
handleRunEnd(line, lineIndex, 0, null, line.length);
|
|
168
|
+
handleRunEnd(line, lineIndex, 0, null, line.length, inTable);
|
|
143
169
|
// Adjust for pending run on new line
|
|
144
170
|
emphasisIndex = 0;
|
|
145
171
|
emphasisLength = 0;
|
package/lib/md039.js
CHANGED
|
@@ -24,8 +24,8 @@ module.exports = {
|
|
|
24
24
|
linkText = "";
|
|
25
25
|
} else if (type === "link_close") {
|
|
26
26
|
inLink = false;
|
|
27
|
-
const left = linkText.
|
|
28
|
-
const right = linkText.
|
|
27
|
+
const left = linkText.trimStart().length !== linkText.length;
|
|
28
|
+
const right = linkText.trimEnd().length !== linkText.length;
|
|
29
29
|
if (left || right) {
|
|
30
30
|
const line = params.lines[lineNumber - 1];
|
|
31
31
|
let range = null;
|
package/lib/md041.js
CHANGED
|
@@ -6,7 +6,7 @@ const { addErrorContext, frontMatterHasTitle } = require("../helpers");
|
|
|
6
6
|
|
|
7
7
|
module.exports = {
|
|
8
8
|
"names": [ "MD041", "first-line-heading", "first-line-h1" ],
|
|
9
|
-
"description": "First line in file should be a top
|
|
9
|
+
"description": "First line in a file should be a top-level heading",
|
|
10
10
|
"tags": [ "headings", "headers" ],
|
|
11
11
|
"function": function MD041(params, onError) {
|
|
12
12
|
const level = Number(params.config.level || 1);
|
|
@@ -17,11 +17,22 @@ module.exports = {
|
|
|
17
17
|
params.config.front_matter_title
|
|
18
18
|
);
|
|
19
19
|
if (!foundFrontMatterTitle) {
|
|
20
|
+
const htmlHeadingRe = new RegExp(`^<h${level}[ />]`, "i");
|
|
20
21
|
params.tokens.every((token) => {
|
|
22
|
+
let isError = false;
|
|
21
23
|
if (token.type === "html_block") {
|
|
22
|
-
|
|
24
|
+
if (token.content.startsWith("<!--")) {
|
|
25
|
+
// Ignore leading HTML comments
|
|
26
|
+
return true;
|
|
27
|
+
} else if (!htmlHeadingRe.test(token.content)) {
|
|
28
|
+
// Something other than an HTML heading
|
|
29
|
+
isError = true;
|
|
30
|
+
}
|
|
31
|
+
} else if ((token.type !== "heading_open") || (token.tag !== tag)) {
|
|
32
|
+
// Something other than a Markdown heading
|
|
33
|
+
isError = true;
|
|
23
34
|
}
|
|
24
|
-
if (
|
|
35
|
+
if (isError) {
|
|
25
36
|
addErrorContext(onError, token.lineNumber, token.line);
|
|
26
37
|
}
|
|
27
38
|
return false;
|
package/lib/md043.js
CHANGED
|
@@ -13,30 +13,46 @@ module.exports = {
|
|
|
13
13
|
const requiredHeadings = params.config.headings || params.config.headers;
|
|
14
14
|
if (Array.isArray(requiredHeadings)) {
|
|
15
15
|
const levels = {};
|
|
16
|
-
[ 1, 2, 3, 4, 5, 6 ].forEach(
|
|
16
|
+
[ 1, 2, 3, 4, 5, 6 ].forEach((level) => {
|
|
17
17
|
levels["h" + level] = "######".substr(-level);
|
|
18
18
|
});
|
|
19
19
|
let i = 0;
|
|
20
|
-
let
|
|
21
|
-
let
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
let matchAny = false;
|
|
21
|
+
let hasError = false;
|
|
22
|
+
let anyHeadings = false;
|
|
23
|
+
// eslint-disable-next-line func-style
|
|
24
|
+
const getExpected = () => requiredHeadings[i++] || "[None]";
|
|
25
|
+
forEachHeading(params, (heading, content) => {
|
|
26
|
+
if (!hasError) {
|
|
27
|
+
anyHeadings = true;
|
|
24
28
|
const actual = levels[heading.tag] + " " + content;
|
|
25
|
-
const expected =
|
|
29
|
+
const expected = getExpected();
|
|
26
30
|
if (expected === "*") {
|
|
27
|
-
|
|
31
|
+
const nextExpected = getExpected();
|
|
32
|
+
if (nextExpected.toLowerCase() !== actual.toLowerCase()) {
|
|
33
|
+
matchAny = true;
|
|
34
|
+
i--;
|
|
35
|
+
}
|
|
36
|
+
} else if (expected === "+") {
|
|
37
|
+
matchAny = true;
|
|
28
38
|
} else if (expected.toLowerCase() === actual.toLowerCase()) {
|
|
29
|
-
|
|
30
|
-
} else if (
|
|
39
|
+
matchAny = false;
|
|
40
|
+
} else if (matchAny) {
|
|
31
41
|
i--;
|
|
32
42
|
} else {
|
|
33
43
|
addErrorDetailIf(onError, heading.lineNumber,
|
|
34
44
|
expected, actual);
|
|
35
|
-
|
|
45
|
+
hasError = true;
|
|
36
46
|
}
|
|
37
47
|
}
|
|
38
48
|
});
|
|
39
|
-
|
|
49
|
+
const extraHeadings = requiredHeadings.length - i;
|
|
50
|
+
if (
|
|
51
|
+
!hasError &&
|
|
52
|
+
((extraHeadings > 1) ||
|
|
53
|
+
((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
|
|
54
|
+
(anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
|
|
55
|
+
) {
|
|
40
56
|
addErrorContext(onError, params.lines.length,
|
|
41
57
|
requiredHeadings[i]);
|
|
42
58
|
}
|
package/lib/md044.js
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf, bareUrlRe, escapeForRegExp,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const startNonWordRe = /^\W/;
|
|
9
|
-
const endNonWordRe = /\W$/;
|
|
5
|
+
const { addErrorDetailIf, bareUrlRe, escapeForRegExp, forEachLine,
|
|
6
|
+
overlapsAnyRange, linkRe, linkReferenceRe } = require("../helpers");
|
|
7
|
+
const { inlineCodeSpanRanges, lineMetadata } = require("./cache");
|
|
10
8
|
|
|
11
9
|
module.exports = {
|
|
12
10
|
"names": [ "MD044", "proper-names" ],
|
|
@@ -15,80 +13,65 @@ module.exports = {
|
|
|
15
13
|
"function": function MD044(params, onError) {
|
|
16
14
|
let names = params.config.names;
|
|
17
15
|
names = Array.isArray(names) ? names : [];
|
|
16
|
+
names.sort((a, b) => (b.length - a.length) || a.localeCompare(b));
|
|
18
17
|
const codeBlocks = params.config.code_blocks;
|
|
19
18
|
const includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
} else if (type === "link_close") {
|
|
29
|
-
inAutoLink = false;
|
|
30
|
-
} else if ((type === "text") && inAutoLink) {
|
|
31
|
-
autolinkText.add(child);
|
|
19
|
+
const exclusions = [];
|
|
20
|
+
forEachLine(lineMetadata(), (line, lineIndex) => {
|
|
21
|
+
if (linkReferenceRe.test(line)) {
|
|
22
|
+
exclusions.push([ lineIndex, 0, line.length ]);
|
|
23
|
+
} else {
|
|
24
|
+
let match = null;
|
|
25
|
+
while ((match = bareUrlRe.exec(line)) !== null) {
|
|
26
|
+
exclusions.push([ lineIndex, match.index, match[0].length ]);
|
|
32
27
|
}
|
|
33
|
-
|
|
28
|
+
while ((match = linkRe.exec(line)) !== null) {
|
|
29
|
+
const [ , text, destination ] = match;
|
|
30
|
+
if (destination) {
|
|
31
|
+
exclusions.push(
|
|
32
|
+
[ lineIndex, match.index + text.length, destination.length ]
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
34
37
|
});
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
if (!includeCodeBlocks) {
|
|
39
|
+
exclusions.push(...inlineCodeSpanRanges());
|
|
40
|
+
}
|
|
41
|
+
for (const name of names) {
|
|
37
42
|
const escapedName = escapeForRegExp(name);
|
|
38
|
-
const startNamePattern =
|
|
39
|
-
const endNamePattern =
|
|
43
|
+
const startNamePattern = /^\W/.test(name) ? "" : "\\b_*";
|
|
44
|
+
const endNamePattern = /\W$/.test(name) ? "" : "_*\\b";
|
|
40
45
|
const namePattern =
|
|
41
|
-
`(${startNamePattern})(${escapedName})
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const fixInfo = (matchIndex === -1) ?
|
|
64
|
-
null :
|
|
65
|
-
{
|
|
66
|
-
"editColumn": matchIndex + 1,
|
|
67
|
-
"deleteCount": matchLength,
|
|
68
|
-
"insertText": name
|
|
69
|
-
};
|
|
70
|
-
addErrorDetailIf(
|
|
71
|
-
onError,
|
|
72
|
-
lineNumber,
|
|
73
|
-
name,
|
|
74
|
-
nameMatch,
|
|
75
|
-
null,
|
|
76
|
-
null,
|
|
77
|
-
range,
|
|
78
|
-
fixInfo
|
|
79
|
-
);
|
|
46
|
+
`(${startNamePattern})(${escapedName})${endNamePattern}`;
|
|
47
|
+
const nameRe = new RegExp(namePattern, "gi");
|
|
48
|
+
forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
|
|
49
|
+
if (includeCodeBlocks || (!inCode && !onFence)) {
|
|
50
|
+
let match = null;
|
|
51
|
+
while ((match = nameRe.exec(line)) !== null) {
|
|
52
|
+
const [ , leftMatch, nameMatch ] = match;
|
|
53
|
+
const index = match.index + leftMatch.length;
|
|
54
|
+
const length = nameMatch.length;
|
|
55
|
+
if (!overlapsAnyRange(exclusions, lineIndex, index, length)) {
|
|
56
|
+
addErrorDetailIf(
|
|
57
|
+
onError,
|
|
58
|
+
lineIndex + 1,
|
|
59
|
+
name,
|
|
60
|
+
nameMatch,
|
|
61
|
+
null,
|
|
62
|
+
null,
|
|
63
|
+
[ index + 1, length ],
|
|
64
|
+
{
|
|
65
|
+
"editColumn": index + 1,
|
|
66
|
+
"deleteCount": length,
|
|
67
|
+
"insertText": name
|
|
80
68
|
}
|
|
81
|
-
|
|
69
|
+
);
|
|
82
70
|
}
|
|
83
|
-
|
|
71
|
+
exclusions.push([ lineIndex, index, length ]);
|
|
72
|
+
}
|
|
84
73
|
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (includeCodeBlocks) {
|
|
88
|
-
forEachInlineChild(params, "code_inline", forToken);
|
|
89
|
-
filterTokens(params, "code_block", forToken);
|
|
90
|
-
filterTokens(params, "fence", forToken);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
93
76
|
}
|
|
94
77
|
};
|
package/lib/rules.js
CHANGED
|
@@ -54,7 +54,8 @@ const rules = [
|
|
|
54
54
|
];
|
|
55
55
|
rules.forEach((rule) => {
|
|
56
56
|
const name = rule.names[0].toLowerCase();
|
|
57
|
-
|
|
57
|
+
// eslint-disable-next-line dot-notation
|
|
58
|
+
rule["information"] =
|
|
58
59
|
new URL(`${homepage}/blob/v${version}/doc/Rules.md#${name}`);
|
|
59
60
|
});
|
|
60
61
|
module.exports = rules;
|