markdownlint 0.22.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +13 -3
- package/LICENSE +1 -1
- package/README.md +140 -83
- package/demo/markdownlint-browser.js +1599 -3309
- package/doc/CustomRules.md +15 -4
- package/doc/Prettier.md +23 -0
- package/doc/Rules.md +191 -80
- package/helpers/LICENSE +1 -1
- package/helpers/helpers.js +260 -68
- package/helpers/package.json +1 -1
- package/lib/cache.js +14 -5
- package/lib/constants.js +7 -0
- package/lib/markdownlint.d.ts +31 -11
- package/lib/markdownlint.js +336 -254
- package/lib/md002.js +1 -1
- package/lib/md004.js +46 -13
- package/lib/md006.js +1 -1
- package/lib/md007.js +4 -2
- package/lib/md009.js +24 -20
- package/lib/md010.js +25 -16
- 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/md032.js +2 -2
- package/lib/md033.js +12 -8
- package/lib/md035.js +4 -4
- package/lib/md037.js +40 -12
- package/lib/md039.js +4 -3
- package/lib/md041.js +14 -3
- package/lib/md043.js +9 -5
- package/lib/md044.js +54 -71
- package/lib/md049.js +45 -0
- package/lib/md050.js +45 -0
- package/lib/rules.js +6 -5
- package/package.json +46 -37
- package/schema/.markdownlint.jsonc +266 -0
- package/schema/.markdownlint.yaml +239 -0
- package/schema/markdownlint-config-schema.json +129 -22
- package/schema/build-config-schema.js +0 -417
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
|
/**
|
|
@@ -99,32 +106,47 @@ module.exports.includesSorted = function includesSorted(array, element) {
|
|
|
99
106
|
return false;
|
|
100
107
|
};
|
|
101
108
|
|
|
102
|
-
// Replaces the
|
|
109
|
+
// Replaces the content of properly-formatted CommonMark comments with "."
|
|
103
110
|
// This preserves the line/column information for the rest of the document
|
|
104
|
-
//
|
|
105
|
-
//
|
|
111
|
+
// https://spec.commonmark.org/0.29/#html-blocks
|
|
112
|
+
// https://spec.commonmark.org/0.29/#html-comment
|
|
106
113
|
const htmlCommentBegin = "<!--";
|
|
107
114
|
const htmlCommentEnd = "-->";
|
|
108
115
|
module.exports.clearHtmlCommentText = function clearHtmlCommentText(text) {
|
|
109
116
|
let i = 0;
|
|
110
117
|
while ((i = text.indexOf(htmlCommentBegin, i)) !== -1) {
|
|
111
|
-
const j = text.indexOf(htmlCommentEnd, i);
|
|
118
|
+
const j = text.indexOf(htmlCommentEnd, i + 2);
|
|
112
119
|
if (j === -1) {
|
|
113
120
|
// Un-terminated comments are treated as text
|
|
114
121
|
break;
|
|
115
122
|
}
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
// If the comment has content...
|
|
124
|
+
if (j > i + htmlCommentBegin.length) {
|
|
125
|
+
let k = i - 1;
|
|
126
|
+
while (text[k] === " ") {
|
|
127
|
+
k--;
|
|
128
|
+
}
|
|
129
|
+
// If comment is not within an indented code block...
|
|
130
|
+
if (k >= i - 4) {
|
|
131
|
+
const content = text.slice(i + htmlCommentBegin.length, j);
|
|
132
|
+
const isBlock = (k < 0) || (text[k] === "\n");
|
|
133
|
+
const isValid = isBlock ||
|
|
134
|
+
(!content.startsWith(">") && !content.startsWith("->") &&
|
|
135
|
+
!content.endsWith("-") && !content.includes("--"));
|
|
136
|
+
// If a valid block/inline comment...
|
|
137
|
+
if (isValid) {
|
|
138
|
+
const inlineCommentIndex = text
|
|
139
|
+
.slice(i, j + htmlCommentEnd.length)
|
|
140
|
+
.search(inlineCommentRe);
|
|
141
|
+
// If not a markdownlint inline directive...
|
|
142
|
+
if (inlineCommentIndex === -1) {
|
|
143
|
+
text =
|
|
144
|
+
text.slice(0, i + htmlCommentBegin.length) +
|
|
145
|
+
content.replace(/[^\r\n]/g, ".") +
|
|
146
|
+
text.slice(j);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
128
150
|
}
|
|
129
151
|
i = j + htmlCommentEnd.length;
|
|
130
152
|
}
|
|
@@ -165,6 +187,22 @@ module.exports.fencedCodeBlockStyleFor =
|
|
|
165
187
|
}
|
|
166
188
|
};
|
|
167
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
|
+
|
|
168
206
|
/**
|
|
169
207
|
* Return the number of characters of indent for a token.
|
|
170
208
|
*
|
|
@@ -173,7 +211,7 @@ module.exports.fencedCodeBlockStyleFor =
|
|
|
173
211
|
*/
|
|
174
212
|
function indentFor(token) {
|
|
175
213
|
const line = token.line.replace(/^[\s>]*(> |>)/, "");
|
|
176
|
-
return line.length - line.
|
|
214
|
+
return line.length - line.trimStart().length;
|
|
177
215
|
}
|
|
178
216
|
module.exports.indentFor = indentFor;
|
|
179
217
|
|
|
@@ -223,66 +261,85 @@ function filterTokens(params, type, handler) {
|
|
|
223
261
|
}
|
|
224
262
|
module.exports.filterTokens = filterTokens;
|
|
225
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Returns whether a token is a math block (created by markdown-it-texmath).
|
|
266
|
+
*
|
|
267
|
+
* @param {Object} token MarkdownItToken instance.
|
|
268
|
+
* @returns {boolean} True iff token is a math block.
|
|
269
|
+
*/
|
|
270
|
+
function isMathBlock(token) {
|
|
271
|
+
return (
|
|
272
|
+
((token.tag === "$$") || (token.tag === "math")) &&
|
|
273
|
+
token.type.startsWith("math_block") &&
|
|
274
|
+
!token.type.endsWith("_end")
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
module.exports.isMathBlock = isMathBlock;
|
|
278
|
+
|
|
226
279
|
// Get line metadata array
|
|
227
280
|
module.exports.getLineMetadata = function getLineMetadata(params) {
|
|
228
|
-
const lineMetadata = params.lines.map(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
filterTokens(params, "fence",
|
|
281
|
+
const lineMetadata = params.lines.map(
|
|
282
|
+
(line, index) => [ line, index, false, 0, false, false, false, false ]
|
|
283
|
+
);
|
|
284
|
+
filterTokens(params, "fence", (token) => {
|
|
232
285
|
lineMetadata[token.map[0]][3] = 1;
|
|
233
286
|
lineMetadata[token.map[1] - 1][3] = -1;
|
|
234
287
|
for (let i = token.map[0] + 1; i < token.map[1] - 1; i++) {
|
|
235
288
|
lineMetadata[i][2] = true;
|
|
236
289
|
}
|
|
237
290
|
});
|
|
238
|
-
filterTokens(params, "code_block",
|
|
291
|
+
filterTokens(params, "code_block", (token) => {
|
|
239
292
|
for (let i = token.map[0]; i < token.map[1]; i++) {
|
|
240
293
|
lineMetadata[i][2] = true;
|
|
241
294
|
}
|
|
242
295
|
});
|
|
243
|
-
filterTokens(params, "table_open",
|
|
296
|
+
filterTokens(params, "table_open", (token) => {
|
|
244
297
|
for (let i = token.map[0]; i < token.map[1]; i++) {
|
|
245
298
|
lineMetadata[i][4] = true;
|
|
246
299
|
}
|
|
247
300
|
});
|
|
248
|
-
filterTokens(params, "list_item_open",
|
|
301
|
+
filterTokens(params, "list_item_open", (token) => {
|
|
249
302
|
let count = 1;
|
|
250
303
|
for (let i = token.map[0]; i < token.map[1]; i++) {
|
|
251
304
|
lineMetadata[i][5] = count;
|
|
252
305
|
count++;
|
|
253
306
|
}
|
|
254
307
|
});
|
|
255
|
-
filterTokens(params, "hr",
|
|
308
|
+
filterTokens(params, "hr", (token) => {
|
|
256
309
|
lineMetadata[token.map[0]][6] = true;
|
|
257
310
|
});
|
|
311
|
+
params.tokens.filter(isMathBlock).forEach((token) => {
|
|
312
|
+
for (let i = token.map[0]; i < token.map[1]; i++) {
|
|
313
|
+
lineMetadata[i][7] = true;
|
|
314
|
+
}
|
|
315
|
+
});
|
|
258
316
|
return lineMetadata;
|
|
259
317
|
};
|
|
260
318
|
|
|
261
|
-
|
|
262
|
-
|
|
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) {
|
|
263
328
|
lineMetadata.forEach(function forMetadata(metadata) {
|
|
264
|
-
// Parameters: line, lineIndex, inCode, onFence, inTable, inItem, inBreak
|
|
265
329
|
handler(...metadata);
|
|
266
330
|
});
|
|
267
|
-
}
|
|
331
|
+
}
|
|
332
|
+
module.exports.forEachLine = forEachLine;
|
|
268
333
|
|
|
269
334
|
// Returns (nested) lists as a flat array (in order)
|
|
270
|
-
module.exports.flattenLists = function flattenLists(
|
|
335
|
+
module.exports.flattenLists = function flattenLists(tokens) {
|
|
271
336
|
const flattenedLists = [];
|
|
272
337
|
const stack = [];
|
|
273
338
|
let current = null;
|
|
274
339
|
let nesting = 0;
|
|
275
340
|
const nestingStack = [];
|
|
276
341
|
let lastWithMap = { "map": [ 0, 1 ] };
|
|
277
|
-
|
|
278
|
-
if (
|
|
279
|
-
(token.type === "math_block") &&
|
|
280
|
-
(token.tag === "math") &&
|
|
281
|
-
token.map[1]
|
|
282
|
-
) {
|
|
283
|
-
// markdown-it-texmath package does not account for math_block_end
|
|
284
|
-
token.map[1]++;
|
|
285
|
-
}
|
|
342
|
+
tokens.forEach((token) => {
|
|
286
343
|
if ((token.type === "bullet_list_open") ||
|
|
287
344
|
(token.type === "ordered_list_open")) {
|
|
288
345
|
// Save current context and start a new one
|
|
@@ -354,7 +411,8 @@ module.exports.forEachHeading = function forEachHeading(params, handler) {
|
|
|
354
411
|
* Calls the provided function for each inline code span's content.
|
|
355
412
|
*
|
|
356
413
|
* @param {string} input Markdown content.
|
|
357
|
-
* @param {Function} handler Callback function
|
|
414
|
+
* @param {Function} handler Callback function taking (code, lineIndex,
|
|
415
|
+
* columnIndex, ticks).
|
|
358
416
|
* @returns {void}
|
|
359
417
|
*/
|
|
360
418
|
function forEachInlineCodeSpan(input, handler) {
|
|
@@ -488,6 +546,60 @@ module.exports.addErrorContext = function addErrorContext(
|
|
|
488
546
|
addError(onError, lineNumber, null, context, range, fixInfo);
|
|
489
547
|
};
|
|
490
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
|
+
|
|
491
603
|
// Returns a range object for a line by applying a RegExp
|
|
492
604
|
module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
|
|
493
605
|
let range = null;
|
|
@@ -523,6 +635,18 @@ module.exports.frontMatterHasTitle =
|
|
|
523
635
|
function emphasisMarkersInContent(params) {
|
|
524
636
|
const { lines } = params;
|
|
525
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
|
+
});
|
|
526
650
|
// Search code spans
|
|
527
651
|
filterTokens(params, "inline", (token) => {
|
|
528
652
|
const { children, lineNumber, map } = token;
|
|
@@ -533,31 +657,19 @@ function emphasisMarkersInContent(params) {
|
|
|
533
657
|
(code, lineIndex, column, tickCount) => {
|
|
534
658
|
const codeLines = code.split(newLineRe);
|
|
535
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;
|
|
536
663
|
let match = null;
|
|
537
664
|
while ((match = emphasisMarkersRe.exec(codeLine))) {
|
|
538
|
-
const byLineIndex = lineNumber - 1 + lineIndex + codeLineIndex;
|
|
539
|
-
const inLine = byLine[byLineIndex] || [];
|
|
540
|
-
const codeLineOffset = codeLineIndex ? 0 : column - 1 + tickCount;
|
|
541
665
|
inLine.push(codeLineOffset + match.index);
|
|
542
|
-
byLine[byLineIndex] = inLine;
|
|
543
666
|
}
|
|
667
|
+
byLine[byLineIndex] = inLine;
|
|
544
668
|
});
|
|
545
669
|
}
|
|
546
670
|
);
|
|
547
671
|
}
|
|
548
672
|
});
|
|
549
|
-
// Search links
|
|
550
|
-
lines.forEach((tokenLine, tokenLineIndex) => {
|
|
551
|
-
let linkMatch = null;
|
|
552
|
-
while ((linkMatch = linkRe.exec(tokenLine))) {
|
|
553
|
-
let markerMatch = null;
|
|
554
|
-
while ((markerMatch = emphasisMarkersRe.exec(linkMatch[0]))) {
|
|
555
|
-
const inLine = byLine[tokenLineIndex] || [];
|
|
556
|
-
inLine.push(linkMatch.index + markerMatch.index);
|
|
557
|
-
byLine[tokenLineIndex] = inLine;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
673
|
return byLine;
|
|
562
674
|
}
|
|
563
675
|
module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
|
|
@@ -566,9 +678,10 @@ module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
|
|
|
566
678
|
* Gets the most common line ending, falling back to the platform default.
|
|
567
679
|
*
|
|
568
680
|
* @param {string} input Markdown content to analyze.
|
|
681
|
+
* @param {string} [platform] Platform identifier (process.platform).
|
|
569
682
|
* @returns {string} Preferred line ending.
|
|
570
683
|
*/
|
|
571
|
-
function getPreferredLineEnding(input) {
|
|
684
|
+
function getPreferredLineEnding(input, platform) {
|
|
572
685
|
let cr = 0;
|
|
573
686
|
let lf = 0;
|
|
574
687
|
let crlf = 0;
|
|
@@ -589,7 +702,8 @@ function getPreferredLineEnding(input) {
|
|
|
589
702
|
});
|
|
590
703
|
let preferredLineEnding = null;
|
|
591
704
|
if (!cr && !lf && !crlf) {
|
|
592
|
-
preferredLineEnding =
|
|
705
|
+
preferredLineEnding =
|
|
706
|
+
((platform || process.platform) === "win32") ? "\r\n" : "\n";
|
|
593
707
|
} else if ((lf >= crlf) && (lf >= cr)) {
|
|
594
708
|
preferredLineEnding = "\n";
|
|
595
709
|
} else if (crlf >= cr) {
|
|
@@ -692,8 +806,9 @@ module.exports.applyFixes = function applyFixes(input, errors) {
|
|
|
692
806
|
const editIndex = editColumn - 1;
|
|
693
807
|
if (
|
|
694
808
|
(lineIndex !== lastLineIndex) ||
|
|
695
|
-
(
|
|
696
|
-
(
|
|
809
|
+
(deleteCount === -1) ||
|
|
810
|
+
((editIndex + deleteCount) <=
|
|
811
|
+
(lastEditIndex - ((deleteCount > 0) ? 0 : 1)))
|
|
697
812
|
) {
|
|
698
813
|
lines[lineIndex] = applyFix(lines[lineIndex], fixInfo, lineEnding);
|
|
699
814
|
}
|
|
@@ -703,3 +818,80 @@ module.exports.applyFixes = function applyFixes(input, errors) {
|
|
|
703
818
|
// Return corrected input
|
|
704
819
|
return lines.filter((line) => line !== null).join(lineEnding);
|
|
705
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
|
};
|
package/lib/constants.js
ADDED
package/lib/markdownlint.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export = markdownlint;
|
|
|
8
8
|
*/
|
|
9
9
|
declare function markdownlint(options: Options, callback: LintCallback): void;
|
|
10
10
|
declare namespace markdownlint {
|
|
11
|
-
export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, RuleFunction, RuleParams, MarkdownItToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback };
|
|
11
|
+
export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, RuleFunction, RuleParams, MarkdownItToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Configuration options.
|
|
@@ -52,9 +52,13 @@ type Options = {
|
|
|
52
52
|
* Additional plugins.
|
|
53
53
|
*/
|
|
54
54
|
markdownItPlugins?: Plugin[];
|
|
55
|
+
/**
|
|
56
|
+
* File system implementation.
|
|
57
|
+
*/
|
|
58
|
+
fs?: any;
|
|
55
59
|
};
|
|
56
60
|
/**
|
|
57
|
-
* Called with the result of the lint
|
|
61
|
+
* Called with the result of the lint function.
|
|
58
62
|
*/
|
|
59
63
|
type LintCallback = (err: Error | null, results?: LintResults) => void;
|
|
60
64
|
/**
|
|
@@ -70,18 +74,21 @@ declare function markdownlintSync(options: Options): LintResults;
|
|
|
70
74
|
* @param {string} file Configuration file name.
|
|
71
75
|
* @param {ConfigurationParser[] | ReadConfigCallback} parsers Parsing
|
|
72
76
|
* function(s).
|
|
77
|
+
* @param {Object} [fs] File system implementation.
|
|
73
78
|
* @param {ReadConfigCallback} [callback] Callback (err, result) function.
|
|
74
79
|
* @returns {void}
|
|
75
80
|
*/
|
|
76
|
-
declare function readConfig(file: string, parsers: ConfigurationParser[] | ReadConfigCallback, callback?: ReadConfigCallback): void;
|
|
81
|
+
declare function readConfig(file: string, parsers: ConfigurationParser[] | ReadConfigCallback, fs?: any, callback?: ReadConfigCallback): void;
|
|
77
82
|
/**
|
|
78
83
|
* Read specified configuration file synchronously.
|
|
79
84
|
*
|
|
80
85
|
* @param {string} file Configuration file name.
|
|
81
86
|
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
|
|
87
|
+
* @param {Object} [fs] File system implementation.
|
|
82
88
|
* @returns {Configuration} Configuration object.
|
|
89
|
+
* @throws An Error if processing fails.
|
|
83
90
|
*/
|
|
84
|
-
declare function readConfigSync(file: string, parsers?: ConfigurationParser[]): Configuration;
|
|
91
|
+
declare function readConfigSync(file: string, parsers?: ConfigurationParser[], fs?: any): Configuration;
|
|
85
92
|
/**
|
|
86
93
|
* Gets the (semantic) version of the library.
|
|
87
94
|
*
|
|
@@ -199,9 +206,9 @@ type RuleOnErrorInfo = {
|
|
|
199
206
|
*/
|
|
200
207
|
lineNumber: number;
|
|
201
208
|
/**
|
|
202
|
-
*
|
|
209
|
+
* Detail about the error.
|
|
203
210
|
*/
|
|
204
|
-
|
|
211
|
+
detail?: string;
|
|
205
212
|
/**
|
|
206
213
|
* Context for the error.
|
|
207
214
|
*/
|
|
@@ -256,6 +263,10 @@ type Rule = {
|
|
|
256
263
|
* Rule tag(s).
|
|
257
264
|
*/
|
|
258
265
|
tags: string[];
|
|
266
|
+
/**
|
|
267
|
+
* True if asynchronous.
|
|
268
|
+
*/
|
|
269
|
+
asynchronous?: boolean;
|
|
259
270
|
/**
|
|
260
271
|
* Rule implementation.
|
|
261
272
|
*/
|
|
@@ -310,12 +321,16 @@ type LintError = {
|
|
|
310
321
|
/**
|
|
311
322
|
* Fix information.
|
|
312
323
|
*/
|
|
313
|
-
fixInfo
|
|
324
|
+
fixInfo?: FixInfo;
|
|
314
325
|
};
|
|
315
326
|
/**
|
|
316
327
|
* Fix information.
|
|
317
328
|
*/
|
|
318
329
|
type FixInfo = {
|
|
330
|
+
/**
|
|
331
|
+
* Line number (1-based).
|
|
332
|
+
*/
|
|
333
|
+
lineNumber?: number;
|
|
319
334
|
/**
|
|
320
335
|
* Column of the fix (1-based).
|
|
321
336
|
*/
|
|
@@ -334,20 +349,24 @@ type FixInfo = {
|
|
|
334
349
|
* {@link ../schema/markdownlint-config-schema.json}.
|
|
335
350
|
*/
|
|
336
351
|
type Configuration = {
|
|
337
|
-
[x: string]:
|
|
352
|
+
[x: string]: RuleConfiguration;
|
|
338
353
|
};
|
|
339
354
|
/**
|
|
340
355
|
* Rule configuration.
|
|
341
356
|
*/
|
|
342
|
-
type RuleConfiguration = any;
|
|
357
|
+
type RuleConfiguration = boolean | any;
|
|
343
358
|
/**
|
|
344
359
|
* Parses a configuration string and returns a configuration object.
|
|
345
360
|
*/
|
|
346
361
|
type ConfigurationParser = (text: string) => Configuration;
|
|
347
362
|
/**
|
|
348
|
-
* Called with the result of the readConfig
|
|
363
|
+
* Called with the result of the readConfig function.
|
|
349
364
|
*/
|
|
350
365
|
type ReadConfigCallback = (err: Error | null, config?: Configuration) => void;
|
|
366
|
+
/**
|
|
367
|
+
* Called with the result of the resolveConfigExtends function.
|
|
368
|
+
*/
|
|
369
|
+
type ResolveConfigExtendsCallback = (err: Error | null, path?: string) => void;
|
|
351
370
|
/**
|
|
352
371
|
* Lint specified Markdown files.
|
|
353
372
|
*
|
|
@@ -360,6 +379,7 @@ declare function markdownlintPromise(options: Options): Promise<LintResults>;
|
|
|
360
379
|
*
|
|
361
380
|
* @param {string} file Configuration file name.
|
|
362
381
|
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
|
|
382
|
+
* @param {Object} [fs] File system implementation.
|
|
363
383
|
* @returns {Promise<Configuration>} Configuration object.
|
|
364
384
|
*/
|
|
365
|
-
declare function readConfigPromise(file: string, parsers?: ConfigurationParser[]): Promise<Configuration>;
|
|
385
|
+
declare function readConfigPromise(file: string, parsers?: ConfigurationParser[], fs?: any): Promise<Configuration>;
|