markdownlint 0.41.0 → 0.42.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 +24 -0
- package/CONTRIBUTING.md +62 -77
- package/README.md +7 -7
- package/doc/CustomRules.md +6 -4
- package/doc/Rules.md +87 -36
- package/doc/md010.md +0 -1
- package/doc/md024.md +1 -1
- package/doc/md029.md +11 -1
- package/doc/md043.md +47 -9
- package/doc/md051.md +19 -19
- package/doc/md055.md +6 -3
- package/doc/md060.md +3 -2
- package/helpers/README.md +2 -2
- package/helpers/helpers.cjs +23 -7
- package/helpers/micromark-helpers.cjs +8 -12
- package/helpers/package.json +1 -5
- package/helpers/shared.cjs +1 -1
- package/lib/cache.mjs +1 -0
- package/lib/configuration-strict.d.ts +124 -112
- package/lib/constants.mjs +1 -1
- package/lib/defer-require.cjs +1 -0
- package/lib/exports.d.mts +29 -1
- package/lib/markdownit.cjs +8 -1
- package/lib/markdownlint.d.mts +337 -195
- package/lib/markdownlint.mjs +83 -87
- package/lib/md004.mjs +1 -0
- package/lib/md005.mjs +1 -1
- package/lib/md007.mjs +2 -2
- package/lib/md009.mjs +2 -3
- package/lib/md010.mjs +1 -1
- package/lib/md011.mjs +10 -7
- package/lib/md012.mjs +1 -1
- package/lib/md013.mjs +13 -9
- package/lib/md018.mjs +1 -1
- package/lib/md019-md021.mjs +1 -0
- package/lib/md020.mjs +1 -1
- package/lib/md022.mjs +6 -6
- package/lib/md024.mjs +1 -1
- package/lib/md025.mjs +1 -1
- package/lib/md027.mjs +1 -1
- package/lib/md028.mjs +1 -2
- package/lib/md029.mjs +34 -23
- package/lib/md030.mjs +4 -4
- package/lib/md031.mjs +1 -1
- package/lib/md032.mjs +5 -4
- package/lib/md033.mjs +1 -1
- package/lib/md034.mjs +1 -1
- package/lib/md037.mjs +1 -2
- package/lib/md039.mjs +5 -1
- package/lib/md041.mjs +1 -1
- package/lib/md043.mjs +98 -44
- package/lib/md044.mjs +2 -2
- package/lib/md048.mjs +1 -3
- package/lib/md051.mjs +3 -3
- package/lib/md055.mjs +11 -1
- package/lib/md056.mjs +1 -1
- package/lib/md058.mjs +1 -1
- package/lib/md060.mjs +24 -4
- package/lib/micromark-parse.mjs +3 -2
- package/lib/resolve-module.cjs +3 -2
- package/lib/resolve-module.d.cts +15 -10
- package/package.json +33 -34
- package/schema/.markdownlint.jsonc +54 -54
- package/schema/.markdownlint.yaml +76 -76
- package/schema/markdownlint-config-schema-strict.json +126 -118
- package/schema/markdownlint-config-schema.json +126 -118
package/lib/markdownlint.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// @ts-ignore
|
|
4
4
|
import { fs as nodeFs, os, path } from "#node-imports";
|
|
5
|
+
import { RE2JS } from "re2js";
|
|
5
6
|
import { initialize as cacheInitialize } from "./cache.mjs";
|
|
6
7
|
import { version } from "./constants.mjs";
|
|
7
8
|
import { requireMarkdownItCjs } from "./defer-require.cjs";
|
|
@@ -76,7 +77,7 @@ function validateRuleList(ruleList, synchronous) {
|
|
|
76
77
|
) {
|
|
77
78
|
result = newError("asynchronous", rule.asynchronous);
|
|
78
79
|
}
|
|
79
|
-
if (!result && rule.asynchronous
|
|
80
|
+
if (!result && synchronous && rule.asynchronous) {
|
|
80
81
|
result = new Error(
|
|
81
82
|
"Custom rule " + rule.names.join("/") + " at index " + customIndex +
|
|
82
83
|
" is asynchronous and can not be used in a synchronous context."
|
|
@@ -93,6 +94,7 @@ function validateRuleList(ruleList, synchronous) {
|
|
|
93
94
|
}
|
|
94
95
|
for (const tag of rule.tags) {
|
|
95
96
|
const tagUpper = tag.toUpperCase();
|
|
97
|
+
// eslint-disable-next-line unicorn/no-computed-property-existence-check
|
|
96
98
|
if (!result && allIds[tagUpper]) {
|
|
97
99
|
result = new Error("Tag '" + tag + "' of custom rule at index " +
|
|
98
100
|
customIndex + " is already used as a name.");
|
|
@@ -116,14 +118,15 @@ function validateRuleList(ruleList, synchronous) {
|
|
|
116
118
|
* Remove front matter (if present at beginning of content).
|
|
117
119
|
*
|
|
118
120
|
* @param {string} content Markdown content.
|
|
119
|
-
* @param {
|
|
121
|
+
* @param {RE2JS | null} frontMatterRe2 Regular expression to match front matter.
|
|
120
122
|
* @returns {RemoveFrontMatterResult} Trimmed content and front matter lines.
|
|
121
123
|
*/
|
|
122
|
-
function removeFrontMatter(content,
|
|
124
|
+
function removeFrontMatter(content, frontMatterRe2) {
|
|
123
125
|
/** @type {string[]} */
|
|
124
126
|
let frontMatterLines = [];
|
|
125
|
-
if (
|
|
126
|
-
const frontMatterMatch =
|
|
127
|
+
if (frontMatterRe2) {
|
|
128
|
+
const frontMatterMatch = frontMatterRe2.exec(content);
|
|
129
|
+
// @ts-ignore
|
|
127
130
|
if (frontMatterMatch && !frontMatterMatch.index) {
|
|
128
131
|
const contentMatched = frontMatterMatch[0];
|
|
129
132
|
content = content.slice(contentMatched.length);
|
|
@@ -198,7 +201,7 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
|
198
201
|
let ruleDefaultEnable = true;
|
|
199
202
|
/** @type {"error" | "warning"} */
|
|
200
203
|
let ruleDefaultSeverity = "error";
|
|
201
|
-
// eslint-disable-next-line unicorn/no-unused-
|
|
204
|
+
// eslint-disable-next-line unicorn/no-unused-builtin-method-return
|
|
202
205
|
Object.entries(config).every(([ key, value ]) => {
|
|
203
206
|
if (key.toUpperCase() === "DEFAULT") {
|
|
204
207
|
ruleDefaultEnable = !!value;
|
|
@@ -235,7 +238,7 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
|
235
238
|
if (value instanceof Object) {
|
|
236
239
|
/** @type {{ enabled?: boolean, severity?: "error" | "warning" }} */
|
|
237
240
|
const valueObject = value;
|
|
238
|
-
enabled = (valueObject.enabled === undefined)
|
|
241
|
+
enabled = (valueObject.enabled === undefined) || !!valueObject.enabled;
|
|
239
242
|
severity = (valueObject.severity === "warning") ? "warning" : "error";
|
|
240
243
|
effectiveValue = Object.fromEntries(
|
|
241
244
|
Object.entries(value).filter(
|
|
@@ -276,7 +279,6 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
|
276
279
|
*
|
|
277
280
|
* @param {Rule[]} ruleList List of rules.
|
|
278
281
|
* @param {string[]} lines List of content lines.
|
|
279
|
-
* @param {string[]} frontMatterLines List of front matter lines.
|
|
280
282
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
281
283
|
* @param {Configuration} config Configuration object.
|
|
282
284
|
* @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
|
|
@@ -286,7 +288,6 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
|
286
288
|
function getEnabledRulesPerLineNumber(
|
|
287
289
|
ruleList,
|
|
288
290
|
lines,
|
|
289
|
-
frontMatterLines,
|
|
290
291
|
noInlineConfig,
|
|
291
292
|
config,
|
|
292
293
|
configParsers,
|
|
@@ -296,7 +297,7 @@ function getEnabledRulesPerLineNumber(
|
|
|
296
297
|
let enabledRules = new Map();
|
|
297
298
|
/** @type {Map<string, boolean>} */
|
|
298
299
|
let capturedRules = enabledRules;
|
|
299
|
-
const enabledRulesPerLineNumber = new Array(1
|
|
300
|
+
const enabledRulesPerLineNumber = new Array(1);
|
|
300
301
|
// Helper functions
|
|
301
302
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
302
303
|
function handleInlineConfig(/** @type {string[]} */ input, /** @type {(act: string, par: string, ind: number) => void} */ forEachMatch, /** @type {(() => void)|undefined} */ forEachLine = undefined) {
|
|
@@ -372,8 +373,7 @@ function getEnabledRulesPerLineNumber(
|
|
|
372
373
|
const disableLine = (action === "DISABLE-LINE");
|
|
373
374
|
const disableNextLine = (action === "DISABLE-NEXT-LINE");
|
|
374
375
|
if (disableLine || disableNextLine) {
|
|
375
|
-
const nextLineNumber =
|
|
376
|
-
frontMatterLines.length + lineNumber + (disableNextLine ? 1 : 0);
|
|
376
|
+
const nextLineNumber = lineNumber + (disableNextLine ? 1 : 0);
|
|
377
377
|
enabledRulesPerLineNumber[nextLineNumber] =
|
|
378
378
|
applyEnableDisable(
|
|
379
379
|
action,
|
|
@@ -415,7 +415,7 @@ function getEnabledRulesPerLineNumber(
|
|
|
415
415
|
* @param {MarkdownItFactory} markdownItFactory Function to create a markdown-it parser.
|
|
416
416
|
* @param {Configuration} config Configuration object.
|
|
417
417
|
* @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
|
|
418
|
-
* @param {
|
|
418
|
+
* @param {RE2JS | null} frontMatterRe2 Regular expression for front matter.
|
|
419
419
|
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
|
|
420
420
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
421
421
|
* @param {boolean} synchronous Whether to execute synchronously.
|
|
@@ -430,7 +430,7 @@ function lintContent(
|
|
|
430
430
|
markdownItFactory,
|
|
431
431
|
config,
|
|
432
432
|
configParsers,
|
|
433
|
-
|
|
433
|
+
frontMatterRe2,
|
|
434
434
|
handleRuleFailures,
|
|
435
435
|
noInlineConfig,
|
|
436
436
|
synchronous,
|
|
@@ -439,17 +439,17 @@ function lintContent(
|
|
|
439
439
|
// eslint-disable-next-line jsdoc/reject-any-type
|
|
440
440
|
const callbackError = (/** @type {any} */ error) => callback(error instanceof Error ? error : new Error(error));
|
|
441
441
|
// Remove UTF-8 byte order marker (if present)
|
|
442
|
-
|
|
442
|
+
// eslint-disable-next-line unicorn/prefer-unicode-code-point-escapes
|
|
443
|
+
const fullContent = content.replace(/^\uFEFF/, "");
|
|
444
|
+
const fullContentLines = fullContent.split(helpers.newLineRe);
|
|
445
|
+
const fullContentNormalized = fullContentLines.join("\n");
|
|
443
446
|
// Remove front matter
|
|
444
|
-
const
|
|
445
|
-
const { frontMatterLines } = removeFrontMatterResult;
|
|
446
|
-
content = removeFrontMatterResult.content;
|
|
447
|
+
const { "content": markdownContentNormalized, frontMatterLines } = removeFrontMatter(fullContentNormalized, frontMatterRe2);
|
|
447
448
|
// Get enabled rules per line (with HTML comments present)
|
|
448
449
|
const { effectiveConfig, enabledRulesPerLineNumber, enabledRuleList, rulesSeverity } =
|
|
449
450
|
getEnabledRulesPerLineNumber(
|
|
450
451
|
ruleList,
|
|
451
|
-
|
|
452
|
-
frontMatterLines,
|
|
452
|
+
fullContentLines,
|
|
453
453
|
noInlineConfig,
|
|
454
454
|
config,
|
|
455
455
|
configParsers,
|
|
@@ -464,13 +464,12 @@ function lintContent(
|
|
|
464
464
|
const customRulesPresent = (ruleList.length !== rules.length);
|
|
465
465
|
// Parse content into parser tokens
|
|
466
466
|
const micromarkTokens = needMicromarkTokens ?
|
|
467
|
-
micromarkParse(
|
|
467
|
+
micromarkParse(markdownContentNormalized, { "freezeTokens": customRulesPresent }) :
|
|
468
468
|
[];
|
|
469
469
|
// Hide the content of HTML comments from rules
|
|
470
|
-
const
|
|
471
|
-
content = helpers.clearHtmlCommentText(content);
|
|
470
|
+
const markdownContentNormalizedSanitized = helpers.clearHtmlCommentText(markdownContentNormalized);
|
|
472
471
|
// Parse content into lines and get markdown-it tokens
|
|
473
|
-
const lines =
|
|
472
|
+
const lines = markdownContentNormalizedSanitized.split(helpers.newLineRe);
|
|
474
473
|
// Function to run after fetching markdown-it tokens (when needed)
|
|
475
474
|
const lintContentInternal = (/** @type {MarkdownItToken[]} */ markdownitTokens) => {
|
|
476
475
|
// Create (frozen) parameters for rules
|
|
@@ -537,37 +536,45 @@ function lintContent(
|
|
|
537
536
|
}
|
|
538
537
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
539
538
|
function onError(/** @type {RuleOnErrorInfo} */ errorInfo) {
|
|
540
|
-
if (!errorInfo
|
|
541
|
-
|
|
539
|
+
if (!errorInfo) {
|
|
540
|
+
throwError("onErrorInfo");
|
|
541
|
+
}
|
|
542
|
+
if ((errorInfo.frontMatter !== undefined) &&
|
|
543
|
+
!helpers.isBoolean(errorInfo.frontMatter)) {
|
|
544
|
+
throwError("frontMatter");
|
|
545
|
+
}
|
|
546
|
+
const useFrontMatter = Boolean(errorInfo.frontMatter);
|
|
547
|
+
const contentLines = useFrontMatter ? frontMatterLines : lines;
|
|
548
|
+
const lineOffset = useFrontMatter ? 0 : frontMatterLines.length;
|
|
549
|
+
if (!helpers.isNumber(errorInfo.lineNumber) ||
|
|
542
550
|
(errorInfo.lineNumber < 1) ||
|
|
543
|
-
(errorInfo.lineNumber >
|
|
551
|
+
(errorInfo.lineNumber > contentLines.length)) {
|
|
544
552
|
throwError("lineNumber");
|
|
545
553
|
}
|
|
546
|
-
const lineNumber = errorInfo.lineNumber +
|
|
554
|
+
const lineNumber = errorInfo.lineNumber + lineOffset;
|
|
547
555
|
if (!enabledRulesPerLineNumber[lineNumber].get(ruleName)) {
|
|
548
556
|
return;
|
|
549
557
|
}
|
|
550
|
-
if (errorInfo.detail &&
|
|
558
|
+
if ((errorInfo.detail !== undefined) &&
|
|
551
559
|
!helpers.isString(errorInfo.detail)) {
|
|
552
560
|
throwError("detail");
|
|
553
561
|
}
|
|
554
|
-
if (errorInfo.context &&
|
|
562
|
+
if ((errorInfo.context !== undefined) &&
|
|
555
563
|
!helpers.isString(errorInfo.context)) {
|
|
556
564
|
throwError("context");
|
|
557
565
|
}
|
|
558
|
-
if (errorInfo.information &&
|
|
566
|
+
if ((errorInfo.information !== undefined) &&
|
|
559
567
|
!helpers.isUrl(errorInfo.information)) {
|
|
560
568
|
throwError("information");
|
|
561
569
|
}
|
|
562
|
-
if (errorInfo.range &&
|
|
570
|
+
if ((errorInfo.range !== undefined) &&
|
|
563
571
|
(!Array.isArray(errorInfo.range) ||
|
|
564
572
|
(errorInfo.range.length !== 2) ||
|
|
565
573
|
!helpers.isNumber(errorInfo.range[0]) ||
|
|
566
574
|
(errorInfo.range[0] < 1) ||
|
|
567
575
|
!helpers.isNumber(errorInfo.range[1]) ||
|
|
568
576
|
(errorInfo.range[1] < 1) ||
|
|
569
|
-
((errorInfo.range[0] + errorInfo.range[1] - 1) >
|
|
570
|
-
lines[errorInfo.lineNumber - 1].length))) {
|
|
577
|
+
((errorInfo.range[0] + errorInfo.range[1] - 1) > contentLines[errorInfo.lineNumber - 1].length))) {
|
|
571
578
|
throwError("range");
|
|
572
579
|
}
|
|
573
580
|
const fixInfo = errorInfo.fixInfo;
|
|
@@ -579,18 +586,16 @@ function lintContent(
|
|
|
579
586
|
if (fixInfo.lineNumber !== undefined) {
|
|
580
587
|
if ((!helpers.isNumber(fixInfo.lineNumber) ||
|
|
581
588
|
(fixInfo.lineNumber < 1) ||
|
|
582
|
-
(fixInfo.lineNumber >
|
|
589
|
+
(fixInfo.lineNumber > contentLines.length))) {
|
|
583
590
|
throwError("fixInfo.lineNumber");
|
|
584
591
|
}
|
|
585
|
-
cleanFixInfo.lineNumber =
|
|
586
|
-
fixInfo.lineNumber + frontMatterLines.length;
|
|
592
|
+
cleanFixInfo.lineNumber = fixInfo.lineNumber + lineOffset;
|
|
587
593
|
}
|
|
588
594
|
const effectiveLineNumber = fixInfo.lineNumber || errorInfo.lineNumber;
|
|
589
595
|
if (fixInfo.editColumn !== undefined) {
|
|
590
596
|
if ((!helpers.isNumber(fixInfo.editColumn) ||
|
|
591
597
|
(fixInfo.editColumn < 1) ||
|
|
592
|
-
(fixInfo.editColumn >
|
|
593
|
-
lines[effectiveLineNumber - 1].length + 1))) {
|
|
598
|
+
(fixInfo.editColumn > contentLines[effectiveLineNumber - 1].length + 1))) {
|
|
594
599
|
throwError("fixInfo.editColumn");
|
|
595
600
|
}
|
|
596
601
|
cleanFixInfo.editColumn = fixInfo.editColumn;
|
|
@@ -598,8 +603,7 @@ function lintContent(
|
|
|
598
603
|
if (fixInfo.deleteCount !== undefined) {
|
|
599
604
|
if ((!helpers.isNumber(fixInfo.deleteCount) ||
|
|
600
605
|
(fixInfo.deleteCount < -1) ||
|
|
601
|
-
(fixInfo.deleteCount >
|
|
602
|
-
lines[effectiveLineNumber - 1].length))) {
|
|
606
|
+
(((fixInfo.editColumn ?? 1) + fixInfo.deleteCount - 1) > contentLines[effectiveLineNumber - 1].length))) {
|
|
603
607
|
throwError("fixInfo.deleteCount");
|
|
604
608
|
}
|
|
605
609
|
cleanFixInfo.deleteCount = fixInfo.deleteCount;
|
|
@@ -686,7 +690,7 @@ function lintContent(
|
|
|
686
690
|
if (!needMarkdownItTokens || synchronous) {
|
|
687
691
|
// Need/able to call into markdown-it and lintContentInternal synchronously
|
|
688
692
|
const markdownItTokens = needMarkdownItTokens ?
|
|
689
|
-
requireMarkdownItCjs().getMarkdownItTokens(markdownItFactory(),
|
|
693
|
+
requireMarkdownItCjs().getMarkdownItTokens(markdownItFactory(), markdownContentNormalized, lines) :
|
|
690
694
|
[];
|
|
691
695
|
lintContentInternal(markdownItTokens);
|
|
692
696
|
} else {
|
|
@@ -697,7 +701,7 @@ function lintContent(
|
|
|
697
701
|
// eslint-disable-next-line no-promise-executor-return
|
|
698
702
|
new Promise((resolve) => resolve(markdownItFactory()))
|
|
699
703
|
]).then(([ markdownitCjs, markdownIt ]) => {
|
|
700
|
-
const markdownItTokens = markdownitCjs.getMarkdownItTokens(markdownIt,
|
|
704
|
+
const markdownItTokens = markdownitCjs.getMarkdownItTokens(markdownIt, markdownContentNormalized, lines);
|
|
701
705
|
lintContentInternal(markdownItTokens);
|
|
702
706
|
}).catch(callbackError);
|
|
703
707
|
}
|
|
@@ -712,7 +716,7 @@ function lintContent(
|
|
|
712
716
|
* @param {MarkdownItFactory} markdownItFactory Function to create a markdown-it parser.
|
|
713
717
|
* @param {Configuration} config Configuration object.
|
|
714
718
|
* @param {ConfigurationParser[] | undefined} configParsers Configuration parsers.
|
|
715
|
-
* @param {
|
|
719
|
+
* @param {RE2JS | null} frontMatterRe2 Regular expression for front matter.
|
|
716
720
|
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
|
|
717
721
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
718
722
|
* @param {FsLike} fs File system implementation.
|
|
@@ -727,7 +731,7 @@ function lintFile(
|
|
|
727
731
|
markdownItFactory,
|
|
728
732
|
config,
|
|
729
733
|
configParsers,
|
|
730
|
-
|
|
734
|
+
frontMatterRe2,
|
|
731
735
|
handleRuleFailures,
|
|
732
736
|
noInlineConfig,
|
|
733
737
|
fs,
|
|
@@ -735,23 +739,22 @@ function lintFile(
|
|
|
735
739
|
callback) {
|
|
736
740
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
737
741
|
function lintContentWrapper(/** @type {Error | null} */ err, /** @type {string} */ content) {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
);
|
|
742
|
+
return err ?
|
|
743
|
+
callback(err) :
|
|
744
|
+
lintContent(
|
|
745
|
+
ruleList,
|
|
746
|
+
aliasToRuleNames,
|
|
747
|
+
file,
|
|
748
|
+
content,
|
|
749
|
+
markdownItFactory,
|
|
750
|
+
config,
|
|
751
|
+
configParsers,
|
|
752
|
+
frontMatterRe2,
|
|
753
|
+
handleRuleFailures,
|
|
754
|
+
noInlineConfig,
|
|
755
|
+
synchronous,
|
|
756
|
+
callback
|
|
757
|
+
);
|
|
755
758
|
}
|
|
756
759
|
// Make a/synchronous call to read file
|
|
757
760
|
if (synchronous) {
|
|
@@ -771,8 +774,8 @@ function lintFile(
|
|
|
771
774
|
*/
|
|
772
775
|
function lintInput(options, synchronous, callback) {
|
|
773
776
|
// Normalize inputs
|
|
774
|
-
options
|
|
775
|
-
callback
|
|
777
|
+
options ||= {};
|
|
778
|
+
callback ||= () => {};
|
|
776
779
|
/** @type {Rule[]} */
|
|
777
780
|
// @ts-ignore
|
|
778
781
|
const customRuleList =
|
|
@@ -805,9 +808,8 @@ function lintInput(options, synchronous, callback) {
|
|
|
805
808
|
const stringsKeys = Object.keys(strings);
|
|
806
809
|
const config = options.config || { "default": true };
|
|
807
810
|
const configParsers = options.configParsers || undefined;
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
options.frontMatter;
|
|
811
|
+
const frontMatterRe = (options.frontMatter === undefined) ? helpers.frontMatterRe : options.frontMatter;
|
|
812
|
+
const frontMatterRe2 = frontMatterRe && RE2JS.compile(RE2JS.translateRegExp(frontMatterRe));
|
|
811
813
|
const handleRuleFailures = !!options.handleRuleFailures;
|
|
812
814
|
const noInlineConfig = !!options.noInlineConfig;
|
|
813
815
|
const markdownItFactory =
|
|
@@ -850,7 +852,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
850
852
|
markdownItFactory,
|
|
851
853
|
config,
|
|
852
854
|
configParsers,
|
|
853
|
-
|
|
855
|
+
frontMatterRe2,
|
|
854
856
|
handleRuleFailures,
|
|
855
857
|
noInlineConfig,
|
|
856
858
|
fs,
|
|
@@ -868,7 +870,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
868
870
|
markdownItFactory,
|
|
869
871
|
config,
|
|
870
872
|
configParsers,
|
|
871
|
-
|
|
873
|
+
frontMatterRe2,
|
|
872
874
|
handleRuleFailures,
|
|
873
875
|
noInlineConfig,
|
|
874
876
|
synchronous,
|
|
@@ -1027,8 +1029,8 @@ function resolveConfigExtendsSync(configFile, referenceId, fs) {
|
|
|
1027
1029
|
*/
|
|
1028
1030
|
function extendConfig(config, file, parsers, fs, callback) {
|
|
1029
1031
|
const configExtends = config.extends;
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
+
return configExtends ?
|
|
1033
|
+
resolveConfigExtends(
|
|
1032
1034
|
file,
|
|
1033
1035
|
helpers.expandTildePath(configExtends, os),
|
|
1034
1036
|
fs,
|
|
@@ -1050,9 +1052,8 @@ function extendConfig(config, file, parsers, fs, callback) {
|
|
|
1050
1052
|
return callback(null, result);
|
|
1051
1053
|
}
|
|
1052
1054
|
)
|
|
1053
|
-
)
|
|
1054
|
-
|
|
1055
|
-
return callback(null, config);
|
|
1055
|
+
) :
|
|
1056
|
+
callback(null, config);
|
|
1056
1057
|
}
|
|
1057
1058
|
|
|
1058
1059
|
/**
|
|
@@ -1112,13 +1113,11 @@ export function readConfigAsync(file, parsers, fs, callback) {
|
|
|
1112
1113
|
// Try to parse file
|
|
1113
1114
|
// @ts-ignore
|
|
1114
1115
|
const { config, message } = parseConfiguration(file, content, parsers);
|
|
1115
|
-
|
|
1116
|
+
return config ?
|
|
1116
1117
|
// @ts-ignore
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
// @ts-ignore
|
|
1121
|
-
return extendConfig(config, file, parsers, fsLike, callback);
|
|
1118
|
+
extendConfig(config, file, parsers, fsLike, callback) :
|
|
1119
|
+
// @ts-ignore
|
|
1120
|
+
callback(new Error(message));
|
|
1122
1121
|
});
|
|
1123
1122
|
}
|
|
1124
1123
|
|
|
@@ -1209,6 +1208,7 @@ export function applyFix(line, fixInfo, lineEnding = "\n") {
|
|
|
1209
1208
|
const editIndex = editColumn - 1;
|
|
1210
1209
|
return (deleteCount === -1) ?
|
|
1211
1210
|
null :
|
|
1211
|
+
// eslint-disable-next-line unicorn/no-unsafe-string-replacement
|
|
1212
1212
|
line.slice(0, editIndex) + insertText.replace(/\n/g, lineEnding) + line.slice(editIndex + deleteCount);
|
|
1213
1213
|
}
|
|
1214
1214
|
|
|
@@ -1408,6 +1408,7 @@ export function getVersion() {
|
|
|
1408
1408
|
* Fix information for RuleOnError callback.
|
|
1409
1409
|
*
|
|
1410
1410
|
* @typedef {Object} RuleOnErrorInfo
|
|
1411
|
+
* @property {boolean} [frontMatter] True if applies to front matter.
|
|
1411
1412
|
* @property {number} lineNumber Line number (1-based).
|
|
1412
1413
|
* @property {string} [detail] Detail about the error.
|
|
1413
1414
|
* @property {string} [context] Context for the error.
|
|
@@ -1448,12 +1449,7 @@ export function getVersion() {
|
|
|
1448
1449
|
* @returns {MarkdownItBaseToken[]} Tokens.
|
|
1449
1450
|
*/
|
|
1450
1451
|
|
|
1451
|
-
/**
|
|
1452
|
-
* Instance of the markdown-it parser.
|
|
1453
|
-
*
|
|
1454
|
-
* @typedef MarkdownIt
|
|
1455
|
-
* @property {MarkdownItParse} parse Method to parse input.
|
|
1456
|
-
*/
|
|
1452
|
+
/** @typedef {import("markdown-it").MarkdownIt} MarkdownIt */
|
|
1457
1453
|
|
|
1458
1454
|
/**
|
|
1459
1455
|
* Gets an instance of the markdown-it parser. Any plugins should already have been loaded.
|
|
@@ -1487,7 +1483,7 @@ export function getVersion() {
|
|
|
1487
1483
|
/**
|
|
1488
1484
|
* Lint results.
|
|
1489
1485
|
*
|
|
1490
|
-
* @typedef {
|
|
1486
|
+
* @typedef {{[k: string]: LintError[]}} LintResults
|
|
1491
1487
|
*/
|
|
1492
1488
|
|
|
1493
1489
|
/**
|
package/lib/md004.mjs
CHANGED
|
@@ -40,6 +40,7 @@ export default {
|
|
|
40
40
|
for (const listItemMarker of listItemMarkers) {
|
|
41
41
|
const itemStyle = markerToStyle(listItemMarker.text);
|
|
42
42
|
if (style === "sublist") {
|
|
43
|
+
// eslint-disable-next-line unicorn/no-computed-property-existence-check
|
|
43
44
|
if (!nestingStyles[nesting]) {
|
|
44
45
|
nestingStyles[nesting] =
|
|
45
46
|
(itemStyle === nestingStyles[nesting - 1]) ?
|
package/lib/md005.mjs
CHANGED
|
@@ -34,7 +34,7 @@ export default {
|
|
|
34
34
|
} else {
|
|
35
35
|
const markerLength = listItemPrefix.text.trim().length;
|
|
36
36
|
const actualEnd = listItemPrefix.startColumn + markerLength - 1;
|
|
37
|
-
expectedEnd
|
|
37
|
+
expectedEnd ||= actualEnd;
|
|
38
38
|
if ((expectedIndent !== actualIndent) || endMatching) {
|
|
39
39
|
if (expectedEnd === actualEnd) {
|
|
40
40
|
endMatching = true;
|
package/lib/md007.mjs
CHANGED
|
@@ -18,9 +18,9 @@ export default {
|
|
|
18
18
|
"tags": [ "bullet", "ul", "indentation" ],
|
|
19
19
|
"parser": "micromark",
|
|
20
20
|
"function": function MD007(params, onError) {
|
|
21
|
-
const indent = Number(params.config.indent
|
|
21
|
+
const indent = Number(params.config.indent ?? 2);
|
|
22
22
|
const startIndented = !!params.config.start_indented;
|
|
23
|
-
const startIndent = Number(params.config.start_indent
|
|
23
|
+
const startIndent = Number(params.config.start_indent ?? indent);
|
|
24
24
|
const unorderedListNesting = new Map();
|
|
25
25
|
let lastBlockQuotePrefix = null;
|
|
26
26
|
const tokens = filterByTypesCached(unorderedListTypes);
|
package/lib/md009.mjs
CHANGED
|
@@ -11,10 +11,9 @@ export default {
|
|
|
11
11
|
"tags": [ "whitespace" ],
|
|
12
12
|
"parser": "micromark",
|
|
13
13
|
"function": function MD009(params, onError) {
|
|
14
|
-
|
|
15
|
-
brSpaces = Number((brSpaces === undefined) ? 2 : brSpaces);
|
|
14
|
+
const brSpaces = Number(params.config.br_spaces ?? 2);
|
|
16
15
|
const codeBlocks = params.config.code_blocks;
|
|
17
|
-
const includeCode = (codeBlocks
|
|
16
|
+
const includeCode = (codeBlocks !== undefined) && !!codeBlocks;
|
|
18
17
|
const listItemEmptyLines = !!params.config.list_item_empty_lines;
|
|
19
18
|
const strict = !!params.config.strict;
|
|
20
19
|
const codeBlockLineNumbers = new Set();
|
package/lib/md010.mjs
CHANGED
|
@@ -14,7 +14,7 @@ export default {
|
|
|
14
14
|
"parser": "micromark",
|
|
15
15
|
"function": function MD010(params, onError) {
|
|
16
16
|
const codeBlocks = params.config.code_blocks;
|
|
17
|
-
const includeCode = (codeBlocks === undefined)
|
|
17
|
+
const includeCode = (codeBlocks === undefined) || !!codeBlocks;
|
|
18
18
|
const ignoreCodeLanguages = new Set(
|
|
19
19
|
(params.config.ignore_code_languages || [])
|
|
20
20
|
.map((/** @type {void} */ language) => String(language).toLowerCase())
|
package/lib/md011.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import { re } from "re2js";
|
|
3
4
|
import { addError, hasOverlap } from "../helpers/helpers.cjs";
|
|
4
5
|
import { addRangeToSet } from "../helpers/micromark-helpers.cjs";
|
|
5
6
|
import { filterByTypesCached } from "./cache.mjs";
|
|
6
7
|
|
|
7
|
-
/** @typedef {import("micromark-extension-math")} */
|
|
8
|
+
/** @typedef {typeof import("micromark-extension-math")} _ambient_ */
|
|
8
9
|
|
|
9
|
-
const reversedLinkRe =
|
|
10
|
+
const reversedLinkRe = re`(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\]`;
|
|
10
11
|
|
|
11
12
|
/** @type {import("markdownlint").Rule} */
|
|
12
13
|
export default {
|
|
@@ -16,22 +17,24 @@ export default {
|
|
|
16
17
|
"parser": "micromark",
|
|
17
18
|
"function": function MD011(params, onError) {
|
|
18
19
|
const ignoreBlockLineNumbers = new Set();
|
|
19
|
-
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "mathFlow" ])) {
|
|
20
|
+
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow", "mathFlow" ])) {
|
|
20
21
|
addRangeToSet(ignoreBlockLineNumbers, ignoreBlock.startLine, ignoreBlock.endLine);
|
|
21
22
|
}
|
|
22
23
|
const ignoreTexts = filterByTypesCached([ "codeText", "mathText" ]);
|
|
23
24
|
for (const [ lineIndex, line ] of params.lines.entries()) {
|
|
24
25
|
const lineNumber = lineIndex + 1;
|
|
25
26
|
if (!ignoreBlockLineNumbers.has(lineNumber)) {
|
|
26
|
-
|
|
27
|
-
while ((match = reversedLinkRe.exec(line)) !== null) {
|
|
27
|
+
for (const match of reversedLinkRe.matchAll(line)) {
|
|
28
28
|
const [ reversedLink, preChar, linkText, linkDestination ] = match;
|
|
29
|
+
const matchIndex = match.index ?? 0;
|
|
30
|
+
const matchLength = match[0].length;
|
|
29
31
|
if (
|
|
32
|
+
(line[matchIndex + matchLength] !== "(") &&
|
|
30
33
|
!linkText.endsWith("\\") &&
|
|
31
34
|
!linkDestination.endsWith("\\")
|
|
32
35
|
) {
|
|
33
|
-
const column =
|
|
34
|
-
const length =
|
|
36
|
+
const column = matchIndex + preChar.length + 1;
|
|
37
|
+
const length = matchLength - preChar.length;
|
|
35
38
|
/** @type {import("../helpers/helpers.cjs").FileRange} */
|
|
36
39
|
const range = { "startLine": lineNumber, "startColumn": column, "endLine": lineNumber, "endColumn": column + length - 1 };
|
|
37
40
|
if (!ignoreTexts.some((ignoreText) => hasOverlap(ignoreText, range))) {
|
package/lib/md012.mjs
CHANGED
|
@@ -11,7 +11,7 @@ export default {
|
|
|
11
11
|
"tags": [ "whitespace", "blank_lines" ],
|
|
12
12
|
"parser": "micromark",
|
|
13
13
|
"function": function MD012(params, onError) {
|
|
14
|
-
const maximum = Number(params.config.maximum
|
|
14
|
+
const maximum = Number(params.config.maximum ?? 1);
|
|
15
15
|
const { lines } = params;
|
|
16
16
|
const codeBlockLineNumbers = new Set();
|
|
17
17
|
for (const codeBlock of filterByTypesCached([ "codeFenced", "codeIndented" ])) {
|
package/lib/md013.mjs
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import { re } from "re2js";
|
|
3
4
|
import { addErrorDetailIf } from "../helpers/helpers.cjs";
|
|
4
5
|
import { filterByTypesCached, getReferenceLinkImageData } from "./cache.mjs";
|
|
5
6
|
import { addRangeToSet, getDescendantsByType } from "../helpers/micromark-helpers.cjs";
|
|
6
7
|
|
|
8
|
+
/** @typedef {typeof import("micromark-extension-gfm-autolink-literal")} _ambient_ */
|
|
9
|
+
/** @typedef {typeof import("micromark-extension-gfm-table")} _ambient_2_ */
|
|
10
|
+
|
|
7
11
|
// Regular expression for a line that is not wrappable
|
|
8
12
|
const notWrappableRe = /^(?:[#>\s]*\s)?\S*$/;
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
// Regular expression for the last run of non-whitespace
|
|
15
|
+
const lastRunRe = re`\S*$`;
|
|
12
16
|
|
|
13
17
|
/** @type {import("markdownlint").Rule} */
|
|
14
18
|
export default {
|
|
@@ -17,17 +21,17 @@ export default {
|
|
|
17
21
|
"tags": [ "line_length" ],
|
|
18
22
|
"parser": "micromark",
|
|
19
23
|
"function": function MD013(params, onError) {
|
|
20
|
-
const lineLength = Number(params.config.line_length
|
|
21
|
-
const headingLineLength = Number(params.config.heading_line_length
|
|
22
|
-
const codeLineLength = Number(params.config.code_block_line_length
|
|
24
|
+
const lineLength = Number(params.config.line_length ?? 80);
|
|
25
|
+
const headingLineLength = Number(params.config.heading_line_length ?? lineLength);
|
|
26
|
+
const codeLineLength = Number(params.config.code_block_line_length ?? lineLength);
|
|
23
27
|
const strict = !!params.config.strict;
|
|
24
28
|
const stern = !!params.config.stern;
|
|
25
29
|
const codeBlocks = params.config.code_blocks;
|
|
26
|
-
const includeCodeBlocks = (codeBlocks === undefined)
|
|
30
|
+
const includeCodeBlocks = (codeBlocks === undefined) || !!codeBlocks;
|
|
27
31
|
const tables = params.config.tables;
|
|
28
|
-
const includeTables = (tables === undefined)
|
|
32
|
+
const includeTables = (tables === undefined) || !!tables;
|
|
29
33
|
const headings = params.config.headings;
|
|
30
|
-
const includeHeadings = (headings === undefined)
|
|
34
|
+
const includeHeadings = (headings === undefined) || !!headings;
|
|
31
35
|
const headingLineNumbers = new Set();
|
|
32
36
|
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
|
|
33
37
|
addRangeToSet(headingLineNumbers, heading.startLine, heading.endLine);
|
|
@@ -66,7 +70,7 @@ export default {
|
|
|
66
70
|
const maxLength = inCode ? codeLineLength : (isHeading ? headingLineLength : lineLength);
|
|
67
71
|
// If not strict/stern, the last run of non-whitespace is allowed to go
|
|
68
72
|
// beyond the limit as long as it begins within the limit
|
|
69
|
-
const text = (strict || stern) ? line : line.
|
|
73
|
+
const text = (strict || stern) ? line : lastRunRe.matcher(line).replaceFirst("#");
|
|
70
74
|
if ((maxLength > 0) &&
|
|
71
75
|
(includeCodeBlocks || !inCode) &&
|
|
72
76
|
(includeTables || !inTable) &&
|
package/lib/md018.mjs
CHANGED
|
@@ -13,7 +13,7 @@ export default {
|
|
|
13
13
|
"function": function MD018(params, onError) {
|
|
14
14
|
const { lines } = params;
|
|
15
15
|
const ignoreBlockLineNumbers = new Set();
|
|
16
|
-
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow" ])) {
|
|
16
|
+
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow", "mathFlow" ])) {
|
|
17
17
|
addRangeToSet(ignoreBlockLineNumbers, ignoreBlock.startLine, ignoreBlock.endLine);
|
|
18
18
|
}
|
|
19
19
|
for (const [ lineIndex, line ] of lines.entries()) {
|
package/lib/md019-md021.mjs
CHANGED
|
@@ -16,6 +16,7 @@ function validateHeadingSpaces(onError, heading, delta) {
|
|
|
16
16
|
const { children, startLine, text } = heading;
|
|
17
17
|
let index = (delta > 0) ? 0 : (children.length - 1);
|
|
18
18
|
while (
|
|
19
|
+
// eslint-disable-next-line unicorn/no-computed-property-existence-check
|
|
19
20
|
children[index] &&
|
|
20
21
|
(children[index].type !== "atxHeadingSequence")
|
|
21
22
|
) {
|
package/lib/md020.mjs
CHANGED
|
@@ -13,7 +13,7 @@ export default {
|
|
|
13
13
|
"function": function MD020(params, onError) {
|
|
14
14
|
const { lines } = params;
|
|
15
15
|
const ignoreBlockLineNumbers = new Set();
|
|
16
|
-
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow" ])) {
|
|
16
|
+
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow", "mathFlow" ])) {
|
|
17
17
|
addRangeToSet(ignoreBlockLineNumbers, ignoreBlock.startLine, ignoreBlock.endLine);
|
|
18
18
|
}
|
|
19
19
|
for (const [ lineIndex, line ] of lines.entries()) {
|