markdownlint 0.37.4 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/CONTRIBUTING.md +2 -3
- package/README.md +39 -21
- package/doc/Rules.md +158 -35
- package/doc/md027.md +8 -0
- package/doc/md038.md +23 -11
- package/doc/md041.md +31 -16
- package/doc/md043.md +22 -4
- package/doc/md045.md +9 -1
- package/doc/md051.md +17 -0
- package/doc/md052.md +12 -0
- package/doc/md053.md +3 -3
- package/doc/md059.md +30 -0
- package/helpers/README.md +2 -2
- package/helpers/helpers.cjs +4 -6
- package/helpers/micromark-helpers.cjs +29 -3
- package/helpers/package.json +1 -1
- package/lib/cache.mjs +19 -5
- package/lib/configuration-strict.d.ts +190 -126
- package/lib/constants.mjs +1 -1
- package/lib/exports.d.mts +1 -0
- package/lib/exports.mjs +1 -0
- package/lib/markdownit.cjs +6 -13
- package/lib/markdownlint.d.mts +36 -2
- package/lib/markdownlint.mjs +306 -282
- package/lib/md025.mjs +23 -15
- package/lib/md027.mjs +11 -1
- package/lib/md036.mjs +12 -2
- package/lib/md038.mjs +52 -71
- package/lib/md041.mjs +54 -16
- package/lib/md043.mjs +2 -0
- package/lib/md045.mjs +3 -1
- package/lib/md051.mjs +13 -5
- package/lib/md052.mjs +2 -1
- package/lib/md059.mjs +71 -0
- package/lib/micromark-parse.mjs +14 -3
- package/lib/parse-configuration.mjs +46 -0
- package/lib/rules.mjs +3 -1
- package/package.json +28 -25
- package/schema/.markdownlint.jsonc +82 -60
- package/schema/.markdownlint.yaml +74 -56
- package/schema/markdownlint-config-schema-strict.json +259 -144
- package/schema/markdownlint-config-schema.json +259 -144
package/lib/markdownlint.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { requireMarkdownItCjs } from "./defer-require.cjs";
|
|
|
8
8
|
import { resolveModule } from "./resolve-module.cjs";
|
|
9
9
|
import rules from "./rules.mjs";
|
|
10
10
|
import { parse as micromarkParse } from "./micromark-parse.mjs";
|
|
11
|
+
import parseConfiguration from "./parse-configuration.mjs";
|
|
11
12
|
import * as helpers from "../helpers/helpers.cjs";
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -269,37 +270,13 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
/**
|
|
272
|
-
*
|
|
273
|
+
* Result object for getEnabledRulesPerLineNumber.
|
|
273
274
|
*
|
|
274
|
-
* @
|
|
275
|
-
* @
|
|
276
|
-
* @
|
|
277
|
-
* @
|
|
275
|
+
* @typedef {Object} EnabledRulesPerLineNumberResult
|
|
276
|
+
* @property {Configuration} effectiveConfig Effective configuration.
|
|
277
|
+
* @property {any[]} enabledRulesPerLineNumber Enabled rules per line number.
|
|
278
|
+
* @property {Rule[]} enabledRuleList Enabled rule list.
|
|
278
279
|
*/
|
|
279
|
-
function parseConfiguration(name, content, parsers) {
|
|
280
|
-
let config = null;
|
|
281
|
-
let message = "";
|
|
282
|
-
const errors = [];
|
|
283
|
-
let index = 0;
|
|
284
|
-
// Try each parser
|
|
285
|
-
(parsers || [ JSON.parse ]).every((parser) => {
|
|
286
|
-
try {
|
|
287
|
-
config = parser(content);
|
|
288
|
-
} catch (error) {
|
|
289
|
-
errors.push(`Parser ${index++}: ${error.message}`);
|
|
290
|
-
}
|
|
291
|
-
return !config;
|
|
292
|
-
});
|
|
293
|
-
// Message if unable to parse
|
|
294
|
-
if (!config) {
|
|
295
|
-
errors.unshift(`Unable to parse '${name}'`);
|
|
296
|
-
message = errors.join("; ");
|
|
297
|
-
}
|
|
298
|
-
return {
|
|
299
|
-
config,
|
|
300
|
-
message
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
280
|
|
|
304
281
|
/**
|
|
305
282
|
* Create a mapping of enabled rules per line.
|
|
@@ -309,10 +286,10 @@ function parseConfiguration(name, content, parsers) {
|
|
|
309
286
|
* @param {string[]} frontMatterLines List of front matter lines.
|
|
310
287
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
311
288
|
* @param {Configuration} config Configuration object.
|
|
312
|
-
* @param {ConfigurationParser[] |
|
|
289
|
+
* @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
|
|
313
290
|
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
|
314
291
|
* names.
|
|
315
|
-
* @returns {
|
|
292
|
+
* @returns {EnabledRulesPerLineNumberResult} Effective configuration and enabled rules per line number.
|
|
316
293
|
*/
|
|
317
294
|
function getEnabledRulesPerLineNumber(
|
|
318
295
|
ruleList,
|
|
@@ -443,17 +420,17 @@ function getEnabledRulesPerLineNumber(
|
|
|
443
420
|
* Lints a string containing Markdown content.
|
|
444
421
|
*
|
|
445
422
|
* @param {Rule[]} ruleList List of rules.
|
|
446
|
-
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
|
447
|
-
* names.
|
|
423
|
+
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule names.
|
|
448
424
|
* @param {string} name Identifier for the content.
|
|
449
425
|
* @param {string} content Markdown content.
|
|
450
|
-
* @param {
|
|
426
|
+
* @param {MarkdownItFactory} markdownItFactory Function to create a markdown-it parser.
|
|
451
427
|
* @param {Configuration} config Configuration object.
|
|
452
|
-
* @param {ConfigurationParser[] |
|
|
428
|
+
* @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
|
|
453
429
|
* @param {RegExp | null} frontMatter Regular expression for front matter.
|
|
454
430
|
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
|
|
455
431
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
456
432
|
* @param {number} resultVersion Version of the LintResults object to return.
|
|
433
|
+
* @param {boolean} synchronous Whether to execute synchronously.
|
|
457
434
|
* @param {LintContentCallback} callback Callback (err, result) function.
|
|
458
435
|
* @returns {void}
|
|
459
436
|
*/
|
|
@@ -462,14 +439,17 @@ function lintContent(
|
|
|
462
439
|
aliasToRuleNames,
|
|
463
440
|
name,
|
|
464
441
|
content,
|
|
465
|
-
|
|
442
|
+
markdownItFactory,
|
|
466
443
|
config,
|
|
467
444
|
configParsers,
|
|
468
445
|
frontMatter,
|
|
469
446
|
handleRuleFailures,
|
|
470
447
|
noInlineConfig,
|
|
471
448
|
resultVersion,
|
|
449
|
+
synchronous,
|
|
472
450
|
callback) {
|
|
451
|
+
// Provide a consistent error-reporting callback
|
|
452
|
+
const callbackError = (error) => callback(error instanceof Error ? error : new Error(error));
|
|
473
453
|
// Remove UTF-8 byte order marker (if present)
|
|
474
454
|
content = content.replace(/^\uFEFF/, "");
|
|
475
455
|
// Remove front matter
|
|
@@ -490,263 +470,280 @@ function lintContent(
|
|
|
490
470
|
const needMarkdownItTokens = enabledRuleList.some(
|
|
491
471
|
(rule) => (rule.parser === "markdownit") || (rule.parser === undefined)
|
|
492
472
|
);
|
|
473
|
+
const needMicromarkTokens = enabledRuleList.some(
|
|
474
|
+
(rule) => (rule.parser === "micromark")
|
|
475
|
+
);
|
|
493
476
|
const customRulesPresent = (ruleList.length !== rules.length);
|
|
494
477
|
// Parse content into parser tokens
|
|
495
|
-
const micromarkTokens =
|
|
496
|
-
content,
|
|
497
|
-
|
|
498
|
-
);
|
|
478
|
+
const micromarkTokens = needMicromarkTokens ?
|
|
479
|
+
micromarkParse(content, { "freezeTokens": customRulesPresent }) :
|
|
480
|
+
[];
|
|
499
481
|
// Hide the content of HTML comments from rules
|
|
500
482
|
const preClearedContent = content;
|
|
501
483
|
content = helpers.clearHtmlCommentText(content);
|
|
502
484
|
// Parse content into lines and get markdown-it tokens
|
|
503
485
|
const lines = content.split(helpers.newLineRe);
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
})
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
})
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
"frontMatterLines": Object.freeze(frontMatterLines)
|
|
530
|
-
};
|
|
531
|
-
cacheInitialize({
|
|
532
|
-
...paramsBase,
|
|
533
|
-
"parsers": parsersMicromark,
|
|
534
|
-
"config": null
|
|
535
|
-
});
|
|
536
|
-
// Function to run for each rule
|
|
537
|
-
let results = [];
|
|
538
|
-
/**
|
|
539
|
-
* @param {Rule} rule Rule.
|
|
540
|
-
* @returns {Promise<void> | null} Promise.
|
|
541
|
-
*/
|
|
542
|
-
const forRule = (rule) => {
|
|
543
|
-
// Configure rule
|
|
544
|
-
const ruleName = rule.names[0].toUpperCase();
|
|
545
|
-
const tokens = {};
|
|
546
|
-
let parsers = parsersNone;
|
|
547
|
-
if (rule.parser === undefined) {
|
|
548
|
-
tokens.tokens = markdownitTokens;
|
|
549
|
-
parsers = parsersMarkdownIt;
|
|
550
|
-
} else if (rule.parser === "markdownit") {
|
|
551
|
-
parsers = parsersMarkdownIt;
|
|
552
|
-
} else if (rule.parser === "micromark") {
|
|
553
|
-
parsers = parsersMicromark;
|
|
554
|
-
}
|
|
555
|
-
const params = {
|
|
556
|
-
...paramsBase,
|
|
557
|
-
...tokens,
|
|
558
|
-
parsers,
|
|
559
|
-
"config": effectiveConfig[ruleName]
|
|
486
|
+
// Function to run after fetching markdown-it tokens (when needed)
|
|
487
|
+
const lintContentInternal = (markdownitTokens) => {
|
|
488
|
+
// Create (frozen) parameters for rules
|
|
489
|
+
/** @type {MarkdownParsers} */
|
|
490
|
+
// @ts-ignore
|
|
491
|
+
const parsersMarkdownIt = Object.freeze({
|
|
492
|
+
"markdownit": Object.freeze({
|
|
493
|
+
"tokens": markdownitTokens
|
|
494
|
+
})
|
|
495
|
+
});
|
|
496
|
+
/** @type {MarkdownParsers} */
|
|
497
|
+
// @ts-ignore
|
|
498
|
+
const parsersMicromark = Object.freeze({
|
|
499
|
+
"micromark": Object.freeze({
|
|
500
|
+
"tokens": micromarkTokens
|
|
501
|
+
})
|
|
502
|
+
});
|
|
503
|
+
/** @type {MarkdownParsers} */
|
|
504
|
+
// @ts-ignore
|
|
505
|
+
const parsersNone = Object.freeze({});
|
|
506
|
+
const paramsBase = {
|
|
507
|
+
name,
|
|
508
|
+
version,
|
|
509
|
+
"lines": Object.freeze(lines),
|
|
510
|
+
"frontMatterLines": Object.freeze(frontMatterLines)
|
|
560
511
|
};
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
//
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (
|
|
583
|
-
|
|
584
|
-
throwError("context");
|
|
585
|
-
}
|
|
586
|
-
if (errorInfo.information &&
|
|
587
|
-
!helpers.isUrl(errorInfo.information)) {
|
|
588
|
-
throwError("information");
|
|
512
|
+
cacheInitialize({
|
|
513
|
+
...paramsBase,
|
|
514
|
+
"parsers": parsersMicromark,
|
|
515
|
+
"config": null
|
|
516
|
+
});
|
|
517
|
+
// Function to run for each rule
|
|
518
|
+
let results = [];
|
|
519
|
+
/**
|
|
520
|
+
* @param {Rule} rule Rule.
|
|
521
|
+
* @returns {Promise<void> | null} Promise.
|
|
522
|
+
*/
|
|
523
|
+
const forRule = (rule) => {
|
|
524
|
+
// Configure rule
|
|
525
|
+
const ruleName = rule.names[0].toUpperCase();
|
|
526
|
+
const tokens = {};
|
|
527
|
+
let parsers = parsersNone;
|
|
528
|
+
if (rule.parser === undefined) {
|
|
529
|
+
tokens.tokens = markdownitTokens;
|
|
530
|
+
parsers = parsersMarkdownIt;
|
|
531
|
+
} else if (rule.parser === "markdownit") {
|
|
532
|
+
parsers = parsersMarkdownIt;
|
|
533
|
+
} else if (rule.parser === "micromark") {
|
|
534
|
+
parsers = parsersMicromark;
|
|
589
535
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
536
|
+
const params = {
|
|
537
|
+
...paramsBase,
|
|
538
|
+
...tokens,
|
|
539
|
+
parsers,
|
|
540
|
+
"config": effectiveConfig[ruleName]
|
|
541
|
+
};
|
|
542
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
543
|
+
function throwError(property) {
|
|
544
|
+
throw new Error(
|
|
545
|
+
`Value of '${property}' passed to onError by '${ruleName}' is incorrect for '${name}'.`);
|
|
600
546
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
547
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
548
|
+
function onError(errorInfo) {
|
|
549
|
+
if (!errorInfo ||
|
|
550
|
+
!helpers.isNumber(errorInfo.lineNumber) ||
|
|
551
|
+
(errorInfo.lineNumber < 1) ||
|
|
552
|
+
(errorInfo.lineNumber > lines.length)) {
|
|
553
|
+
throwError("lineNumber");
|
|
606
554
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
(fixInfo.lineNumber > lines.length))) {
|
|
611
|
-
throwError("fixInfo.lineNumber");
|
|
612
|
-
}
|
|
613
|
-
cleanFixInfo.lineNumber =
|
|
614
|
-
fixInfo.lineNumber + frontMatterLines.length;
|
|
555
|
+
const lineNumber = errorInfo.lineNumber + frontMatterLines.length;
|
|
556
|
+
if (!enabledRulesPerLineNumber[lineNumber][ruleName]) {
|
|
557
|
+
return;
|
|
615
558
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
(fixInfo.editColumn < 1) ||
|
|
620
|
-
(fixInfo.editColumn >
|
|
621
|
-
lines[effectiveLineNumber - 1].length + 1))) {
|
|
622
|
-
throwError("fixInfo.editColumn");
|
|
623
|
-
}
|
|
624
|
-
cleanFixInfo.editColumn = fixInfo.editColumn;
|
|
559
|
+
if (errorInfo.detail &&
|
|
560
|
+
!helpers.isString(errorInfo.detail)) {
|
|
561
|
+
throwError("detail");
|
|
625
562
|
}
|
|
626
|
-
if (
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
cleanFixInfo.deleteCount = fixInfo.deleteCount;
|
|
563
|
+
if (errorInfo.context &&
|
|
564
|
+
!helpers.isString(errorInfo.context)) {
|
|
565
|
+
throwError("context");
|
|
566
|
+
}
|
|
567
|
+
if (errorInfo.information &&
|
|
568
|
+
!helpers.isUrl(errorInfo.information)) {
|
|
569
|
+
throwError("information");
|
|
634
570
|
}
|
|
635
|
-
if (
|
|
636
|
-
|
|
637
|
-
|
|
571
|
+
if (errorInfo.range &&
|
|
572
|
+
(!Array.isArray(errorInfo.range) ||
|
|
573
|
+
(errorInfo.range.length !== 2) ||
|
|
574
|
+
!helpers.isNumber(errorInfo.range[0]) ||
|
|
575
|
+
(errorInfo.range[0] < 1) ||
|
|
576
|
+
!helpers.isNumber(errorInfo.range[1]) ||
|
|
577
|
+
(errorInfo.range[1] < 1) ||
|
|
578
|
+
((errorInfo.range[0] + errorInfo.range[1] - 1) >
|
|
579
|
+
lines[errorInfo.lineNumber - 1].length))) {
|
|
580
|
+
throwError("range");
|
|
581
|
+
}
|
|
582
|
+
const fixInfo = errorInfo.fixInfo;
|
|
583
|
+
const cleanFixInfo = {};
|
|
584
|
+
if (fixInfo) {
|
|
585
|
+
if (!helpers.isObject(fixInfo)) {
|
|
586
|
+
throwError("fixInfo");
|
|
587
|
+
}
|
|
588
|
+
if (fixInfo.lineNumber !== undefined) {
|
|
589
|
+
if ((!helpers.isNumber(fixInfo.lineNumber) ||
|
|
590
|
+
(fixInfo.lineNumber < 1) ||
|
|
591
|
+
(fixInfo.lineNumber > lines.length))) {
|
|
592
|
+
throwError("fixInfo.lineNumber");
|
|
593
|
+
}
|
|
594
|
+
cleanFixInfo.lineNumber =
|
|
595
|
+
fixInfo.lineNumber + frontMatterLines.length;
|
|
596
|
+
}
|
|
597
|
+
const effectiveLineNumber = fixInfo.lineNumber || errorInfo.lineNumber;
|
|
598
|
+
if (fixInfo.editColumn !== undefined) {
|
|
599
|
+
if ((!helpers.isNumber(fixInfo.editColumn) ||
|
|
600
|
+
(fixInfo.editColumn < 1) ||
|
|
601
|
+
(fixInfo.editColumn >
|
|
602
|
+
lines[effectiveLineNumber - 1].length + 1))) {
|
|
603
|
+
throwError("fixInfo.editColumn");
|
|
604
|
+
}
|
|
605
|
+
cleanFixInfo.editColumn = fixInfo.editColumn;
|
|
606
|
+
}
|
|
607
|
+
if (fixInfo.deleteCount !== undefined) {
|
|
608
|
+
if ((!helpers.isNumber(fixInfo.deleteCount) ||
|
|
609
|
+
(fixInfo.deleteCount < -1) ||
|
|
610
|
+
(fixInfo.deleteCount >
|
|
611
|
+
lines[effectiveLineNumber - 1].length))) {
|
|
612
|
+
throwError("fixInfo.deleteCount");
|
|
613
|
+
}
|
|
614
|
+
cleanFixInfo.deleteCount = fixInfo.deleteCount;
|
|
615
|
+
}
|
|
616
|
+
if (fixInfo.insertText !== undefined) {
|
|
617
|
+
if (!helpers.isString(fixInfo.insertText)) {
|
|
618
|
+
throwError("fixInfo.insertText");
|
|
619
|
+
}
|
|
620
|
+
cleanFixInfo.insertText = fixInfo.insertText;
|
|
638
621
|
}
|
|
639
|
-
cleanFixInfo.insertText = fixInfo.insertText;
|
|
640
622
|
}
|
|
623
|
+
const information = errorInfo.information || rule.information;
|
|
624
|
+
results.push({
|
|
625
|
+
lineNumber,
|
|
626
|
+
"ruleName": rule.names[0],
|
|
627
|
+
"ruleNames": rule.names,
|
|
628
|
+
"ruleDescription": rule.description,
|
|
629
|
+
"ruleInformation": information ? information.href : null,
|
|
630
|
+
"errorDetail": errorInfo.detail || null,
|
|
631
|
+
"errorContext": errorInfo.context || null,
|
|
632
|
+
"errorRange": errorInfo.range ? [ ...errorInfo.range ] : null,
|
|
633
|
+
"fixInfo": fixInfo ? cleanFixInfo : null
|
|
634
|
+
});
|
|
641
635
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
lineNumber,
|
|
645
|
-
"
|
|
646
|
-
"ruleNames": rule.names,
|
|
647
|
-
"ruleDescription": rule.description,
|
|
648
|
-
"ruleInformation": information ? information.href : null,
|
|
649
|
-
"errorDetail": errorInfo.detail || null,
|
|
650
|
-
"errorContext": errorInfo.context || null,
|
|
651
|
-
"errorRange": errorInfo.range ? [ ...errorInfo.range ] : null,
|
|
652
|
-
"fixInfo": fixInfo ? cleanFixInfo : null
|
|
636
|
+
// Call (possibly external) rule function to report errors
|
|
637
|
+
const catchCallsOnError = (error) => onError({
|
|
638
|
+
"lineNumber": 1,
|
|
639
|
+
"detail": `This rule threw an exception: ${error.message || error}`
|
|
653
640
|
});
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
// Asynchronous rule, ensure it returns a Promise
|
|
663
|
-
const ruleFunctionPromise =
|
|
664
|
-
Promise.resolve().then(invokeRuleFunction);
|
|
665
|
-
return handleRuleFailures ?
|
|
666
|
-
ruleFunctionPromise.catch(catchCallsOnError) :
|
|
667
|
-
ruleFunctionPromise;
|
|
668
|
-
}
|
|
669
|
-
// Synchronous rule
|
|
670
|
-
try {
|
|
671
|
-
invokeRuleFunction();
|
|
672
|
-
} catch (error) {
|
|
673
|
-
if (handleRuleFailures) {
|
|
674
|
-
catchCallsOnError(error);
|
|
675
|
-
} else {
|
|
676
|
-
throw error;
|
|
641
|
+
const invokeRuleFunction = () => rule.function(params, onError);
|
|
642
|
+
if (rule.asynchronous) {
|
|
643
|
+
// Asynchronous rule, ensure it returns a Promise
|
|
644
|
+
const ruleFunctionPromise =
|
|
645
|
+
Promise.resolve().then(invokeRuleFunction);
|
|
646
|
+
return handleRuleFailures ?
|
|
647
|
+
ruleFunctionPromise.catch(catchCallsOnError) :
|
|
648
|
+
ruleFunctionPromise;
|
|
677
649
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
));
|
|
688
|
-
if (resultVersion < 3) {
|
|
689
|
-
// Remove fixInfo and multiple errors for the same rule and line number
|
|
690
|
-
const noPrevious = {
|
|
691
|
-
"ruleName": null,
|
|
692
|
-
"lineNumber": -1
|
|
693
|
-
};
|
|
694
|
-
results = results.filter((error, index, array) => {
|
|
695
|
-
delete error.fixInfo;
|
|
696
|
-
const previous = array[index - 1] || noPrevious;
|
|
697
|
-
return (
|
|
698
|
-
(error.ruleName !== previous.ruleName) ||
|
|
699
|
-
(error.lineNumber !== previous.lineNumber)
|
|
700
|
-
);
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
if (resultVersion === 0) {
|
|
704
|
-
// Return a dictionary of rule->[line numbers]
|
|
705
|
-
const dictionary = {};
|
|
706
|
-
for (const error of results) {
|
|
707
|
-
const ruleLines = dictionary[error.ruleName] || [];
|
|
708
|
-
ruleLines.push(error.lineNumber);
|
|
709
|
-
dictionary[error.ruleName] = ruleLines;
|
|
650
|
+
// Synchronous rule
|
|
651
|
+
try {
|
|
652
|
+
invokeRuleFunction();
|
|
653
|
+
} catch (error) {
|
|
654
|
+
if (handleRuleFailures) {
|
|
655
|
+
catchCallsOnError(error);
|
|
656
|
+
} else {
|
|
657
|
+
throw error;
|
|
658
|
+
}
|
|
710
659
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
//
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
660
|
+
return null;
|
|
661
|
+
};
|
|
662
|
+
const formatResults = () => {
|
|
663
|
+
// Sort results by rule name by line number
|
|
664
|
+
results.sort((a, b) => (
|
|
665
|
+
a.ruleName.localeCompare(b.ruleName) ||
|
|
666
|
+
a.lineNumber - b.lineNumber
|
|
667
|
+
));
|
|
668
|
+
if (resultVersion < 3) {
|
|
669
|
+
// Remove fixInfo and multiple errors for the same rule and line number
|
|
670
|
+
const noPrevious = {
|
|
671
|
+
"ruleName": null,
|
|
672
|
+
"lineNumber": -1
|
|
673
|
+
};
|
|
674
|
+
results = results.filter((error, index, array) => {
|
|
675
|
+
delete error.fixInfo;
|
|
676
|
+
const previous = array[index - 1] || noPrevious;
|
|
677
|
+
return (
|
|
678
|
+
(error.ruleName !== previous.ruleName) ||
|
|
679
|
+
(error.lineNumber !== previous.lineNumber)
|
|
680
|
+
);
|
|
681
|
+
});
|
|
718
682
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
683
|
+
if (resultVersion === 0) {
|
|
684
|
+
// Return a dictionary of rule->[line numbers]
|
|
685
|
+
const dictionary = {};
|
|
686
|
+
for (const error of results) {
|
|
687
|
+
const ruleLines = dictionary[error.ruleName] || [];
|
|
688
|
+
ruleLines.push(error.lineNumber);
|
|
689
|
+
dictionary[error.ruleName] = ruleLines;
|
|
690
|
+
}
|
|
691
|
+
// @ts-ignore
|
|
692
|
+
results = dictionary;
|
|
693
|
+
} else if (resultVersion === 1) {
|
|
694
|
+
// Use ruleAlias instead of ruleNames
|
|
695
|
+
for (const error of results) {
|
|
696
|
+
error.ruleAlias = error.ruleNames[1] || error.ruleName;
|
|
697
|
+
delete error.ruleNames;
|
|
698
|
+
}
|
|
699
|
+
} else {
|
|
700
|
+
// resultVersion 2 or 3: Remove unwanted ruleName
|
|
701
|
+
for (const error of results) {
|
|
702
|
+
delete error.ruleName;
|
|
703
|
+
}
|
|
723
704
|
}
|
|
705
|
+
return results;
|
|
706
|
+
};
|
|
707
|
+
// Run all rules
|
|
708
|
+
const ruleListAsync = enabledRuleList.filter((rule) => rule.asynchronous);
|
|
709
|
+
const ruleListSync = enabledRuleList.filter((rule) => !rule.asynchronous);
|
|
710
|
+
const ruleListAsyncFirst = [
|
|
711
|
+
...ruleListAsync,
|
|
712
|
+
...ruleListSync
|
|
713
|
+
];
|
|
714
|
+
const callbackSuccess = () => callback(null, formatResults());
|
|
715
|
+
try {
|
|
716
|
+
const ruleResults = ruleListAsyncFirst.map(forRule);
|
|
717
|
+
if (ruleListAsync.length > 0) {
|
|
718
|
+
Promise.all(ruleResults.slice(0, ruleListAsync.length))
|
|
719
|
+
.then(callbackSuccess)
|
|
720
|
+
.catch(callbackError);
|
|
721
|
+
} else {
|
|
722
|
+
callbackSuccess();
|
|
723
|
+
}
|
|
724
|
+
} catch (error) {
|
|
725
|
+
callbackError(error);
|
|
726
|
+
} finally {
|
|
727
|
+
cacheInitialize();
|
|
724
728
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
} else {
|
|
744
|
-
callbackSuccess();
|
|
745
|
-
}
|
|
746
|
-
} catch (error) {
|
|
747
|
-
callbackError(error);
|
|
748
|
-
} finally {
|
|
749
|
-
cacheInitialize();
|
|
729
|
+
};
|
|
730
|
+
if (!needMarkdownItTokens || synchronous) {
|
|
731
|
+
// Need/able to call into markdown-it and lintContentInternal synchronously
|
|
732
|
+
const markdownItTokens = needMarkdownItTokens ?
|
|
733
|
+
requireMarkdownItCjs().getMarkdownItTokens(markdownItFactory(), preClearedContent, lines) :
|
|
734
|
+
[];
|
|
735
|
+
lintContentInternal(markdownItTokens);
|
|
736
|
+
} else {
|
|
737
|
+
// Need to call into markdown-it and lintContentInternal asynchronously
|
|
738
|
+
Promise.all([
|
|
739
|
+
// eslint-disable-next-line no-inline-comments
|
|
740
|
+
import(/* webpackMode: "eager" */ "./markdownit.cjs"),
|
|
741
|
+
// eslint-disable-next-line no-promise-executor-return
|
|
742
|
+
new Promise((resolve) => resolve(markdownItFactory()))
|
|
743
|
+
]).then(([ markdownitCjs, markdownIt ]) => {
|
|
744
|
+
const markdownItTokens = markdownitCjs.getMarkdownItTokens(markdownIt, preClearedContent, lines);
|
|
745
|
+
lintContentInternal(markdownItTokens);
|
|
746
|
+
}).catch(callbackError);
|
|
750
747
|
}
|
|
751
748
|
}
|
|
752
749
|
|
|
@@ -754,12 +751,11 @@ function lintContent(
|
|
|
754
751
|
* Lints a file containing Markdown content.
|
|
755
752
|
*
|
|
756
753
|
* @param {Rule[]} ruleList List of rules.
|
|
757
|
-
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
|
758
|
-
* names.
|
|
754
|
+
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule names.
|
|
759
755
|
* @param {string} file Path of file to lint.
|
|
760
|
-
* @param {
|
|
756
|
+
* @param {MarkdownItFactory} markdownItFactory Function to create a markdown-it parser.
|
|
761
757
|
* @param {Configuration} config Configuration object.
|
|
762
|
-
* @param {ConfigurationParser[] |
|
|
758
|
+
* @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
|
|
763
759
|
* @param {RegExp | null} frontMatter Regular expression for front matter.
|
|
764
760
|
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
|
|
765
761
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
@@ -773,7 +769,7 @@ function lintFile(
|
|
|
773
769
|
ruleList,
|
|
774
770
|
aliasToRuleNames,
|
|
775
771
|
file,
|
|
776
|
-
|
|
772
|
+
markdownItFactory,
|
|
777
773
|
config,
|
|
778
774
|
configParsers,
|
|
779
775
|
frontMatter,
|
|
@@ -793,13 +789,14 @@ function lintFile(
|
|
|
793
789
|
aliasToRuleNames,
|
|
794
790
|
file,
|
|
795
791
|
content,
|
|
796
|
-
|
|
792
|
+
markdownItFactory,
|
|
797
793
|
config,
|
|
798
794
|
configParsers,
|
|
799
795
|
frontMatter,
|
|
800
796
|
handleRuleFailures,
|
|
801
797
|
noInlineConfig,
|
|
802
798
|
resultVersion,
|
|
799
|
+
synchronous,
|
|
803
800
|
callback
|
|
804
801
|
);
|
|
805
802
|
}
|
|
@@ -851,7 +848,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
851
848
|
const strings = options.strings || {};
|
|
852
849
|
const stringsKeys = Object.keys(strings);
|
|
853
850
|
const config = options.config || { "default": true };
|
|
854
|
-
const configParsers = options.configParsers ||
|
|
851
|
+
const configParsers = options.configParsers || undefined;
|
|
855
852
|
const frontMatter = (options.frontMatter === undefined) ?
|
|
856
853
|
helpers.frontMatterRe :
|
|
857
854
|
options.frontMatter;
|
|
@@ -860,7 +857,9 @@ function lintInput(options, synchronous, callback) {
|
|
|
860
857
|
const resultVersion = (options.resultVersion === undefined) ?
|
|
861
858
|
3 :
|
|
862
859
|
options.resultVersion;
|
|
863
|
-
const
|
|
860
|
+
const markdownItFactory =
|
|
861
|
+
options.markdownItFactory ||
|
|
862
|
+
(() => { throw new Error("The option 'markdownItFactory' was required (due to the option 'customRules' including a rule requiring the 'markdown-it' parser), but 'markdownItFactory' was not set."); });
|
|
864
863
|
const fs = options.fs || nodeFs;
|
|
865
864
|
const aliasToRuleNames = mapAliasToRuleNames(ruleList);
|
|
866
865
|
const results = newResults(ruleList);
|
|
@@ -892,7 +891,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
892
891
|
ruleList,
|
|
893
892
|
aliasToRuleNames,
|
|
894
893
|
currentItem,
|
|
895
|
-
|
|
894
|
+
markdownItFactory,
|
|
896
895
|
config,
|
|
897
896
|
configParsers,
|
|
898
897
|
frontMatter,
|
|
@@ -911,13 +910,14 @@ function lintInput(options, synchronous, callback) {
|
|
|
911
910
|
aliasToRuleNames,
|
|
912
911
|
currentItem,
|
|
913
912
|
strings[currentItem] || "",
|
|
914
|
-
|
|
913
|
+
markdownItFactory,
|
|
915
914
|
config,
|
|
916
915
|
configParsers,
|
|
917
916
|
frontMatter,
|
|
918
917
|
handleRuleFailures,
|
|
919
918
|
noInlineConfig,
|
|
920
919
|
resultVersion,
|
|
920
|
+
synchronous,
|
|
921
921
|
lintWorkerCallback
|
|
922
922
|
);
|
|
923
923
|
} else if (concurrency === 0) {
|
|
@@ -1194,6 +1194,7 @@ export function readConfigSync(file, parsers, fs) {
|
|
|
1194
1194
|
// Try to parse file
|
|
1195
1195
|
const { config, message } = parseConfiguration(file, content, parsers);
|
|
1196
1196
|
if (!config) {
|
|
1197
|
+
// @ts-ignore
|
|
1197
1198
|
throw new Error(message);
|
|
1198
1199
|
}
|
|
1199
1200
|
// Extend configuration
|
|
@@ -1473,6 +1474,29 @@ export function getVersion() {
|
|
|
1473
1474
|
* @property {RuleFunction} function Rule implementation.
|
|
1474
1475
|
*/
|
|
1475
1476
|
|
|
1477
|
+
/**
|
|
1478
|
+
* Method used by the markdown-it parser to parse input.
|
|
1479
|
+
*
|
|
1480
|
+
* @callback MarkdownItParse
|
|
1481
|
+
* @param {string} src Source string.
|
|
1482
|
+
* @param {Object} env Environment sandbox.
|
|
1483
|
+
* @returns {import("markdown-it").Token[]} Tokens.
|
|
1484
|
+
*/
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* Instance of the markdown-it parser.
|
|
1488
|
+
*
|
|
1489
|
+
* @typedef MarkdownIt
|
|
1490
|
+
* @property {MarkdownItParse} parse Method to parse input.
|
|
1491
|
+
*/
|
|
1492
|
+
|
|
1493
|
+
/**
|
|
1494
|
+
* Gets an instance of the markdown-it parser. Any plugins should already have been loaded.
|
|
1495
|
+
*
|
|
1496
|
+
* @callback MarkdownItFactory
|
|
1497
|
+
* @returns {MarkdownIt|Promise<MarkdownIt>} Instance of the markdown-it parser.
|
|
1498
|
+
*/
|
|
1499
|
+
|
|
1476
1500
|
/**
|
|
1477
1501
|
* Configuration options.
|
|
1478
1502
|
*
|
|
@@ -1484,7 +1508,7 @@ export function getVersion() {
|
|
|
1484
1508
|
* @property {RegExp | null} [frontMatter] Front matter pattern.
|
|
1485
1509
|
* @property {Object} [fs] File system implementation.
|
|
1486
1510
|
* @property {boolean} [handleRuleFailures] True to catch exceptions.
|
|
1487
|
-
* @property {
|
|
1511
|
+
* @property {MarkdownItFactory} [markdownItFactory] Function to create a markdown-it parser.
|
|
1488
1512
|
* @property {boolean} [noInlineConfig] True to ignore HTML directives.
|
|
1489
1513
|
* @property {number} [resultVersion] Results object version.
|
|
1490
1514
|
* @property {Object.<string, string>} [strings] Strings to lint.
|
|
@@ -1501,7 +1525,7 @@ export function getVersion() {
|
|
|
1501
1525
|
*
|
|
1502
1526
|
* @callback ToStringCallback
|
|
1503
1527
|
* @param {boolean} [ruleAliases] True to use rule aliases.
|
|
1504
|
-
* @returns {string}
|
|
1528
|
+
* @returns {string} Pretty-printed results.
|
|
1505
1529
|
*/
|
|
1506
1530
|
|
|
1507
1531
|
/**
|