markdownlint 0.34.0 → 0.36.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 +21 -0
- package/README.md +97 -45
- package/demo/markdownlint-browser.js +2186 -2164
- package/doc/CustomRules.md +66 -255
- package/doc/Rules.md +102 -29
- package/doc/md003.md +2 -2
- package/doc/md004.md +4 -3
- package/doc/md034.md +5 -0
- package/doc/md035.md +3 -8
- package/doc/md044.md +10 -0
- package/doc/md046.md +2 -2
- package/doc/md048.md +3 -2
- package/doc/md049.md +4 -3
- package/doc/md050.md +4 -4
- package/doc/md051.md +11 -3
- package/doc/md055.md +3 -2
- package/doc/md058.md +48 -0
- package/helpers/README.md +2 -45
- package/helpers/helpers.js +115 -607
- package/helpers/micromark-helpers.cjs +296 -0
- package/helpers/micromark-parse.cjs +313 -0
- package/helpers/package.json +5 -2
- package/helpers/shared.js +6 -0
- package/helpers/test.cjs +28 -0
- package/lib/cache.js +62 -13
- package/lib/configuration-strict.d.ts +1199 -0
- package/lib/configuration.d.ts +3 -1173
- package/lib/constants.js +3 -2
- package/lib/markdownit.cjs +172 -0
- package/lib/markdownlint.d.ts +103 -56
- package/lib/markdownlint.js +180 -120
- package/lib/md001.js +15 -9
- package/lib/md003.js +14 -9
- package/lib/md004.js +55 -48
- package/lib/md005.js +4 -13
- package/lib/md007.js +7 -11
- package/lib/md009.js +50 -44
- package/lib/md010.js +52 -33
- package/lib/md011.js +32 -23
- package/lib/md012.js +17 -9
- package/lib/md013.js +44 -38
- package/lib/md014.js +31 -38
- package/lib/md018.js +17 -9
- package/lib/md019-md021.js +80 -0
- package/lib/md020.js +14 -8
- package/lib/md022.js +13 -29
- package/lib/md023.js +22 -23
- package/lib/md024.js +12 -9
- package/lib/md025.js +15 -10
- package/lib/md026.js +2 -7
- package/lib/md027.js +7 -12
- package/lib/md028.js +3 -8
- package/lib/md029.js +40 -34
- package/lib/md030.js +4 -10
- package/lib/md031.js +45 -23
- package/lib/md032.js +33 -31
- package/lib/md033.js +3 -8
- package/lib/md034.js +21 -34
- package/lib/md035.js +2 -7
- package/lib/md036.js +19 -24
- package/lib/md037.js +3 -8
- package/lib/md038.js +11 -22
- package/lib/md039.js +64 -54
- package/lib/md040.js +13 -20
- package/lib/md041.js +5 -5
- package/lib/md042.js +44 -38
- package/lib/md043.js +20 -14
- package/lib/md044.js +34 -36
- package/lib/md045.js +5 -10
- package/lib/md046.js +2 -8
- package/lib/md048.js +30 -23
- package/lib/md049-md050.js +22 -12
- package/lib/md051.js +20 -28
- package/lib/md052.js +2 -2
- package/lib/md053.js +7 -6
- package/lib/md054.js +16 -23
- package/lib/md055.js +41 -49
- package/lib/md056.js +32 -33
- package/lib/md058.js +58 -0
- package/lib/rules.js +11 -4
- package/package.json +23 -20
- package/schema/.markdownlint.jsonc +58 -52
- package/schema/.markdownlint.yaml +56 -51
- package/schema/ValidatingConfiguration.md +6 -8
- package/schema/markdownlint-config-schema-strict.json +1841 -0
- package/schema/markdownlint-config-schema.json +141 -109
- package/style/prettier.json +1 -0
- package/helpers/micromark.cjs +0 -445
- package/lib/md019.js +0 -42
- package/lib/md021.js +0 -65
package/lib/markdownlint.js
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
const path = require("node:path");
|
|
6
6
|
const { promisify } = require("node:util");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
// const { deprecatedRuleNames } = require("./constants");
|
|
7
|
+
const micromark = require("../helpers/micromark-parse.cjs");
|
|
8
|
+
const { version } = require("./constants");
|
|
10
9
|
const rules = require("./rules");
|
|
11
10
|
const helpers = require("../helpers");
|
|
12
11
|
const cache = require("./cache");
|
|
@@ -32,7 +31,7 @@ function validateRuleList(ruleList, synchronous) {
|
|
|
32
31
|
const allIds = {};
|
|
33
32
|
for (const [ index, rule ] of ruleList.entries()) {
|
|
34
33
|
const customIndex = index - rules.length;
|
|
35
|
-
// eslint-disable-next-line
|
|
34
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
36
35
|
function newError(property, value) {
|
|
37
36
|
return new Error(
|
|
38
37
|
`Property '${property}' of custom rule at index ${customIndex} is incorrect: '${value}'.`);
|
|
@@ -59,7 +58,7 @@ function validateRuleList(ruleList, synchronous) {
|
|
|
59
58
|
!result &&
|
|
60
59
|
(rule.parser !== undefined) &&
|
|
61
60
|
(rule.parser !== "markdownit") &&
|
|
62
|
-
|
|
61
|
+
(rule.parser !== "micromark") &&
|
|
63
62
|
(rule.parser !== "none")
|
|
64
63
|
) {
|
|
65
64
|
result = newError("parser", rule.parser);
|
|
@@ -198,83 +197,6 @@ function removeFrontMatter(content, frontMatter) {
|
|
|
198
197
|
};
|
|
199
198
|
}
|
|
200
199
|
|
|
201
|
-
/**
|
|
202
|
-
* Freeze all freeze-able members of a token and its children.
|
|
203
|
-
*
|
|
204
|
-
* @param {MarkdownItToken} token A markdown-it token.
|
|
205
|
-
* @returns {void}
|
|
206
|
-
*/
|
|
207
|
-
function freezeToken(token) {
|
|
208
|
-
if (token.attrs) {
|
|
209
|
-
for (const attr of token.attrs) {
|
|
210
|
-
Object.freeze(attr);
|
|
211
|
-
}
|
|
212
|
-
Object.freeze(token.attrs);
|
|
213
|
-
}
|
|
214
|
-
if (token.children) {
|
|
215
|
-
for (const child of token.children) {
|
|
216
|
-
freezeToken(child);
|
|
217
|
-
}
|
|
218
|
-
Object.freeze(token.children);
|
|
219
|
-
}
|
|
220
|
-
if (token.map) {
|
|
221
|
-
Object.freeze(token.map);
|
|
222
|
-
}
|
|
223
|
-
Object.freeze(token);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Annotate tokens with line/lineNumber and freeze them.
|
|
228
|
-
*
|
|
229
|
-
* @param {MarkdownItToken[]} tokens Array of markdown-it tokens.
|
|
230
|
-
* @param {string[]} lines Lines of Markdown content.
|
|
231
|
-
* @returns {void}
|
|
232
|
-
*/
|
|
233
|
-
function annotateAndFreezeTokens(tokens, lines) {
|
|
234
|
-
let trMap = null;
|
|
235
|
-
for (const token of tokens) {
|
|
236
|
-
// Provide missing maps for table content
|
|
237
|
-
if (token.type === "tr_open") {
|
|
238
|
-
trMap = token.map;
|
|
239
|
-
} else if (token.type === "tr_close") {
|
|
240
|
-
trMap = null;
|
|
241
|
-
}
|
|
242
|
-
if (!token.map && trMap) {
|
|
243
|
-
token.map = [ ...trMap ];
|
|
244
|
-
}
|
|
245
|
-
// Update token metadata
|
|
246
|
-
if (token.map) {
|
|
247
|
-
token.line = lines[token.map[0]];
|
|
248
|
-
token.lineNumber = token.map[0] + 1;
|
|
249
|
-
// Trim bottom of token to exclude whitespace lines
|
|
250
|
-
while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
|
|
251
|
-
token.map[1]--;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
// Annotate children with lineNumber
|
|
255
|
-
if (token.children) {
|
|
256
|
-
const codeSpanExtraLines = [];
|
|
257
|
-
if (token.children.some((child) => child.type === "code_inline")) {
|
|
258
|
-
helpers.forEachInlineCodeSpan(token.content, (code) => {
|
|
259
|
-
codeSpanExtraLines.push(code.split(helpers.newLineRe).length - 1);
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
let lineNumber = token.lineNumber;
|
|
263
|
-
for (const child of token.children) {
|
|
264
|
-
child.lineNumber = lineNumber;
|
|
265
|
-
child.line = lines[lineNumber - 1];
|
|
266
|
-
if ((child.type === "softbreak") || (child.type === "hardbreak")) {
|
|
267
|
-
lineNumber++;
|
|
268
|
-
} else if (child.type === "code_inline") {
|
|
269
|
-
lineNumber += codeSpanExtraLines.shift();
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
freezeToken(token);
|
|
274
|
-
}
|
|
275
|
-
Object.freeze(tokens);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
200
|
/**
|
|
279
201
|
* Map rule names/tags to canonical rule name.
|
|
280
202
|
*
|
|
@@ -507,10 +429,18 @@ function getEnabledRulesPerLineNumber(
|
|
|
507
429
|
handleInlineConfig(lines, enableDisableFile);
|
|
508
430
|
handleInlineConfig(lines, captureRestoreEnableDisable, updateLineState);
|
|
509
431
|
handleInlineConfig(lines, disableLineNextLine);
|
|
432
|
+
// Create the list of rules that are used at least once
|
|
433
|
+
const enabledRuleList = [];
|
|
434
|
+
for (const [ index, ruleName ] of allRuleNames.entries()) {
|
|
435
|
+
if (enabledRulesPerLineNumber.some((enabledRulesForLine) => enabledRulesForLine[ruleName])) {
|
|
436
|
+
enabledRuleList.push(ruleList[index]);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
510
439
|
// Return results
|
|
511
440
|
return {
|
|
512
441
|
effectiveConfig,
|
|
513
|
-
enabledRulesPerLineNumber
|
|
442
|
+
enabledRulesPerLineNumber,
|
|
443
|
+
enabledRuleList
|
|
514
444
|
};
|
|
515
445
|
}
|
|
516
446
|
|
|
@@ -522,7 +452,7 @@ function getEnabledRulesPerLineNumber(
|
|
|
522
452
|
* names.
|
|
523
453
|
* @param {string} name Identifier for the content.
|
|
524
454
|
* @param {string} content Markdown content.
|
|
525
|
-
* @param {
|
|
455
|
+
* @param {Plugin[]} markdownItPlugins Additional plugins.
|
|
526
456
|
* @param {Configuration} config Configuration object.
|
|
527
457
|
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
|
|
528
458
|
* @param {RegExp | null} frontMatter Regular expression for front matter.
|
|
@@ -537,7 +467,7 @@ function lintContent(
|
|
|
537
467
|
aliasToRuleNames,
|
|
538
468
|
name,
|
|
539
469
|
content,
|
|
540
|
-
|
|
470
|
+
markdownItPlugins,
|
|
541
471
|
config,
|
|
542
472
|
configParsers,
|
|
543
473
|
frontMatter,
|
|
@@ -552,7 +482,7 @@ function lintContent(
|
|
|
552
482
|
const { frontMatterLines } = removeFrontMatterResult;
|
|
553
483
|
content = removeFrontMatterResult.content;
|
|
554
484
|
// Get enabled rules per line (with HTML comments present)
|
|
555
|
-
const { effectiveConfig, enabledRulesPerLineNumber } =
|
|
485
|
+
const { effectiveConfig, enabledRulesPerLineNumber, enabledRuleList } =
|
|
556
486
|
getEnabledRulesPerLineNumber(
|
|
557
487
|
ruleList,
|
|
558
488
|
content.split(helpers.newLineRe),
|
|
@@ -562,14 +492,23 @@ function lintContent(
|
|
|
562
492
|
configParsers,
|
|
563
493
|
aliasToRuleNames
|
|
564
494
|
);
|
|
495
|
+
const needMarkdownItTokens = enabledRuleList.some(
|
|
496
|
+
(rule) => (rule.parser === "markdownit") || (rule.parser === undefined)
|
|
497
|
+
);
|
|
498
|
+
const customRulesPresent = (ruleList.length !== rules.length);
|
|
565
499
|
// Parse content into parser tokens
|
|
566
|
-
const
|
|
567
|
-
|
|
500
|
+
const micromarkTokens = micromark.parse(
|
|
501
|
+
content,
|
|
502
|
+
{ "freezeTokens": customRulesPresent }
|
|
503
|
+
);
|
|
568
504
|
// Hide the content of HTML comments from rules
|
|
505
|
+
const preClearedContent = content;
|
|
569
506
|
content = helpers.clearHtmlCommentText(content);
|
|
570
|
-
// Parse content into lines and
|
|
507
|
+
// Parse content into lines and get markdown-it tokens
|
|
571
508
|
const lines = content.split(helpers.newLineRe);
|
|
572
|
-
|
|
509
|
+
const markdownitTokens = needMarkdownItTokens ?
|
|
510
|
+
require("./markdownit.cjs").getMarkdownItTokens(markdownItPlugins, preClearedContent, lines) :
|
|
511
|
+
[];
|
|
573
512
|
// Create (frozen) parameters for rules
|
|
574
513
|
/** @type {MarkdownParsers} */
|
|
575
514
|
// @ts-ignore
|
|
@@ -590,23 +529,14 @@ function lintContent(
|
|
|
590
529
|
const parsersNone = Object.freeze({});
|
|
591
530
|
const paramsBase = {
|
|
592
531
|
name,
|
|
593
|
-
|
|
532
|
+
version,
|
|
594
533
|
"lines": Object.freeze(lines),
|
|
595
534
|
"frontMatterLines": Object.freeze(frontMatterLines)
|
|
596
535
|
};
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const flattenedLists =
|
|
602
|
-
helpers.flattenLists(markdownitTokens);
|
|
603
|
-
const referenceLinkImageData =
|
|
604
|
-
helpers.getReferenceLinkImageData(micromarkTokens);
|
|
605
|
-
cache.set({
|
|
606
|
-
codeBlockAndSpanRanges,
|
|
607
|
-
flattenedLists,
|
|
608
|
-
lineMetadata,
|
|
609
|
-
referenceLinkImageData
|
|
536
|
+
cache.initialize({
|
|
537
|
+
...paramsBase,
|
|
538
|
+
"parsers": parsersMicromark,
|
|
539
|
+
"config": null
|
|
610
540
|
});
|
|
611
541
|
// Function to run for each rule
|
|
612
542
|
let results = [];
|
|
@@ -800,8 +730,8 @@ function lintContent(
|
|
|
800
730
|
return results;
|
|
801
731
|
}
|
|
802
732
|
// Run all rules
|
|
803
|
-
const ruleListAsync =
|
|
804
|
-
const ruleListSync =
|
|
733
|
+
const ruleListAsync = enabledRuleList.filter((rule) => rule.asynchronous);
|
|
734
|
+
const ruleListSync = enabledRuleList.filter((rule) => !rule.asynchronous);
|
|
805
735
|
const ruleListAsyncFirst = [
|
|
806
736
|
...ruleListAsync,
|
|
807
737
|
...ruleListSync
|
|
@@ -821,7 +751,7 @@ function lintContent(
|
|
|
821
751
|
} catch (error) {
|
|
822
752
|
callbackError(error);
|
|
823
753
|
} finally {
|
|
824
|
-
cache.
|
|
754
|
+
cache.initialize();
|
|
825
755
|
}
|
|
826
756
|
}
|
|
827
757
|
|
|
@@ -832,7 +762,7 @@ function lintContent(
|
|
|
832
762
|
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
|
833
763
|
* names.
|
|
834
764
|
* @param {string} file Path of file to lint.
|
|
835
|
-
* @param {
|
|
765
|
+
* @param {Plugin[]} markdownItPlugins Additional plugins.
|
|
836
766
|
* @param {Configuration} config Configuration object.
|
|
837
767
|
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
|
|
838
768
|
* @param {RegExp | null} frontMatter Regular expression for front matter.
|
|
@@ -848,7 +778,7 @@ function lintFile(
|
|
|
848
778
|
ruleList,
|
|
849
779
|
aliasToRuleNames,
|
|
850
780
|
file,
|
|
851
|
-
|
|
781
|
+
markdownItPlugins,
|
|
852
782
|
config,
|
|
853
783
|
configParsers,
|
|
854
784
|
frontMatter,
|
|
@@ -868,7 +798,7 @@ function lintFile(
|
|
|
868
798
|
aliasToRuleNames,
|
|
869
799
|
file,
|
|
870
800
|
content,
|
|
871
|
-
|
|
801
|
+
markdownItPlugins,
|
|
872
802
|
config,
|
|
873
803
|
configParsers,
|
|
874
804
|
frontMatter,
|
|
@@ -928,17 +858,14 @@ function lintInput(options, synchronous, callback) {
|
|
|
928
858
|
const config = options.config || { "default": true };
|
|
929
859
|
const configParsers = options.configParsers || null;
|
|
930
860
|
const frontMatter = (options.frontMatter === undefined) ?
|
|
931
|
-
helpers.frontMatterRe :
|
|
861
|
+
helpers.frontMatterRe :
|
|
862
|
+
options.frontMatter;
|
|
932
863
|
const handleRuleFailures = !!options.handleRuleFailures;
|
|
933
864
|
const noInlineConfig = !!options.noInlineConfig;
|
|
934
865
|
const resultVersion = (options.resultVersion === undefined) ?
|
|
935
|
-
3 :
|
|
936
|
-
|
|
866
|
+
3 :
|
|
867
|
+
options.resultVersion;
|
|
937
868
|
const markdownItPlugins = options.markdownItPlugins || [];
|
|
938
|
-
for (const plugin of markdownItPlugins) {
|
|
939
|
-
// @ts-ignore
|
|
940
|
-
md.use(...plugin);
|
|
941
|
-
}
|
|
942
869
|
const fs = options.fs || require("node:fs");
|
|
943
870
|
const aliasToRuleNames = mapAliasToRuleNames(ruleList);
|
|
944
871
|
const results = newResults(ruleList);
|
|
@@ -970,7 +897,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
970
897
|
ruleList,
|
|
971
898
|
aliasToRuleNames,
|
|
972
899
|
currentItem,
|
|
973
|
-
|
|
900
|
+
markdownItPlugins,
|
|
974
901
|
config,
|
|
975
902
|
configParsers,
|
|
976
903
|
frontMatter,
|
|
@@ -989,7 +916,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
989
916
|
aliasToRuleNames,
|
|
990
917
|
currentItem,
|
|
991
918
|
strings[currentItem] || "",
|
|
992
|
-
|
|
919
|
+
markdownItPlugins,
|
|
993
920
|
config,
|
|
994
921
|
configParsers,
|
|
995
922
|
frontMatter,
|
|
@@ -1281,13 +1208,126 @@ function readConfigSync(file, parsers, fs) {
|
|
|
1281
1208
|
return config;
|
|
1282
1209
|
}
|
|
1283
1210
|
|
|
1211
|
+
/**
|
|
1212
|
+
* Normalizes the fields of a RuleOnErrorFixInfo instance.
|
|
1213
|
+
*
|
|
1214
|
+
* @param {RuleOnErrorFixInfo} fixInfo RuleOnErrorFixInfo instance.
|
|
1215
|
+
* @param {number} [lineNumber] Line number.
|
|
1216
|
+
* @returns {RuleOnErrorFixInfoNormalized} Normalized RuleOnErrorFixInfo instance.
|
|
1217
|
+
*/
|
|
1218
|
+
function normalizeFixInfo(fixInfo, lineNumber = 0) {
|
|
1219
|
+
return {
|
|
1220
|
+
"lineNumber": fixInfo.lineNumber || lineNumber,
|
|
1221
|
+
"editColumn": fixInfo.editColumn || 1,
|
|
1222
|
+
"deleteCount": fixInfo.deleteCount || 0,
|
|
1223
|
+
"insertText": fixInfo.insertText || ""
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
/**
|
|
1228
|
+
* Applies the specified fix to a Markdown content line.
|
|
1229
|
+
*
|
|
1230
|
+
* @param {string} line Line of Markdown content.
|
|
1231
|
+
* @param {RuleOnErrorFixInfo} fixInfo RuleOnErrorFixInfo instance.
|
|
1232
|
+
* @param {string} [lineEnding] Line ending to use.
|
|
1233
|
+
* @returns {string | null} Fixed content or null if deleted.
|
|
1234
|
+
*/
|
|
1235
|
+
function applyFix(line, fixInfo, lineEnding = "\n") {
|
|
1236
|
+
const { editColumn, deleteCount, insertText } = normalizeFixInfo(fixInfo);
|
|
1237
|
+
const editIndex = editColumn - 1;
|
|
1238
|
+
return (deleteCount === -1) ?
|
|
1239
|
+
null :
|
|
1240
|
+
line.slice(0, editIndex) + insertText.replace(/\n/g, lineEnding) + line.slice(editIndex + deleteCount);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/**
|
|
1244
|
+
* Applies as many of the specified fixes as possible to Markdown content.
|
|
1245
|
+
*
|
|
1246
|
+
* @param {string} input Lines of Markdown content.
|
|
1247
|
+
* @param {RuleOnErrorInfo[]} errors RuleOnErrorInfo instances.
|
|
1248
|
+
* @returns {string} Fixed content.
|
|
1249
|
+
*/
|
|
1250
|
+
function applyFixes(input, errors) {
|
|
1251
|
+
const lineEnding = helpers.getPreferredLineEnding(input, require("node:os"));
|
|
1252
|
+
const lines = input.split(helpers.newLineRe);
|
|
1253
|
+
// Normalize fixInfo objects
|
|
1254
|
+
let fixInfos = errors
|
|
1255
|
+
.filter((error) => error.fixInfo)
|
|
1256
|
+
// @ts-ignore
|
|
1257
|
+
.map((error) => normalizeFixInfo(error.fixInfo, error.lineNumber));
|
|
1258
|
+
// Sort bottom-to-top, line-deletes last, right-to-left, long-to-short
|
|
1259
|
+
fixInfos.sort((a, b) => {
|
|
1260
|
+
const aDeletingLine = (a.deleteCount === -1);
|
|
1261
|
+
const bDeletingLine = (b.deleteCount === -1);
|
|
1262
|
+
return (
|
|
1263
|
+
(b.lineNumber - a.lineNumber) ||
|
|
1264
|
+
(aDeletingLine ? 1 : (bDeletingLine ? -1 : 0)) ||
|
|
1265
|
+
(b.editColumn - a.editColumn) ||
|
|
1266
|
+
(b.insertText.length - a.insertText.length)
|
|
1267
|
+
);
|
|
1268
|
+
});
|
|
1269
|
+
// Remove duplicate entries (needed for following collapse step)
|
|
1270
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
1271
|
+
/** @type RuleOnErrorFixInfo */
|
|
1272
|
+
let lastFixInfo = {};
|
|
1273
|
+
fixInfos = fixInfos.filter((fixInfo) => {
|
|
1274
|
+
const unique = (
|
|
1275
|
+
(fixInfo.lineNumber !== lastFixInfo.lineNumber) ||
|
|
1276
|
+
(fixInfo.editColumn !== lastFixInfo.editColumn) ||
|
|
1277
|
+
(fixInfo.deleteCount !== lastFixInfo.deleteCount) ||
|
|
1278
|
+
(fixInfo.insertText !== lastFixInfo.insertText)
|
|
1279
|
+
);
|
|
1280
|
+
lastFixInfo = fixInfo;
|
|
1281
|
+
return unique;
|
|
1282
|
+
});
|
|
1283
|
+
// Collapse insert/no-delete and no-insert/delete for same line/column
|
|
1284
|
+
lastFixInfo = {
|
|
1285
|
+
"lineNumber": -1
|
|
1286
|
+
};
|
|
1287
|
+
for (const fixInfo of fixInfos) {
|
|
1288
|
+
if (
|
|
1289
|
+
(fixInfo.lineNumber === lastFixInfo.lineNumber) &&
|
|
1290
|
+
(fixInfo.editColumn === lastFixInfo.editColumn) &&
|
|
1291
|
+
!fixInfo.insertText &&
|
|
1292
|
+
(fixInfo.deleteCount > 0) &&
|
|
1293
|
+
lastFixInfo.insertText &&
|
|
1294
|
+
!lastFixInfo.deleteCount) {
|
|
1295
|
+
fixInfo.insertText = lastFixInfo.insertText;
|
|
1296
|
+
lastFixInfo.lineNumber = 0;
|
|
1297
|
+
}
|
|
1298
|
+
lastFixInfo = fixInfo;
|
|
1299
|
+
}
|
|
1300
|
+
fixInfos = fixInfos.filter((fixInfo) => fixInfo.lineNumber);
|
|
1301
|
+
// Apply all (remaining/updated) fixes
|
|
1302
|
+
let lastLineIndex = -1;
|
|
1303
|
+
let lastEditIndex = -1;
|
|
1304
|
+
for (const fixInfo of fixInfos) {
|
|
1305
|
+
const { lineNumber, editColumn, deleteCount } = fixInfo;
|
|
1306
|
+
const lineIndex = lineNumber - 1;
|
|
1307
|
+
const editIndex = editColumn - 1;
|
|
1308
|
+
if (
|
|
1309
|
+
(lineIndex !== lastLineIndex) ||
|
|
1310
|
+
(deleteCount === -1) ||
|
|
1311
|
+
((editIndex + deleteCount) <=
|
|
1312
|
+
(lastEditIndex - ((deleteCount > 0) ? 0 : 1)))
|
|
1313
|
+
) {
|
|
1314
|
+
// @ts-ignore
|
|
1315
|
+
lines[lineIndex] = applyFix(lines[lineIndex], fixInfo, lineEnding);
|
|
1316
|
+
}
|
|
1317
|
+
lastLineIndex = lineIndex;
|
|
1318
|
+
lastEditIndex = editIndex;
|
|
1319
|
+
}
|
|
1320
|
+
// Return corrected input
|
|
1321
|
+
return lines.filter((line) => line !== null).join(lineEnding);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1284
1324
|
/**
|
|
1285
1325
|
* Gets the (semantic) version of the library.
|
|
1286
1326
|
*
|
|
1287
1327
|
* @returns {string} SemVer string.
|
|
1288
1328
|
*/
|
|
1289
1329
|
function getVersion() {
|
|
1290
|
-
return
|
|
1330
|
+
return version;
|
|
1291
1331
|
}
|
|
1292
1332
|
|
|
1293
1333
|
// Export a/synchronous/Promise APIs
|
|
@@ -1300,6 +1340,8 @@ markdownlint.promises = {
|
|
|
1300
1340
|
"extendConfig": extendConfigPromise,
|
|
1301
1341
|
"readConfig": readConfigPromise
|
|
1302
1342
|
};
|
|
1343
|
+
markdownlint.applyFix = applyFix;
|
|
1344
|
+
markdownlint.applyFixes = applyFixes;
|
|
1303
1345
|
module.exports = markdownlint;
|
|
1304
1346
|
|
|
1305
1347
|
// Type declarations
|
|
@@ -1324,6 +1366,7 @@ module.exports = markdownlint;
|
|
|
1324
1366
|
* @property {readonly string[]} lines File/string lines.
|
|
1325
1367
|
* @property {readonly string[]} frontMatterLines Front matter lines.
|
|
1326
1368
|
* @property {RuleConfiguration} config Rule configuration.
|
|
1369
|
+
* @property {string} version Version of the markdownlint library.
|
|
1327
1370
|
*/
|
|
1328
1371
|
|
|
1329
1372
|
/* eslint-enable jsdoc/valid-types */
|
|
@@ -1417,6 +1460,16 @@ module.exports = markdownlint;
|
|
|
1417
1460
|
* @property {string} [insertText] Text to insert (after deleting).
|
|
1418
1461
|
*/
|
|
1419
1462
|
|
|
1463
|
+
/**
|
|
1464
|
+
* RuleOnErrorInfo with all optional properties present.
|
|
1465
|
+
*
|
|
1466
|
+
* @typedef {Object} RuleOnErrorFixInfoNormalized
|
|
1467
|
+
* @property {number} lineNumber Line number (1-based).
|
|
1468
|
+
* @property {number} editColumn Column of the fix (1-based).
|
|
1469
|
+
* @property {number} deleteCount Count of characters to delete.
|
|
1470
|
+
* @property {string} insertText Text to insert (after deleting).
|
|
1471
|
+
*/
|
|
1472
|
+
|
|
1420
1473
|
/**
|
|
1421
1474
|
* Rule definition.
|
|
1422
1475
|
*
|
|
@@ -1517,6 +1570,13 @@ module.exports = markdownlint;
|
|
|
1517
1570
|
* @typedef {import("./configuration").Configuration} Configuration
|
|
1518
1571
|
*/
|
|
1519
1572
|
|
|
1573
|
+
/**
|
|
1574
|
+
* Configuration object for linting rules strictly. For the JSON schema, see
|
|
1575
|
+
* {@link ../schema/markdownlint-config-schema-strict.json}.
|
|
1576
|
+
*
|
|
1577
|
+
* @typedef {import("./configuration-strict").ConfigurationStrict} ConfigurationStrict
|
|
1578
|
+
*/
|
|
1579
|
+
|
|
1520
1580
|
/**
|
|
1521
1581
|
* Rule configuration object.
|
|
1522
1582
|
*
|
package/lib/md001.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf
|
|
5
|
+
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
+
const { getHeadingLevel } = require("../helpers/micromark-helpers.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
6
8
|
|
|
7
9
|
// eslint-disable-next-line jsdoc/valid-types
|
|
8
10
|
/** @type import("./markdownlint").Rule */
|
|
@@ -10,16 +12,20 @@ module.exports = {
|
|
|
10
12
|
"names": [ "MD001", "heading-increment" ],
|
|
11
13
|
"description": "Heading levels should only increment by one level at a time",
|
|
12
14
|
"tags": [ "headings" ],
|
|
13
|
-
"parser": "
|
|
15
|
+
"parser": "micromark",
|
|
14
16
|
"function": function MD001(params, onError) {
|
|
15
|
-
let prevLevel =
|
|
16
|
-
|
|
17
|
-
const level =
|
|
18
|
-
if (
|
|
19
|
-
addErrorDetailIf(
|
|
20
|
-
|
|
17
|
+
let prevLevel = Number.MAX_SAFE_INTEGER;
|
|
18
|
+
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
|
|
19
|
+
const level = getHeadingLevel(heading);
|
|
20
|
+
if (level > prevLevel) {
|
|
21
|
+
addErrorDetailIf(
|
|
22
|
+
onError,
|
|
23
|
+
heading.startLine,
|
|
24
|
+
`h${prevLevel + 1}`,
|
|
25
|
+
`h${level}`
|
|
26
|
+
);
|
|
21
27
|
}
|
|
22
28
|
prevLevel = level;
|
|
23
|
-
}
|
|
29
|
+
}
|
|
24
30
|
}
|
|
25
31
|
};
|
package/lib/md003.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf
|
|
6
|
-
|
|
5
|
+
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
+
const { getHeadingLevel, getHeadingStyle } = require("../helpers/micromark-helpers.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
7
8
|
|
|
8
9
|
// eslint-disable-next-line jsdoc/valid-types
|
|
9
10
|
/** @type import("./markdownlint").Rule */
|
|
@@ -11,16 +12,16 @@ module.exports = {
|
|
|
11
12
|
"names": [ "MD003", "heading-style" ],
|
|
12
13
|
"description": "Heading style",
|
|
13
14
|
"tags": [ "headings" ],
|
|
14
|
-
"parser": "
|
|
15
|
+
"parser": "micromark",
|
|
15
16
|
"function": function MD003(params, onError) {
|
|
16
17
|
let style = String(params.config.style || "consistent");
|
|
17
|
-
|
|
18
|
-
const styleForToken =
|
|
18
|
+
for (const heading of filterByTypesCached([ "atxHeading", "setextHeading" ])) {
|
|
19
|
+
const styleForToken = getHeadingStyle(heading);
|
|
19
20
|
if (style === "consistent") {
|
|
20
21
|
style = styleForToken;
|
|
21
22
|
}
|
|
22
23
|
if (styleForToken !== style) {
|
|
23
|
-
const h12 =
|
|
24
|
+
const h12 = getHeadingLevel(heading) <= 2;
|
|
24
25
|
const setextWithAtx =
|
|
25
26
|
(style === "setext_with_atx") &&
|
|
26
27
|
((h12 && (styleForToken === "setext")) ||
|
|
@@ -36,10 +37,14 @@ module.exports = {
|
|
|
36
37
|
} else if (style === "setext_with_atx_closed") {
|
|
37
38
|
expected = h12 ? "setext" : "atx_closed";
|
|
38
39
|
}
|
|
39
|
-
addErrorDetailIf(
|
|
40
|
-
|
|
40
|
+
addErrorDetailIf(
|
|
41
|
+
onError,
|
|
42
|
+
heading.startLine,
|
|
43
|
+
expected,
|
|
44
|
+
styleForToken
|
|
45
|
+
);
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
|
-
}
|
|
48
|
+
}
|
|
44
49
|
}
|
|
45
50
|
};
|
package/lib/md004.js
CHANGED
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { addErrorDetailIf
|
|
6
|
-
|
|
7
|
-
const {
|
|
5
|
+
const { addErrorDetailIf } = require("../helpers");
|
|
6
|
+
const { getDescendantsByType, getParentOfType } = require("../helpers/micromark-helpers.cjs");
|
|
7
|
+
const { filterByTypesCached } = require("./cache");
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const markerToStyle = {
|
|
10
|
+
"-": "dash",
|
|
11
|
+
"+": "plus",
|
|
12
|
+
"*": "asterisk"
|
|
13
|
+
};
|
|
14
|
+
const styleToMarker = {
|
|
10
15
|
"dash": "-",
|
|
11
16
|
"plus": "+",
|
|
12
17
|
"asterisk": "*"
|
|
@@ -16,7 +21,13 @@ const differentItemStyle = {
|
|
|
16
21
|
"plus": "asterisk",
|
|
17
22
|
"asterisk": "dash"
|
|
18
23
|
};
|
|
19
|
-
const validStyles =
|
|
24
|
+
const validStyles = new Set([
|
|
25
|
+
"asterisk",
|
|
26
|
+
"consistent",
|
|
27
|
+
"dash",
|
|
28
|
+
"plus",
|
|
29
|
+
"sublist"
|
|
30
|
+
]);
|
|
20
31
|
|
|
21
32
|
// eslint-disable-next-line jsdoc/valid-types
|
|
22
33
|
/** @type import("./markdownlint").Rule */
|
|
@@ -24,55 +35,51 @@ module.exports = {
|
|
|
24
35
|
"names": [ "MD004", "ul-style" ],
|
|
25
36
|
"description": "Unordered list style",
|
|
26
37
|
"tags": [ "bullet", "ul" ],
|
|
27
|
-
"parser": "
|
|
38
|
+
"parser": "micromark",
|
|
28
39
|
"function": function MD004(params, onError) {
|
|
29
40
|
const style = String(params.config.style || "consistent");
|
|
30
|
-
let expectedStyle = style;
|
|
41
|
+
let expectedStyle = validStyles.has(style) ? style : "dash";
|
|
31
42
|
const nestingStyles = [];
|
|
32
|
-
for (const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
for (const listUnordered of filterByTypesCached([ "listUnordered" ])) {
|
|
44
|
+
let nesting = 0;
|
|
45
|
+
if (style === "sublist") {
|
|
46
|
+
/** @type {import("../helpers/micromark-helpers.cjs").Token | null} */
|
|
47
|
+
let parent = listUnordered;
|
|
48
|
+
// @ts-ignore
|
|
49
|
+
while ((parent = getParentOfType(parent, [ "listOrdered", "listUnordered" ]))) {
|
|
50
|
+
nesting++;
|
|
36
51
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
expectedStyle = nestingStyles[nesting];
|
|
48
|
-
}
|
|
49
|
-
if (!validStyles.includes(expectedStyle)) {
|
|
50
|
-
expectedStyle = validStyles[0];
|
|
51
|
-
}
|
|
52
|
-
let range = null;
|
|
53
|
-
let fixInfo = null;
|
|
54
|
-
const match = item.line.match(listItemMarkerRe);
|
|
55
|
-
if (match) {
|
|
56
|
-
const column = match.index + 1;
|
|
57
|
-
const length = match[0].length;
|
|
58
|
-
range = [ column, length ];
|
|
59
|
-
fixInfo = {
|
|
60
|
-
"editColumn": match[1].length + 1,
|
|
61
|
-
"deleteCount": 1,
|
|
62
|
-
"insertText": expectedStyleToMarker[expectedStyle]
|
|
63
|
-
};
|
|
52
|
+
}
|
|
53
|
+
const listItemMarkers = getDescendantsByType(listUnordered, [ "listItemPrefix", "listItemMarker" ]);
|
|
54
|
+
for (const listItemMarker of listItemMarkers) {
|
|
55
|
+
const itemStyle = markerToStyle[listItemMarker.text];
|
|
56
|
+
if (style === "sublist") {
|
|
57
|
+
if (!nestingStyles[nesting]) {
|
|
58
|
+
nestingStyles[nesting] =
|
|
59
|
+
(itemStyle === nestingStyles[nesting - 1]) ?
|
|
60
|
+
differentItemStyle[itemStyle] :
|
|
61
|
+
itemStyle;
|
|
64
62
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
expectedStyle,
|
|
69
|
-
itemStyle,
|
|
70
|
-
null,
|
|
71
|
-
null,
|
|
72
|
-
range,
|
|
73
|
-
fixInfo
|
|
74
|
-
);
|
|
63
|
+
expectedStyle = nestingStyles[nesting];
|
|
64
|
+
} else if (expectedStyle === "consistent") {
|
|
65
|
+
expectedStyle = itemStyle;
|
|
75
66
|
}
|
|
67
|
+
const column = listItemMarker.startColumn;
|
|
68
|
+
const length = listItemMarker.endColumn - listItemMarker.startColumn;
|
|
69
|
+
addErrorDetailIf(
|
|
70
|
+
onError,
|
|
71
|
+
listItemMarker.startLine,
|
|
72
|
+
expectedStyle,
|
|
73
|
+
itemStyle,
|
|
74
|
+
undefined,
|
|
75
|
+
undefined,
|
|
76
|
+
[ column, length ],
|
|
77
|
+
{
|
|
78
|
+
"editColumn": column,
|
|
79
|
+
"deleteCount": length,
|
|
80
|
+
"insertText": styleToMarker[expectedStyle]
|
|
81
|
+
}
|
|
82
|
+
);
|
|
76
83
|
}
|
|
77
84
|
}
|
|
78
85
|
}
|