markdownlint 0.37.3 → 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 +14 -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/defer-require.cjs +16 -0
- package/lib/exports.d.mts +2 -0
- package/lib/exports.mjs +2 -0
- package/lib/markdownit.cjs +6 -13
- package/lib/markdownlint.d.mts +36 -2
- package/lib/markdownlint.mjs +314 -292
- 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/md039.mjs +3 -22
- 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/node-imports-browser.mjs +0 -2
- package/lib/node-imports-node.mjs +0 -3
- package/lib/parse-configuration.mjs +46 -0
- package/lib/resolve-module.cjs +52 -0
- package/lib/resolve-module.d.cts +34 -0
- package/lib/rules.mjs +3 -1
- package/package.json +30 -27
- 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/node-imports-browser-module.cjs +0 -7
package/lib/markdownlint.mjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
// @ts-ignore
|
|
4
|
-
import { fs as nodeFs,
|
|
5
|
-
const dynamicRequire = module.createRequire(import.meta.url);
|
|
4
|
+
import { fs as nodeFs, os, path } from "#node-imports";
|
|
6
5
|
import { initialize as cacheInitialize } from "./cache.mjs";
|
|
7
6
|
import { version } from "./constants.mjs";
|
|
7
|
+
import { requireMarkdownItCjs } from "./defer-require.cjs";
|
|
8
|
+
import { resolveModule } from "./resolve-module.cjs";
|
|
8
9
|
import rules from "./rules.mjs";
|
|
9
10
|
import { parse as micromarkParse } from "./micromark-parse.mjs";
|
|
11
|
+
import parseConfiguration from "./parse-configuration.mjs";
|
|
10
12
|
import * as helpers from "../helpers/helpers.cjs";
|
|
11
13
|
|
|
12
14
|
/**
|
|
@@ -268,37 +270,13 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
|
268
270
|
}
|
|
269
271
|
|
|
270
272
|
/**
|
|
271
|
-
*
|
|
273
|
+
* Result object for getEnabledRulesPerLineNumber.
|
|
272
274
|
*
|
|
273
|
-
* @
|
|
274
|
-
* @
|
|
275
|
-
* @
|
|
276
|
-
* @
|
|
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.
|
|
277
279
|
*/
|
|
278
|
-
function parseConfiguration(name, content, parsers) {
|
|
279
|
-
let config = null;
|
|
280
|
-
let message = "";
|
|
281
|
-
const errors = [];
|
|
282
|
-
let index = 0;
|
|
283
|
-
// Try each parser
|
|
284
|
-
(parsers || [ JSON.parse ]).every((parser) => {
|
|
285
|
-
try {
|
|
286
|
-
config = parser(content);
|
|
287
|
-
} catch (error) {
|
|
288
|
-
errors.push(`Parser ${index++}: ${error.message}`);
|
|
289
|
-
}
|
|
290
|
-
return !config;
|
|
291
|
-
});
|
|
292
|
-
// Message if unable to parse
|
|
293
|
-
if (!config) {
|
|
294
|
-
errors.unshift(`Unable to parse '${name}'`);
|
|
295
|
-
message = errors.join("; ");
|
|
296
|
-
}
|
|
297
|
-
return {
|
|
298
|
-
config,
|
|
299
|
-
message
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
280
|
|
|
303
281
|
/**
|
|
304
282
|
* Create a mapping of enabled rules per line.
|
|
@@ -308,10 +286,10 @@ function parseConfiguration(name, content, parsers) {
|
|
|
308
286
|
* @param {string[]} frontMatterLines List of front matter lines.
|
|
309
287
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
310
288
|
* @param {Configuration} config Configuration object.
|
|
311
|
-
* @param {ConfigurationParser[] |
|
|
289
|
+
* @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
|
|
312
290
|
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
|
313
291
|
* names.
|
|
314
|
-
* @returns {
|
|
292
|
+
* @returns {EnabledRulesPerLineNumberResult} Effective configuration and enabled rules per line number.
|
|
315
293
|
*/
|
|
316
294
|
function getEnabledRulesPerLineNumber(
|
|
317
295
|
ruleList,
|
|
@@ -442,17 +420,17 @@ function getEnabledRulesPerLineNumber(
|
|
|
442
420
|
* Lints a string containing Markdown content.
|
|
443
421
|
*
|
|
444
422
|
* @param {Rule[]} ruleList List of rules.
|
|
445
|
-
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
|
446
|
-
* names.
|
|
423
|
+
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule names.
|
|
447
424
|
* @param {string} name Identifier for the content.
|
|
448
425
|
* @param {string} content Markdown content.
|
|
449
|
-
* @param {
|
|
426
|
+
* @param {MarkdownItFactory} markdownItFactory Function to create a markdown-it parser.
|
|
450
427
|
* @param {Configuration} config Configuration object.
|
|
451
|
-
* @param {ConfigurationParser[] |
|
|
428
|
+
* @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
|
|
452
429
|
* @param {RegExp | null} frontMatter Regular expression for front matter.
|
|
453
430
|
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
|
|
454
431
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
455
432
|
* @param {number} resultVersion Version of the LintResults object to return.
|
|
433
|
+
* @param {boolean} synchronous Whether to execute synchronously.
|
|
456
434
|
* @param {LintContentCallback} callback Callback (err, result) function.
|
|
457
435
|
* @returns {void}
|
|
458
436
|
*/
|
|
@@ -461,14 +439,17 @@ function lintContent(
|
|
|
461
439
|
aliasToRuleNames,
|
|
462
440
|
name,
|
|
463
441
|
content,
|
|
464
|
-
|
|
442
|
+
markdownItFactory,
|
|
465
443
|
config,
|
|
466
444
|
configParsers,
|
|
467
445
|
frontMatter,
|
|
468
446
|
handleRuleFailures,
|
|
469
447
|
noInlineConfig,
|
|
470
448
|
resultVersion,
|
|
449
|
+
synchronous,
|
|
471
450
|
callback) {
|
|
451
|
+
// Provide a consistent error-reporting callback
|
|
452
|
+
const callbackError = (error) => callback(error instanceof Error ? error : new Error(error));
|
|
472
453
|
// Remove UTF-8 byte order marker (if present)
|
|
473
454
|
content = content.replace(/^\uFEFF/, "");
|
|
474
455
|
// Remove front matter
|
|
@@ -489,263 +470,280 @@ function lintContent(
|
|
|
489
470
|
const needMarkdownItTokens = enabledRuleList.some(
|
|
490
471
|
(rule) => (rule.parser === "markdownit") || (rule.parser === undefined)
|
|
491
472
|
);
|
|
473
|
+
const needMicromarkTokens = enabledRuleList.some(
|
|
474
|
+
(rule) => (rule.parser === "micromark")
|
|
475
|
+
);
|
|
492
476
|
const customRulesPresent = (ruleList.length !== rules.length);
|
|
493
477
|
// Parse content into parser tokens
|
|
494
|
-
const micromarkTokens =
|
|
495
|
-
content,
|
|
496
|
-
|
|
497
|
-
);
|
|
478
|
+
const micromarkTokens = needMicromarkTokens ?
|
|
479
|
+
micromarkParse(content, { "freezeTokens": customRulesPresent }) :
|
|
480
|
+
[];
|
|
498
481
|
// Hide the content of HTML comments from rules
|
|
499
482
|
const preClearedContent = content;
|
|
500
483
|
content = helpers.clearHtmlCommentText(content);
|
|
501
484
|
// Parse content into lines and get markdown-it tokens
|
|
502
485
|
const lines = content.split(helpers.newLineRe);
|
|
503
|
-
|
|
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
|
-
"frontMatterLines": Object.freeze(frontMatterLines)
|
|
529
|
-
};
|
|
530
|
-
cacheInitialize({
|
|
531
|
-
...paramsBase,
|
|
532
|
-
"parsers": parsersMicromark,
|
|
533
|
-
"config": null
|
|
534
|
-
});
|
|
535
|
-
// Function to run for each rule
|
|
536
|
-
let results = [];
|
|
537
|
-
/**
|
|
538
|
-
* @param {Rule} rule Rule.
|
|
539
|
-
* @returns {Promise<void> | null} Promise.
|
|
540
|
-
*/
|
|
541
|
-
const forRule = (rule) => {
|
|
542
|
-
// Configure rule
|
|
543
|
-
const ruleName = rule.names[0].toUpperCase();
|
|
544
|
-
const tokens = {};
|
|
545
|
-
let parsers = parsersNone;
|
|
546
|
-
if (rule.parser === undefined) {
|
|
547
|
-
tokens.tokens = markdownitTokens;
|
|
548
|
-
parsers = parsersMarkdownIt;
|
|
549
|
-
} else if (rule.parser === "markdownit") {
|
|
550
|
-
parsers = parsersMarkdownIt;
|
|
551
|
-
} else if (rule.parser === "micromark") {
|
|
552
|
-
parsers = parsersMicromark;
|
|
553
|
-
}
|
|
554
|
-
const params = {
|
|
555
|
-
...paramsBase,
|
|
556
|
-
...tokens,
|
|
557
|
-
parsers,
|
|
558
|
-
"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)
|
|
559
511
|
};
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
//
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
if (
|
|
582
|
-
|
|
583
|
-
throwError("context");
|
|
584
|
-
}
|
|
585
|
-
if (errorInfo.information &&
|
|
586
|
-
!helpers.isUrl(errorInfo.information)) {
|
|
587
|
-
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;
|
|
588
535
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
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}'.`);
|
|
599
546
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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");
|
|
605
554
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
(fixInfo.lineNumber > lines.length))) {
|
|
610
|
-
throwError("fixInfo.lineNumber");
|
|
611
|
-
}
|
|
612
|
-
cleanFixInfo.lineNumber =
|
|
613
|
-
fixInfo.lineNumber + frontMatterLines.length;
|
|
555
|
+
const lineNumber = errorInfo.lineNumber + frontMatterLines.length;
|
|
556
|
+
if (!enabledRulesPerLineNumber[lineNumber][ruleName]) {
|
|
557
|
+
return;
|
|
614
558
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
(fixInfo.editColumn < 1) ||
|
|
619
|
-
(fixInfo.editColumn >
|
|
620
|
-
lines[effectiveLineNumber - 1].length + 1))) {
|
|
621
|
-
throwError("fixInfo.editColumn");
|
|
622
|
-
}
|
|
623
|
-
cleanFixInfo.editColumn = fixInfo.editColumn;
|
|
559
|
+
if (errorInfo.detail &&
|
|
560
|
+
!helpers.isString(errorInfo.detail)) {
|
|
561
|
+
throwError("detail");
|
|
624
562
|
}
|
|
625
|
-
if (
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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");
|
|
570
|
+
}
|
|
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");
|
|
633
581
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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;
|
|
637
621
|
}
|
|
638
|
-
cleanFixInfo.insertText = fixInfo.insertText;
|
|
639
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
|
+
});
|
|
640
635
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
lineNumber,
|
|
644
|
-
"
|
|
645
|
-
"ruleNames": rule.names,
|
|
646
|
-
"ruleDescription": rule.description,
|
|
647
|
-
"ruleInformation": information ? information.href : null,
|
|
648
|
-
"errorDetail": errorInfo.detail || null,
|
|
649
|
-
"errorContext": errorInfo.context || null,
|
|
650
|
-
"errorRange": errorInfo.range ? [ ...errorInfo.range ] : null,
|
|
651
|
-
"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}`
|
|
652
640
|
});
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
// Asynchronous rule, ensure it returns a Promise
|
|
662
|
-
const ruleFunctionPromise =
|
|
663
|
-
Promise.resolve().then(invokeRuleFunction);
|
|
664
|
-
return handleRuleFailures ?
|
|
665
|
-
ruleFunctionPromise.catch(catchCallsOnError) :
|
|
666
|
-
ruleFunctionPromise;
|
|
667
|
-
}
|
|
668
|
-
// Synchronous rule
|
|
669
|
-
try {
|
|
670
|
-
invokeRuleFunction();
|
|
671
|
-
} catch (error) {
|
|
672
|
-
if (handleRuleFailures) {
|
|
673
|
-
catchCallsOnError(error);
|
|
674
|
-
} else {
|
|
675
|
-
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;
|
|
676
649
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
));
|
|
687
|
-
if (resultVersion < 3) {
|
|
688
|
-
// Remove fixInfo and multiple errors for the same rule and line number
|
|
689
|
-
const noPrevious = {
|
|
690
|
-
"ruleName": null,
|
|
691
|
-
"lineNumber": -1
|
|
692
|
-
};
|
|
693
|
-
results = results.filter((error, index, array) => {
|
|
694
|
-
delete error.fixInfo;
|
|
695
|
-
const previous = array[index - 1] || noPrevious;
|
|
696
|
-
return (
|
|
697
|
-
(error.ruleName !== previous.ruleName) ||
|
|
698
|
-
(error.lineNumber !== previous.lineNumber)
|
|
699
|
-
);
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
|
-
if (resultVersion === 0) {
|
|
703
|
-
// Return a dictionary of rule->[line numbers]
|
|
704
|
-
const dictionary = {};
|
|
705
|
-
for (const error of results) {
|
|
706
|
-
const ruleLines = dictionary[error.ruleName] || [];
|
|
707
|
-
ruleLines.push(error.lineNumber);
|
|
708
|
-
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
|
+
}
|
|
709
659
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
//
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
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
|
+
});
|
|
717
682
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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
|
+
}
|
|
722
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();
|
|
723
728
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
} else {
|
|
743
|
-
callbackSuccess();
|
|
744
|
-
}
|
|
745
|
-
} catch (error) {
|
|
746
|
-
callbackError(error);
|
|
747
|
-
} finally {
|
|
748
|
-
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);
|
|
749
747
|
}
|
|
750
748
|
}
|
|
751
749
|
|
|
@@ -753,12 +751,11 @@ function lintContent(
|
|
|
753
751
|
* Lints a file containing Markdown content.
|
|
754
752
|
*
|
|
755
753
|
* @param {Rule[]} ruleList List of rules.
|
|
756
|
-
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
|
757
|
-
* names.
|
|
754
|
+
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule names.
|
|
758
755
|
* @param {string} file Path of file to lint.
|
|
759
|
-
* @param {
|
|
756
|
+
* @param {MarkdownItFactory} markdownItFactory Function to create a markdown-it parser.
|
|
760
757
|
* @param {Configuration} config Configuration object.
|
|
761
|
-
* @param {ConfigurationParser[] |
|
|
758
|
+
* @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
|
|
762
759
|
* @param {RegExp | null} frontMatter Regular expression for front matter.
|
|
763
760
|
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
|
|
764
761
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
@@ -772,7 +769,7 @@ function lintFile(
|
|
|
772
769
|
ruleList,
|
|
773
770
|
aliasToRuleNames,
|
|
774
771
|
file,
|
|
775
|
-
|
|
772
|
+
markdownItFactory,
|
|
776
773
|
config,
|
|
777
774
|
configParsers,
|
|
778
775
|
frontMatter,
|
|
@@ -792,13 +789,14 @@ function lintFile(
|
|
|
792
789
|
aliasToRuleNames,
|
|
793
790
|
file,
|
|
794
791
|
content,
|
|
795
|
-
|
|
792
|
+
markdownItFactory,
|
|
796
793
|
config,
|
|
797
794
|
configParsers,
|
|
798
795
|
frontMatter,
|
|
799
796
|
handleRuleFailures,
|
|
800
797
|
noInlineConfig,
|
|
801
798
|
resultVersion,
|
|
799
|
+
synchronous,
|
|
802
800
|
callback
|
|
803
801
|
);
|
|
804
802
|
}
|
|
@@ -850,7 +848,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
850
848
|
const strings = options.strings || {};
|
|
851
849
|
const stringsKeys = Object.keys(strings);
|
|
852
850
|
const config = options.config || { "default": true };
|
|
853
|
-
const configParsers = options.configParsers ||
|
|
851
|
+
const configParsers = options.configParsers || undefined;
|
|
854
852
|
const frontMatter = (options.frontMatter === undefined) ?
|
|
855
853
|
helpers.frontMatterRe :
|
|
856
854
|
options.frontMatter;
|
|
@@ -859,7 +857,9 @@ function lintInput(options, synchronous, callback) {
|
|
|
859
857
|
const resultVersion = (options.resultVersion === undefined) ?
|
|
860
858
|
3 :
|
|
861
859
|
options.resultVersion;
|
|
862
|
-
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."); });
|
|
863
863
|
const fs = options.fs || nodeFs;
|
|
864
864
|
const aliasToRuleNames = mapAliasToRuleNames(ruleList);
|
|
865
865
|
const results = newResults(ruleList);
|
|
@@ -891,7 +891,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
891
891
|
ruleList,
|
|
892
892
|
aliasToRuleNames,
|
|
893
893
|
currentItem,
|
|
894
|
-
|
|
894
|
+
markdownItFactory,
|
|
895
895
|
config,
|
|
896
896
|
configParsers,
|
|
897
897
|
frontMatter,
|
|
@@ -910,13 +910,14 @@ function lintInput(options, synchronous, callback) {
|
|
|
910
910
|
aliasToRuleNames,
|
|
911
911
|
currentItem,
|
|
912
912
|
strings[currentItem] || "",
|
|
913
|
-
|
|
913
|
+
markdownItFactory,
|
|
914
914
|
config,
|
|
915
915
|
configParsers,
|
|
916
916
|
frontMatter,
|
|
917
917
|
handleRuleFailures,
|
|
918
918
|
noInlineConfig,
|
|
919
919
|
resultVersion,
|
|
920
|
+
synchronous,
|
|
920
921
|
lintWorkerCallback
|
|
921
922
|
);
|
|
922
923
|
} else if (concurrency === 0) {
|
|
@@ -1010,10 +1011,10 @@ function resolveConfigExtends(configFile, referenceId, fs, callback) {
|
|
|
1010
1011
|
if (err) {
|
|
1011
1012
|
// Not a file, try require.resolve
|
|
1012
1013
|
try {
|
|
1013
|
-
return callback(
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
)
|
|
1014
|
+
return callback(
|
|
1015
|
+
null,
|
|
1016
|
+
resolveModule(referenceId, [ configFileDirname ])
|
|
1017
|
+
);
|
|
1017
1018
|
} catch {
|
|
1018
1019
|
// Unable to resolve, use resolvedExtendsFile
|
|
1019
1020
|
}
|
|
@@ -1041,10 +1042,7 @@ function resolveConfigExtendsSync(configFile, referenceId, fs) {
|
|
|
1041
1042
|
// Not a file, try require.resolve
|
|
1042
1043
|
}
|
|
1043
1044
|
try {
|
|
1044
|
-
return
|
|
1045
|
-
referenceId,
|
|
1046
|
-
{ "paths": [ configFileDirname ] }
|
|
1047
|
-
);
|
|
1045
|
+
return resolveModule(referenceId, [ configFileDirname ]);
|
|
1048
1046
|
} catch {
|
|
1049
1047
|
// Unable to resolve, return resolvedExtendsFile
|
|
1050
1048
|
}
|
|
@@ -1196,6 +1194,7 @@ export function readConfigSync(file, parsers, fs) {
|
|
|
1196
1194
|
// Try to parse file
|
|
1197
1195
|
const { config, message } = parseConfiguration(file, content, parsers);
|
|
1198
1196
|
if (!config) {
|
|
1197
|
+
// @ts-ignore
|
|
1199
1198
|
throw new Error(message);
|
|
1200
1199
|
}
|
|
1201
1200
|
// Extend configuration
|
|
@@ -1475,6 +1474,29 @@ export function getVersion() {
|
|
|
1475
1474
|
* @property {RuleFunction} function Rule implementation.
|
|
1476
1475
|
*/
|
|
1477
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
|
+
|
|
1478
1500
|
/**
|
|
1479
1501
|
* Configuration options.
|
|
1480
1502
|
*
|
|
@@ -1486,7 +1508,7 @@ export function getVersion() {
|
|
|
1486
1508
|
* @property {RegExp | null} [frontMatter] Front matter pattern.
|
|
1487
1509
|
* @property {Object} [fs] File system implementation.
|
|
1488
1510
|
* @property {boolean} [handleRuleFailures] True to catch exceptions.
|
|
1489
|
-
* @property {
|
|
1511
|
+
* @property {MarkdownItFactory} [markdownItFactory] Function to create a markdown-it parser.
|
|
1490
1512
|
* @property {boolean} [noInlineConfig] True to ignore HTML directives.
|
|
1491
1513
|
* @property {number} [resultVersion] Results object version.
|
|
1492
1514
|
* @property {Object.<string, string>} [strings] Strings to lint.
|
|
@@ -1503,7 +1525,7 @@ export function getVersion() {
|
|
|
1503
1525
|
*
|
|
1504
1526
|
* @callback ToStringCallback
|
|
1505
1527
|
* @param {boolean} [ruleAliases] True to use rule aliases.
|
|
1506
|
-
* @returns {string}
|
|
1528
|
+
* @returns {string} Pretty-printed results.
|
|
1507
1529
|
*/
|
|
1508
1530
|
|
|
1509
1531
|
/**
|