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.
@@ -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
- * Parse the content of a configuration file.
273
+ * Result object for getEnabledRulesPerLineNumber.
273
274
  *
274
- * @param {string} name Name of the configuration file.
275
- * @param {string} content Configuration content.
276
- * @param {ConfigurationParser[] | null} [parsers] Parsing function(s).
277
- * @returns {Object} Configuration object and error message.
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[] | null} configParsers Configuration parsers.
289
+ * @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
313
290
  * @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
314
291
  * names.
315
- * @returns {Object} Effective configuration and enabled rules per line number.
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 {Plugin[]} markdownItPlugins Additional plugins.
426
+ * @param {MarkdownItFactory} markdownItFactory Function to create a markdown-it parser.
451
427
  * @param {Configuration} config Configuration object.
452
- * @param {ConfigurationParser[] | null} configParsers Configuration parsers.
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
- markdownItPlugins,
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 = micromarkParse(
496
- content,
497
- { "freezeTokens": customRulesPresent }
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
- const markdownitTokens = needMarkdownItTokens ?
505
- requireMarkdownItCjs().getMarkdownItTokens(markdownItPlugins, preClearedContent, lines) :
506
- [];
507
- // Create (frozen) parameters for rules
508
- /** @type {MarkdownParsers} */
509
- // @ts-ignore
510
- const parsersMarkdownIt = Object.freeze({
511
- "markdownit": Object.freeze({
512
- "tokens": markdownitTokens
513
- })
514
- });
515
- /** @type {MarkdownParsers} */
516
- // @ts-ignore
517
- const parsersMicromark = Object.freeze({
518
- "micromark": Object.freeze({
519
- "tokens": micromarkTokens
520
- })
521
- });
522
- /** @type {MarkdownParsers} */
523
- // @ts-ignore
524
- const parsersNone = Object.freeze({});
525
- const paramsBase = {
526
- name,
527
- version,
528
- "lines": Object.freeze(lines),
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
- // eslint-disable-next-line jsdoc/require-jsdoc
562
- function throwError(property) {
563
- throw new Error(
564
- `Value of '${property}' passed to onError by '${ruleName}' is incorrect for '${name}'.`);
565
- }
566
- // eslint-disable-next-line jsdoc/require-jsdoc
567
- function onError(errorInfo) {
568
- if (!errorInfo ||
569
- !helpers.isNumber(errorInfo.lineNumber) ||
570
- (errorInfo.lineNumber < 1) ||
571
- (errorInfo.lineNumber > lines.length)) {
572
- throwError("lineNumber");
573
- }
574
- const lineNumber = errorInfo.lineNumber + frontMatterLines.length;
575
- if (!enabledRulesPerLineNumber[lineNumber][ruleName]) {
576
- return;
577
- }
578
- if (errorInfo.detail &&
579
- !helpers.isString(errorInfo.detail)) {
580
- throwError("detail");
581
- }
582
- if (errorInfo.context &&
583
- !helpers.isString(errorInfo.context)) {
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
- if (errorInfo.range &&
591
- (!Array.isArray(errorInfo.range) ||
592
- (errorInfo.range.length !== 2) ||
593
- !helpers.isNumber(errorInfo.range[0]) ||
594
- (errorInfo.range[0] < 1) ||
595
- !helpers.isNumber(errorInfo.range[1]) ||
596
- (errorInfo.range[1] < 1) ||
597
- ((errorInfo.range[0] + errorInfo.range[1] - 1) >
598
- lines[errorInfo.lineNumber - 1].length))) {
599
- throwError("range");
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
- const fixInfo = errorInfo.fixInfo;
602
- const cleanFixInfo = {};
603
- if (fixInfo) {
604
- if (!helpers.isObject(fixInfo)) {
605
- throwError("fixInfo");
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
- if (fixInfo.lineNumber !== undefined) {
608
- if ((!helpers.isNumber(fixInfo.lineNumber) ||
609
- (fixInfo.lineNumber < 1) ||
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
- const effectiveLineNumber = fixInfo.lineNumber || errorInfo.lineNumber;
617
- if (fixInfo.editColumn !== undefined) {
618
- if ((!helpers.isNumber(fixInfo.editColumn) ||
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 (fixInfo.deleteCount !== undefined) {
627
- if ((!helpers.isNumber(fixInfo.deleteCount) ||
628
- (fixInfo.deleteCount < -1) ||
629
- (fixInfo.deleteCount >
630
- lines[effectiveLineNumber - 1].length))) {
631
- throwError("fixInfo.deleteCount");
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 (fixInfo.insertText !== undefined) {
636
- if (!helpers.isString(fixInfo.insertText)) {
637
- throwError("fixInfo.insertText");
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
- const information = errorInfo.information || rule.information;
643
- results.push({
644
- lineNumber,
645
- "ruleName": rule.names[0],
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
- // Call (possibly external) rule function to report errors
656
- const catchCallsOnError = (error) => onError({
657
- "lineNumber": 1,
658
- "detail": `This rule threw an exception: ${error.message || error}`
659
- });
660
- const invokeRuleFunction = () => rule.function(params, onError);
661
- if (rule.asynchronous) {
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
- return null;
680
- };
681
- // eslint-disable-next-line jsdoc/require-jsdoc
682
- function formatResults() {
683
- // Sort results by rule name by line number
684
- results.sort((a, b) => (
685
- a.ruleName.localeCompare(b.ruleName) ||
686
- a.lineNumber - b.lineNumber
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
- // @ts-ignore
712
- results = dictionary;
713
- } else if (resultVersion === 1) {
714
- // Use ruleAlias instead of ruleNames
715
- for (const error of results) {
716
- error.ruleAlias = error.ruleNames[1] || error.ruleName;
717
- delete error.ruleNames;
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
- } else {
720
- // resultVersion 2 or 3: Remove unwanted ruleName
721
- for (const error of results) {
722
- delete error.ruleName;
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
- return results;
726
- }
727
- // Run all rules
728
- const ruleListAsync = enabledRuleList.filter((rule) => rule.asynchronous);
729
- const ruleListSync = enabledRuleList.filter((rule) => !rule.asynchronous);
730
- const ruleListAsyncFirst = [
731
- ...ruleListAsync,
732
- ...ruleListSync
733
- ];
734
- const callbackSuccess = () => callback(null, formatResults());
735
- const callbackError =
736
- (error) => callback(error instanceof Error ? error : new Error(error));
737
- try {
738
- const ruleResults = ruleListAsyncFirst.map(forRule);
739
- if (ruleListAsync.length > 0) {
740
- Promise.all(ruleResults.slice(0, ruleListAsync.length))
741
- .then(callbackSuccess)
742
- .catch(callbackError);
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 {Plugin[]} markdownItPlugins Additional plugins.
756
+ * @param {MarkdownItFactory} markdownItFactory Function to create a markdown-it parser.
761
757
  * @param {Configuration} config Configuration object.
762
- * @param {ConfigurationParser[] | null} configParsers Configuration parsers.
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
- markdownItPlugins,
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
- markdownItPlugins,
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 || null;
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 markdownItPlugins = options.markdownItPlugins || [];
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
- markdownItPlugins,
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
- markdownItPlugins,
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 {Plugin[]} [markdownItPlugins] Additional plugins.
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
  /**