markdownlint 0.24.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/README.md +17 -7
- package/demo/markdownlint-browser.js +584 -367
- package/doc/CustomRules.md +15 -4
- package/doc/Rules.md +86 -13
- package/helpers/helpers.js +168 -50
- package/helpers/package.json +1 -1
- package/lib/cache.js +9 -9
- package/lib/constants.js +7 -0
- package/lib/markdownlint.d.ts +6 -2
- package/lib/markdownlint.js +216 -213
- package/lib/md007.js +4 -2
- package/lib/md010.js +21 -16
- package/lib/md011.js +2 -2
- package/lib/md033.js +12 -8
- package/lib/md035.js +4 -4
- package/lib/md037.js +5 -3
- package/lib/md039.js +2 -1
- package/lib/md043.js +0 -1
- package/lib/md044.js +2 -2
- package/lib/md049.js +45 -0
- package/lib/md050.js +45 -0
- package/lib/rules.js +4 -4
- package/package.json +25 -23
- package/schema/.markdownlint.jsonc +15 -1
- package/schema/.markdownlint.yaml +12 -0
- package/schema/markdownlint-config-schema.json +95 -1
package/lib/markdownlint.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const { promisify } = require("util");
|
|
7
7
|
const markdownIt = require("markdown-it");
|
|
8
|
+
const { deprecatedRuleNames } = require("./constants");
|
|
8
9
|
const rules = require("./rules");
|
|
9
10
|
const helpers = require("../helpers");
|
|
10
11
|
const cache = require("./cache");
|
|
@@ -14,15 +15,14 @@ const cache = require("./cache");
|
|
|
14
15
|
const dynamicRequire = (typeof __non_webpack_require__ === "undefined") ? require : /* c8 ignore next */ __non_webpack_require__;
|
|
15
16
|
// Capture native require implementation for dynamic loading of modules
|
|
16
17
|
|
|
17
|
-
const deprecatedRuleNames = [ "MD002", "MD006" ];
|
|
18
|
-
|
|
19
18
|
/**
|
|
20
19
|
* Validate the list of rules for structure and reuse.
|
|
21
20
|
*
|
|
22
21
|
* @param {Rule[]} ruleList List of rules.
|
|
22
|
+
* @param {boolean} synchronous Whether to execute synchronously.
|
|
23
23
|
* @returns {string} Error message if validation fails.
|
|
24
24
|
*/
|
|
25
|
-
function validateRuleList(ruleList) {
|
|
25
|
+
function validateRuleList(ruleList, synchronous) {
|
|
26
26
|
let result = null;
|
|
27
27
|
if (ruleList.length === rules.length) {
|
|
28
28
|
// No need to validate if only using built-in rules
|
|
@@ -62,6 +62,19 @@ function validateRuleList(ruleList) {
|
|
|
62
62
|
) {
|
|
63
63
|
result = newError("information");
|
|
64
64
|
}
|
|
65
|
+
if (
|
|
66
|
+
!result &&
|
|
67
|
+
(rule.asynchronous !== undefined) &&
|
|
68
|
+
(typeof rule.asynchronous !== "boolean")
|
|
69
|
+
) {
|
|
70
|
+
result = newError("asynchronous");
|
|
71
|
+
}
|
|
72
|
+
if (!result && rule.asynchronous && synchronous) {
|
|
73
|
+
result = new Error(
|
|
74
|
+
"Custom rule " + rule.names.join("/") + " at index " + customIndex +
|
|
75
|
+
" is asynchronous and can not be used in a synchronous context."
|
|
76
|
+
);
|
|
77
|
+
}
|
|
65
78
|
if (!result) {
|
|
66
79
|
rule.names.forEach(function forName(name) {
|
|
67
80
|
const nameUpper = name.toUpperCase();
|
|
@@ -182,27 +195,21 @@ function removeFrontMatter(content, frontMatter) {
|
|
|
182
195
|
* @returns {void}
|
|
183
196
|
*/
|
|
184
197
|
function annotateTokens(tokens, lines) {
|
|
185
|
-
let
|
|
198
|
+
let trMap = null;
|
|
186
199
|
tokens.forEach(function forToken(token) {
|
|
187
|
-
//
|
|
188
|
-
if (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
tableMap
|
|
196
|
-
) {
|
|
197
|
-
tableMap[0]++;
|
|
198
|
-
} else if (
|
|
199
|
-
(token.type === "thead_close") ||
|
|
200
|
-
(token.type === "tbody_close")
|
|
201
|
-
) {
|
|
202
|
-
tableMap = null;
|
|
200
|
+
// Provide missing maps for table content
|
|
201
|
+
if (token.type === "tr_open") {
|
|
202
|
+
trMap = token.map;
|
|
203
|
+
} else if (token.type === "tr_close") {
|
|
204
|
+
trMap = null;
|
|
205
|
+
}
|
|
206
|
+
if (!token.map && trMap) {
|
|
207
|
+
token.map = [ ...trMap ];
|
|
203
208
|
}
|
|
204
|
-
|
|
205
|
-
|
|
209
|
+
// Adjust maps for math blocks
|
|
210
|
+
if (helpers.isMathBlock(token) && token.map[1]) {
|
|
211
|
+
// markdown-it-texmath plugin does not account for math_block_end
|
|
212
|
+
token.map[1]++;
|
|
206
213
|
}
|
|
207
214
|
// Update token metadata
|
|
208
215
|
if (token.map) {
|
|
@@ -335,8 +342,7 @@ function getEnabledRulesPerLineNumber(
|
|
|
335
342
|
const enabledRulesPerLineNumber = new Array(1 + frontMatterLines.length);
|
|
336
343
|
// Helper functions
|
|
337
344
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
338
|
-
function handleInlineConfig(
|
|
339
|
-
const input = perLine ? lines : [ lines.join("\n") ];
|
|
345
|
+
function handleInlineConfig(input, forEachMatch, forEachLine) {
|
|
340
346
|
input.forEach((line, lineIndex) => {
|
|
341
347
|
if (!noInlineConfig) {
|
|
342
348
|
let match = null;
|
|
@@ -367,6 +373,7 @@ function getEnabledRulesPerLineNumber(
|
|
|
367
373
|
}
|
|
368
374
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
369
375
|
function applyEnableDisable(action, parameter, state) {
|
|
376
|
+
state = { ...state };
|
|
370
377
|
const enabled = (action.startsWith("ENABLE"));
|
|
371
378
|
const items = parameter ?
|
|
372
379
|
parameter.trim().toUpperCase().split(/\s+/) :
|
|
@@ -376,40 +383,42 @@ function getEnabledRulesPerLineNumber(
|
|
|
376
383
|
state[ruleName] = enabled;
|
|
377
384
|
});
|
|
378
385
|
});
|
|
386
|
+
return state;
|
|
379
387
|
}
|
|
380
388
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
381
389
|
function enableDisableFile(action, parameter) {
|
|
382
390
|
if ((action === "ENABLE-FILE") || (action === "DISABLE-FILE")) {
|
|
383
|
-
applyEnableDisable(action, parameter, enabledRules);
|
|
391
|
+
enabledRules = applyEnableDisable(action, parameter, enabledRules);
|
|
384
392
|
}
|
|
385
393
|
}
|
|
386
394
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
387
395
|
function captureRestoreEnableDisable(action, parameter) {
|
|
388
396
|
if (action === "CAPTURE") {
|
|
389
|
-
capturedRules =
|
|
397
|
+
capturedRules = enabledRules;
|
|
390
398
|
} else if (action === "RESTORE") {
|
|
391
|
-
enabledRules =
|
|
399
|
+
enabledRules = capturedRules;
|
|
392
400
|
} else if ((action === "ENABLE") || (action === "DISABLE")) {
|
|
393
|
-
enabledRules =
|
|
394
|
-
applyEnableDisable(action, parameter, enabledRules);
|
|
401
|
+
enabledRules = applyEnableDisable(action, parameter, enabledRules);
|
|
395
402
|
}
|
|
396
403
|
}
|
|
397
404
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
398
405
|
function updateLineState() {
|
|
399
|
-
enabledRulesPerLineNumber.push(
|
|
406
|
+
enabledRulesPerLineNumber.push(enabledRules);
|
|
400
407
|
}
|
|
401
408
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
402
409
|
function disableNextLine(action, parameter, lineNumber) {
|
|
403
410
|
if (action === "DISABLE-NEXT-LINE") {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
411
|
+
const nextLineNumber = frontMatterLines.length + lineNumber + 1;
|
|
412
|
+
enabledRulesPerLineNumber[nextLineNumber] =
|
|
413
|
+
applyEnableDisable(
|
|
414
|
+
action,
|
|
415
|
+
parameter,
|
|
416
|
+
enabledRulesPerLineNumber[nextLineNumber] || {}
|
|
417
|
+
);
|
|
409
418
|
}
|
|
410
419
|
}
|
|
411
420
|
// Handle inline comments
|
|
412
|
-
handleInlineConfig(
|
|
421
|
+
handleInlineConfig([ lines.join("\n") ], configureFile);
|
|
413
422
|
const effectiveConfig = getEffectiveConfig(
|
|
414
423
|
ruleList, config, aliasToRuleNames);
|
|
415
424
|
ruleList.forEach((rule) => {
|
|
@@ -418,9 +427,9 @@ function getEnabledRulesPerLineNumber(
|
|
|
418
427
|
enabledRules[ruleName] = !!effectiveConfig[ruleName];
|
|
419
428
|
});
|
|
420
429
|
capturedRules = enabledRules;
|
|
421
|
-
handleInlineConfig(
|
|
422
|
-
handleInlineConfig(
|
|
423
|
-
handleInlineConfig(
|
|
430
|
+
handleInlineConfig(lines, enableDisableFile);
|
|
431
|
+
handleInlineConfig(lines, captureRestoreEnableDisable, updateLineState);
|
|
432
|
+
handleInlineConfig(lines, disableNextLine);
|
|
424
433
|
// Return results
|
|
425
434
|
return {
|
|
426
435
|
effectiveConfig,
|
|
@@ -428,38 +437,6 @@ function getEnabledRulesPerLineNumber(
|
|
|
428
437
|
};
|
|
429
438
|
}
|
|
430
439
|
|
|
431
|
-
/**
|
|
432
|
-
* Compare function for Array.prototype.sort for ascending order of errors.
|
|
433
|
-
*
|
|
434
|
-
* @param {LintError} a First error.
|
|
435
|
-
* @param {LintError} b Second error.
|
|
436
|
-
* @returns {number} Positive value if a>b, negative value if b<a, 0 otherwise.
|
|
437
|
-
*/
|
|
438
|
-
function lineNumberComparison(a, b) {
|
|
439
|
-
return a.lineNumber - b.lineNumber;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Filter function to include everything.
|
|
444
|
-
*
|
|
445
|
-
* @returns {boolean} True.
|
|
446
|
-
*/
|
|
447
|
-
function filterAllValues() {
|
|
448
|
-
return true;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Function to return unique values from a sorted errors array.
|
|
453
|
-
*
|
|
454
|
-
* @param {LintError} value Error instance.
|
|
455
|
-
* @param {number} index Index in array.
|
|
456
|
-
* @param {LintError[]} array Array of errors.
|
|
457
|
-
* @returns {boolean} Filter value.
|
|
458
|
-
*/
|
|
459
|
-
function uniqueFilterForSortedErrors(value, index, array) {
|
|
460
|
-
return (index === 0) || (value.lineNumber > array[index - 1].lineNumber);
|
|
461
|
-
}
|
|
462
|
-
|
|
463
440
|
/**
|
|
464
441
|
* Lints a string containing Markdown content.
|
|
465
442
|
*
|
|
@@ -509,28 +486,28 @@ function lintContent(
|
|
|
509
486
|
);
|
|
510
487
|
// Create parameters for rules
|
|
511
488
|
const params = {
|
|
512
|
-
name,
|
|
513
|
-
tokens,
|
|
514
|
-
lines,
|
|
515
|
-
frontMatterLines
|
|
489
|
+
"name": helpers.deepFreeze(name),
|
|
490
|
+
"tokens": helpers.deepFreeze(tokens),
|
|
491
|
+
"lines": helpers.deepFreeze(lines),
|
|
492
|
+
"frontMatterLines": helpers.deepFreeze(frontMatterLines)
|
|
516
493
|
};
|
|
517
494
|
cache.lineMetadata(helpers.getLineMetadata(params));
|
|
518
495
|
cache.flattenedLists(helpers.flattenLists(params.tokens));
|
|
519
|
-
cache.
|
|
496
|
+
cache.codeBlockAndSpanRanges(
|
|
497
|
+
helpers.codeBlockAndSpanRanges(params, cache.lineMetadata())
|
|
498
|
+
);
|
|
520
499
|
// Function to run for each rule
|
|
521
|
-
|
|
500
|
+
let results = [];
|
|
522
501
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
523
502
|
function forRule(rule) {
|
|
524
503
|
// Configure rule
|
|
525
|
-
const
|
|
526
|
-
const ruleName = ruleNameFriendly.toUpperCase();
|
|
504
|
+
const ruleName = rule.names[0].toUpperCase();
|
|
527
505
|
params.config = effectiveConfig[ruleName];
|
|
528
506
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
529
507
|
function throwError(property) {
|
|
530
508
|
throw new Error(
|
|
531
509
|
"Property '" + property + "' of onError parameter is incorrect.");
|
|
532
510
|
}
|
|
533
|
-
const errors = [];
|
|
534
511
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
535
512
|
function onError(errorInfo) {
|
|
536
513
|
if (!errorInfo ||
|
|
@@ -539,6 +516,10 @@ function lintContent(
|
|
|
539
516
|
(errorInfo.lineNumber > lines.length)) {
|
|
540
517
|
throwError("lineNumber");
|
|
541
518
|
}
|
|
519
|
+
const lineNumber = errorInfo.lineNumber + frontMatterLines.length;
|
|
520
|
+
if (!enabledRulesPerLineNumber[lineNumber][ruleName]) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
542
523
|
if (errorInfo.detail &&
|
|
543
524
|
!helpers.isString(errorInfo.detail)) {
|
|
544
525
|
throwError("detail");
|
|
@@ -549,12 +530,12 @@ function lintContent(
|
|
|
549
530
|
}
|
|
550
531
|
if (errorInfo.range &&
|
|
551
532
|
(!Array.isArray(errorInfo.range) ||
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
533
|
+
(errorInfo.range.length !== 2) ||
|
|
534
|
+
!helpers.isNumber(errorInfo.range[0]) ||
|
|
535
|
+
(errorInfo.range[0] < 1) ||
|
|
536
|
+
!helpers.isNumber(errorInfo.range[1]) ||
|
|
537
|
+
(errorInfo.range[1] < 1) ||
|
|
538
|
+
((errorInfo.range[0] + errorInfo.range[1] - 1) >
|
|
558
539
|
lines[errorInfo.lineNumber - 1].length))) {
|
|
559
540
|
throwError("range");
|
|
560
541
|
}
|
|
@@ -599,78 +580,114 @@ function lintContent(
|
|
|
599
580
|
cleanFixInfo.insertText = fixInfo.insertText;
|
|
600
581
|
}
|
|
601
582
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
"
|
|
605
|
-
"
|
|
606
|
-
"
|
|
583
|
+
results.push({
|
|
584
|
+
lineNumber,
|
|
585
|
+
"ruleName": rule.names[0],
|
|
586
|
+
"ruleNames": rule.names,
|
|
587
|
+
"ruleDescription": rule.description,
|
|
588
|
+
"ruleInformation": rule.information ? rule.information.href : null,
|
|
589
|
+
"errorDetail": errorInfo.detail || null,
|
|
590
|
+
"errorContext": errorInfo.context || null,
|
|
591
|
+
"errorRange": errorInfo.range ? [ ...errorInfo.range ] : null,
|
|
607
592
|
"fixInfo": fixInfo ? cleanFixInfo : null
|
|
608
593
|
});
|
|
609
594
|
}
|
|
610
|
-
// Call (possibly external) rule function
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
595
|
+
// Call (possibly external) rule function to report errors
|
|
596
|
+
const catchCallsOnError = (error) => onError({
|
|
597
|
+
"lineNumber": 1,
|
|
598
|
+
"detail": `This rule threw an exception: ${error.message || error}`
|
|
599
|
+
});
|
|
600
|
+
const invokeRuleFunction = () => rule.function(params, onError);
|
|
601
|
+
if (rule.asynchronous) {
|
|
602
|
+
// Asynchronous rule, ensure it returns a Promise
|
|
603
|
+
const ruleFunctionPromise =
|
|
604
|
+
Promise.resolve().then(invokeRuleFunction);
|
|
605
|
+
return handleRuleFailures ?
|
|
606
|
+
ruleFunctionPromise.catch(catchCallsOnError) :
|
|
607
|
+
ruleFunctionPromise;
|
|
608
|
+
}
|
|
609
|
+
// Synchronous rule
|
|
610
|
+
try {
|
|
611
|
+
invokeRuleFunction();
|
|
612
|
+
} catch (error) {
|
|
613
|
+
if (handleRuleFailures) {
|
|
614
|
+
catchCallsOnError(error);
|
|
615
|
+
} else {
|
|
616
|
+
throw error;
|
|
619
617
|
}
|
|
620
|
-
} else {
|
|
621
|
-
rule.function(params, onError);
|
|
622
618
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
622
|
+
function formatResults() {
|
|
623
|
+
// Sort results by rule name by line number
|
|
624
|
+
results.sort((a, b) => (
|
|
625
|
+
a.ruleName.localeCompare(b.ruleName) ||
|
|
626
|
+
a.lineNumber - b.lineNumber
|
|
627
|
+
));
|
|
628
|
+
if (resultVersion < 3) {
|
|
629
|
+
// Remove fixInfo and multiple errors for the same rule and line number
|
|
630
|
+
const noPrevious = {
|
|
631
|
+
"ruleName": null,
|
|
632
|
+
"lineNumber": -1
|
|
633
|
+
};
|
|
634
|
+
results = results.filter((error, index, array) => {
|
|
635
|
+
delete error.fixInfo;
|
|
636
|
+
const previous = array[index - 1] || noPrevious;
|
|
637
|
+
return (
|
|
638
|
+
(error.ruleName !== previous.ruleName) ||
|
|
639
|
+
(error.lineNumber !== previous.lineNumber)
|
|
640
|
+
);
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
if (resultVersion === 0) {
|
|
644
|
+
// Return a dictionary of rule->[line numbers]
|
|
645
|
+
const dictionary = {};
|
|
646
|
+
for (const error of results) {
|
|
647
|
+
const ruleLines = dictionary[error.ruleName] || [];
|
|
648
|
+
ruleLines.push(error.lineNumber);
|
|
649
|
+
dictionary[error.ruleName] = ruleLines;
|
|
650
|
+
}
|
|
651
|
+
// @ts-ignore
|
|
652
|
+
results = dictionary;
|
|
653
|
+
} else if (resultVersion === 1) {
|
|
654
|
+
// Use ruleAlias instead of ruleNames
|
|
655
|
+
for (const error of results) {
|
|
656
|
+
error.ruleAlias = error.ruleNames[1] || error.ruleName;
|
|
657
|
+
delete error.ruleNames;
|
|
658
|
+
}
|
|
659
|
+
} else {
|
|
660
|
+
// resultVersion 2 or 3: Remove unwanted ruleName
|
|
661
|
+
for (const error of results) {
|
|
662
|
+
delete error.ruleName;
|
|
662
663
|
}
|
|
663
664
|
}
|
|
665
|
+
return results;
|
|
664
666
|
}
|
|
665
667
|
// Run all rules
|
|
668
|
+
const ruleListAsync = ruleList.filter((rule) => rule.asynchronous);
|
|
669
|
+
const ruleListSync = ruleList.filter((rule) => !rule.asynchronous);
|
|
670
|
+
const ruleListAsyncFirst = [
|
|
671
|
+
...ruleListAsync,
|
|
672
|
+
...ruleListSync
|
|
673
|
+
];
|
|
674
|
+
const callbackSuccess = () => callback(null, formatResults());
|
|
675
|
+
const callbackError =
|
|
676
|
+
(error) => callback(error instanceof Error ? error : new Error(error));
|
|
666
677
|
try {
|
|
667
|
-
|
|
678
|
+
const ruleResults = ruleListAsyncFirst.map(forRule);
|
|
679
|
+
if (ruleListAsync.length > 0) {
|
|
680
|
+
Promise.all(ruleResults.slice(0, ruleListAsync.length))
|
|
681
|
+
.then(callbackSuccess)
|
|
682
|
+
.catch(callbackError);
|
|
683
|
+
} else {
|
|
684
|
+
callbackSuccess();
|
|
685
|
+
}
|
|
668
686
|
} catch (error) {
|
|
687
|
+
callbackError(error);
|
|
688
|
+
} finally {
|
|
669
689
|
cache.clear();
|
|
670
|
-
return callback(error);
|
|
671
690
|
}
|
|
672
|
-
cache.clear();
|
|
673
|
-
return callback(null, result);
|
|
674
691
|
}
|
|
675
692
|
|
|
676
693
|
/**
|
|
@@ -731,7 +748,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
731
748
|
callback = callback || function noop() {};
|
|
732
749
|
// eslint-disable-next-line unicorn/prefer-spread
|
|
733
750
|
const ruleList = rules.concat(options.customRules || []);
|
|
734
|
-
const ruleErr = validateRuleList(ruleList);
|
|
751
|
+
const ruleErr = validateRuleList(ruleList, synchronous);
|
|
735
752
|
if (ruleErr) {
|
|
736
753
|
return callback(ruleErr);
|
|
737
754
|
}
|
|
@@ -759,37 +776,32 @@ function lintInput(options, synchronous, callback) {
|
|
|
759
776
|
const fs = options.fs || require("fs");
|
|
760
777
|
const results = newResults(ruleList);
|
|
761
778
|
let done = false;
|
|
762
|
-
|
|
763
|
-
let syncItem = null;
|
|
779
|
+
let concurrency = 0;
|
|
764
780
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
765
|
-
function
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
781
|
+
function lintWorker() {
|
|
782
|
+
let currentItem = null;
|
|
783
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
784
|
+
function lintWorkerCallback(err, result) {
|
|
785
|
+
concurrency--;
|
|
786
|
+
if (err) {
|
|
787
|
+
done = true;
|
|
788
|
+
return callback(err);
|
|
789
|
+
}
|
|
790
|
+
results[currentItem] = result;
|
|
791
|
+
if (!synchronous) {
|
|
792
|
+
lintWorker();
|
|
793
|
+
}
|
|
794
|
+
return null;
|
|
769
795
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
syncItem,
|
|
777
|
-
strings[syncItem] || "",
|
|
778
|
-
md,
|
|
779
|
-
config,
|
|
780
|
-
frontMatter,
|
|
781
|
-
handleRuleFailures,
|
|
782
|
-
noInlineConfig,
|
|
783
|
-
resultVersion,
|
|
784
|
-
syncCallback
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
if (synchronous) {
|
|
788
|
-
// Lint files synchronously
|
|
789
|
-
while (!done && (syncItem = files.shift())) {
|
|
796
|
+
if (done) {
|
|
797
|
+
// Abort for error or nothing left to do
|
|
798
|
+
} else if (files.length > 0) {
|
|
799
|
+
// Lint next file
|
|
800
|
+
concurrency++;
|
|
801
|
+
currentItem = files.shift();
|
|
790
802
|
lintFile(
|
|
791
803
|
ruleList,
|
|
792
|
-
|
|
804
|
+
currentItem,
|
|
793
805
|
md,
|
|
794
806
|
config,
|
|
795
807
|
frontMatter,
|
|
@@ -798,59 +810,48 @@ function lintInput(options, synchronous, callback) {
|
|
|
798
810
|
resultVersion,
|
|
799
811
|
fs,
|
|
800
812
|
synchronous,
|
|
801
|
-
|
|
813
|
+
lintWorkerCallback
|
|
802
814
|
);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
}
|
|
806
|
-
// Lint files asynchronously
|
|
807
|
-
let concurrency = 0;
|
|
808
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
809
|
-
function lintConcurrently() {
|
|
810
|
-
const asyncItem = files.shift();
|
|
811
|
-
if (done) {
|
|
812
|
-
// Nothing to do
|
|
813
|
-
} else if (asyncItem) {
|
|
815
|
+
} else if (stringsKeys.length > 0) {
|
|
816
|
+
// Lint next string
|
|
814
817
|
concurrency++;
|
|
815
|
-
|
|
818
|
+
currentItem = stringsKeys.shift();
|
|
819
|
+
lintContent(
|
|
816
820
|
ruleList,
|
|
817
|
-
|
|
821
|
+
currentItem,
|
|
822
|
+
strings[currentItem] || "",
|
|
818
823
|
md,
|
|
819
824
|
config,
|
|
820
825
|
frontMatter,
|
|
821
826
|
handleRuleFailures,
|
|
822
827
|
noInlineConfig,
|
|
823
828
|
resultVersion,
|
|
824
|
-
|
|
825
|
-
synchronous,
|
|
826
|
-
(err, result) => {
|
|
827
|
-
concurrency--;
|
|
828
|
-
if (err) {
|
|
829
|
-
done = true;
|
|
830
|
-
return callback(err);
|
|
831
|
-
}
|
|
832
|
-
results[asyncItem] = result;
|
|
833
|
-
lintConcurrently();
|
|
834
|
-
return null;
|
|
835
|
-
}
|
|
829
|
+
lintWorkerCallback
|
|
836
830
|
);
|
|
837
831
|
} else if (concurrency === 0) {
|
|
832
|
+
// Finish
|
|
838
833
|
done = true;
|
|
839
834
|
return callback(null, results);
|
|
840
835
|
}
|
|
841
836
|
return null;
|
|
842
837
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
838
|
+
if (synchronous) {
|
|
839
|
+
while (!done) {
|
|
840
|
+
lintWorker();
|
|
841
|
+
}
|
|
842
|
+
} else {
|
|
843
|
+
// Testing on a Raspberry Pi 4 Model B with an artificial 5ms file access
|
|
844
|
+
// delay suggests that a concurrency factor of 8 can eliminate the impact
|
|
845
|
+
// of that delay (i.e., total time is the same as with no delay).
|
|
846
|
+
lintWorker();
|
|
847
|
+
lintWorker();
|
|
848
|
+
lintWorker();
|
|
849
|
+
lintWorker();
|
|
850
|
+
lintWorker();
|
|
851
|
+
lintWorker();
|
|
852
|
+
lintWorker();
|
|
853
|
+
lintWorker();
|
|
854
|
+
}
|
|
854
855
|
return null;
|
|
855
856
|
}
|
|
856
857
|
|
|
@@ -906,12 +907,13 @@ function parseConfiguration(name, content, parsers) {
|
|
|
906
907
|
let config = null;
|
|
907
908
|
let message = "";
|
|
908
909
|
const errors = [];
|
|
910
|
+
let index = 0;
|
|
909
911
|
// Try each parser
|
|
910
912
|
(parsers || [ JSON.parse ]).every((parser) => {
|
|
911
913
|
try {
|
|
912
914
|
config = parser(content);
|
|
913
915
|
} catch (error) {
|
|
914
|
-
errors.push(error.message);
|
|
916
|
+
errors.push(`Parser ${index++}: ${error.message}`);
|
|
915
917
|
}
|
|
916
918
|
return !config;
|
|
917
919
|
});
|
|
@@ -1102,7 +1104,7 @@ function readConfigSync(file, parsers, fs) {
|
|
|
1102
1104
|
* @returns {string} SemVer string.
|
|
1103
1105
|
*/
|
|
1104
1106
|
function getVersion() {
|
|
1105
|
-
return require("
|
|
1107
|
+
return require("./constants").version;
|
|
1106
1108
|
}
|
|
1107
1109
|
|
|
1108
1110
|
// Export a/synchronous/Promise APIs
|
|
@@ -1172,7 +1174,7 @@ module.exports = markdownlint;
|
|
|
1172
1174
|
*
|
|
1173
1175
|
* @typedef {Object} RuleOnErrorInfo
|
|
1174
1176
|
* @property {number} lineNumber Line number (1-based).
|
|
1175
|
-
* @property {string} [
|
|
1177
|
+
* @property {string} [detail] Detail about the error.
|
|
1176
1178
|
* @property {string} [context] Context for the error.
|
|
1177
1179
|
* @property {number[]} [range] Column number (1-based) and length.
|
|
1178
1180
|
* @property {RuleOnErrorFixInfo} [fixInfo] Fix information.
|
|
@@ -1196,6 +1198,7 @@ module.exports = markdownlint;
|
|
|
1196
1198
|
* @property {string} description Rule description.
|
|
1197
1199
|
* @property {URL} [information] Link to more information.
|
|
1198
1200
|
* @property {string[]} tags Rule tag(s).
|
|
1201
|
+
* @property {boolean} [asynchronous] True if asynchronous.
|
|
1199
1202
|
* @property {RuleFunction} function Rule implementation.
|
|
1200
1203
|
*/
|
|
1201
1204
|
|
package/lib/md007.js
CHANGED
|
@@ -13,12 +13,14 @@ module.exports = {
|
|
|
13
13
|
"function": function MD007(params, onError) {
|
|
14
14
|
const indent = Number(params.config.indent || 2);
|
|
15
15
|
const startIndented = !!params.config.start_indented;
|
|
16
|
+
const startIndent = Number(params.config.start_indent || indent);
|
|
16
17
|
flattenedLists().forEach((list) => {
|
|
17
18
|
if (list.unordered && list.parentsUnordered) {
|
|
18
19
|
list.items.forEach((item) => {
|
|
19
20
|
const { lineNumber, line } = item;
|
|
20
|
-
const
|
|
21
|
-
|
|
21
|
+
const expectedIndent =
|
|
22
|
+
(startIndented ? startIndent : 0) +
|
|
23
|
+
(list.nesting * indent);
|
|
22
24
|
const actualIndent = indentFor(item);
|
|
23
25
|
let range = null;
|
|
24
26
|
let editColumn = 1;
|