markdownlint 0.35.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +72 -35
  3. package/demo/markdownlint-browser.js +1258 -1214
  4. package/doc/CustomRules.md +55 -249
  5. package/doc/Rules.md +36 -29
  6. package/doc/md003.md +2 -2
  7. package/doc/md004.md +4 -3
  8. package/doc/md035.md +3 -8
  9. package/doc/md046.md +2 -2
  10. package/doc/md048.md +3 -2
  11. package/doc/md049.md +4 -3
  12. package/doc/md050.md +4 -4
  13. package/doc/md051.md +11 -3
  14. package/doc/md055.md +3 -2
  15. package/helpers/README.md +2 -19
  16. package/helpers/helpers.js +87 -347
  17. package/helpers/micromark-helpers.cjs +296 -0
  18. package/helpers/micromark-parse.cjs +313 -0
  19. package/helpers/package.json +5 -2
  20. package/helpers/shared.js +6 -0
  21. package/helpers/test.cjs +28 -0
  22. package/lib/cache.js +3 -3
  23. package/lib/configuration-strict.d.ts +120 -106
  24. package/lib/constants.js +1 -1
  25. package/lib/markdownit.cjs +172 -0
  26. package/lib/markdownlint.d.ts +41 -3
  27. package/lib/markdownlint.js +153 -115
  28. package/lib/md001.js +1 -1
  29. package/lib/md003.js +1 -1
  30. package/lib/md004.js +3 -3
  31. package/lib/md007.js +3 -3
  32. package/lib/md009.js +1 -1
  33. package/lib/md010.js +18 -14
  34. package/lib/md011.js +24 -23
  35. package/lib/md012.js +1 -1
  36. package/lib/md013.js +1 -1
  37. package/lib/md014.js +5 -9
  38. package/lib/md018.js +1 -1
  39. package/lib/md019-md021.js +1 -1
  40. package/lib/md020.js +2 -2
  41. package/lib/md022.js +9 -15
  42. package/lib/md024.js +1 -1
  43. package/lib/md025.js +1 -1
  44. package/lib/md027.js +2 -2
  45. package/lib/md029.js +3 -3
  46. package/lib/md031.js +8 -6
  47. package/lib/md032.js +27 -15
  48. package/lib/md033.js +1 -1
  49. package/lib/md034.js +21 -29
  50. package/lib/md036.js +18 -24
  51. package/lib/md037.js +1 -1
  52. package/lib/md038.js +9 -14
  53. package/lib/md039.js +13 -15
  54. package/lib/md040.js +11 -13
  55. package/lib/md041.js +4 -4
  56. package/lib/md042.js +2 -6
  57. package/lib/md043.js +1 -1
  58. package/lib/md044.js +31 -28
  59. package/lib/md045.js +2 -2
  60. package/lib/md048.js +28 -17
  61. package/lib/md049-md050.js +20 -5
  62. package/lib/md051.js +16 -18
  63. package/lib/md053.js +5 -4
  64. package/lib/md054.js +10 -12
  65. package/lib/md055.js +39 -46
  66. package/lib/md056.js +31 -36
  67. package/lib/md058.js +29 -14
  68. package/package.json +14 -13
  69. package/schema/.markdownlint.jsonc +55 -52
  70. package/schema/.markdownlint.yaml +54 -52
  71. package/schema/markdownlint-config-schema-strict.json +132 -110
  72. package/schema/markdownlint-config-schema.json +132 -110
  73. package/helpers/micromark.cjs +0 -544
package/lib/constants.js CHANGED
@@ -12,4 +12,4 @@ module.exports.fixableRuleNames = [
12
12
  "MD058"
13
13
  ];
14
14
  module.exports.homepage = "https://github.com/DavidAnson/markdownlint";
15
- module.exports.version = "0.35.0";
15
+ module.exports.version = "0.36.0";
@@ -0,0 +1,172 @@
1
+ // @ts-check
2
+
3
+ "use strict";
4
+
5
+ const { newLineRe } = require("../helpers");
6
+
7
+ /**
8
+ * @callback InlineCodeSpanCallback
9
+ * @param {string} code Code content.
10
+ * @param {number} lineIndex Line index (0-based).
11
+ * @param {number} columnIndex Column index (0-based).
12
+ * @param {number} ticks Count of backticks.
13
+ * @returns {void}
14
+ */
15
+
16
+ /**
17
+ * Calls the provided function for each inline code span's content.
18
+ *
19
+ * @param {string} input Markdown content.
20
+ * @param {InlineCodeSpanCallback} handler Callback function taking (code,
21
+ * lineIndex, columnIndex, ticks).
22
+ * @returns {void}
23
+ */
24
+ function forEachInlineCodeSpan(input, handler) {
25
+ const backtickRe = /`+/g;
26
+ let match = null;
27
+ const backticksLengthAndIndex = [];
28
+ while ((match = backtickRe.exec(input)) !== null) {
29
+ backticksLengthAndIndex.push([ match[0].length, match.index ]);
30
+ }
31
+ const newLinesIndex = [];
32
+ while ((match = newLineRe.exec(input)) !== null) {
33
+ newLinesIndex.push(match.index);
34
+ }
35
+ let lineIndex = 0;
36
+ let lineStartIndex = 0;
37
+ let k = 0;
38
+ for (let i = 0; i < backticksLengthAndIndex.length - 1; i++) {
39
+ const [ startLength, startIndex ] = backticksLengthAndIndex[i];
40
+ if ((startIndex === 0) || (input[startIndex - 1] !== "\\")) {
41
+ for (let j = i + 1; j < backticksLengthAndIndex.length; j++) {
42
+ const [ endLength, endIndex ] = backticksLengthAndIndex[j];
43
+ if (startLength === endLength) {
44
+ for (; k < newLinesIndex.length; k++) {
45
+ const newLineIndex = newLinesIndex[k];
46
+ if (startIndex < newLineIndex) {
47
+ break;
48
+ }
49
+ lineIndex++;
50
+ lineStartIndex = newLineIndex + 1;
51
+ }
52
+ const columnIndex = startIndex - lineStartIndex + startLength;
53
+ handler(
54
+ input.slice(startIndex + startLength, endIndex),
55
+ lineIndex,
56
+ columnIndex,
57
+ startLength
58
+ );
59
+ i = j;
60
+ break;
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Freeze all freeze-able members of a token and its children.
69
+ *
70
+ * @param {import("./markdownlint").MarkdownItToken} token A markdown-it token.
71
+ * @returns {void}
72
+ */
73
+ function freezeToken(token) {
74
+ if (token.attrs) {
75
+ for (const attr of token.attrs) {
76
+ Object.freeze(attr);
77
+ }
78
+ Object.freeze(token.attrs);
79
+ }
80
+ if (token.children) {
81
+ for (const child of token.children) {
82
+ freezeToken(child);
83
+ }
84
+ Object.freeze(token.children);
85
+ }
86
+ if (token.map) {
87
+ Object.freeze(token.map);
88
+ }
89
+ Object.freeze(token);
90
+ }
91
+
92
+ /**
93
+ * Annotate tokens with line/lineNumber and freeze them.
94
+ *
95
+ * @param {Object[]} tokens Array of markdown-it tokens.
96
+ * @param {string[]} lines Lines of Markdown content.
97
+ * @returns {void}
98
+ */
99
+ function annotateAndFreezeTokens(tokens, lines) {
100
+ let trMap = null;
101
+ // eslint-disable-next-line jsdoc/valid-types
102
+ /** @type import("./markdownlint").MarkdownItToken[] */
103
+ // @ts-ignore
104
+ const markdownItTokens = tokens;
105
+ for (const token of markdownItTokens) {
106
+ // Provide missing maps for table content
107
+ if (token.type === "tr_open") {
108
+ trMap = token.map;
109
+ } else if (token.type === "tr_close") {
110
+ trMap = null;
111
+ }
112
+ if (!token.map && trMap) {
113
+ token.map = [ ...trMap ];
114
+ }
115
+ // Update token metadata
116
+ if (token.map) {
117
+ token.line = lines[token.map[0]];
118
+ token.lineNumber = token.map[0] + 1;
119
+ // Trim bottom of token to exclude whitespace lines
120
+ while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
121
+ token.map[1]--;
122
+ }
123
+ }
124
+ // Annotate children with lineNumber
125
+ if (token.children) {
126
+ const codeSpanExtraLines = [];
127
+ if (token.children.some((child) => child.type === "code_inline")) {
128
+ forEachInlineCodeSpan(token.content, (code) => {
129
+ codeSpanExtraLines.push(code.split(newLineRe).length - 1);
130
+ });
131
+ }
132
+ let lineNumber = token.lineNumber;
133
+ for (const child of token.children) {
134
+ child.lineNumber = lineNumber;
135
+ child.line = lines[lineNumber - 1];
136
+ if ((child.type === "softbreak") || (child.type === "hardbreak")) {
137
+ lineNumber++;
138
+ } else if (child.type === "code_inline") {
139
+ lineNumber += codeSpanExtraLines.shift();
140
+ }
141
+ }
142
+ }
143
+ freezeToken(token);
144
+ }
145
+ Object.freeze(tokens);
146
+ }
147
+
148
+ /**
149
+ * Gets an array of markdown-it tokens for the input.
150
+ *
151
+ * @param {import("./markdownlint").Plugin[]} markdownItPlugins Additional plugins.
152
+ * @param {string} content Markdown content.
153
+ * @param {string[]} lines Lines of Markdown content.
154
+ * @returns {import("../lib/markdownlint").MarkdownItToken} Array of markdown-it tokens.
155
+ */
156
+ function getMarkdownItTokens(markdownItPlugins, content, lines) {
157
+ const markdownit = require("markdown-it");
158
+ const md = markdownit({ "html": true });
159
+ for (const plugin of markdownItPlugins) {
160
+ // @ts-ignore
161
+ md.use(...plugin);
162
+ }
163
+ const tokens = md.parse(content, {});
164
+ annotateAndFreezeTokens(tokens, lines);
165
+ // @ts-ignore
166
+ return tokens;
167
+ };
168
+
169
+ module.exports = {
170
+ forEachInlineCodeSpan,
171
+ getMarkdownItTokens
172
+ };
@@ -8,7 +8,7 @@ export = markdownlint;
8
8
  */
9
9
  declare function markdownlint(options: Options | null, callback: LintCallback): void;
10
10
  declare namespace markdownlint {
11
- export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, GetMarkdownIt, RuleFunction, RuleParams, MarkdownParsers, ParserMarkdownIt, ParserMicromark, MarkdownItToken, MicromarkTokenType, MicromarkToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintContentCallback, LintCallback, Configuration, ConfigurationStrict, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
11
+ export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, applyFix, applyFixes, RuleFunction, RuleParams, MarkdownParsers, ParserMarkdownIt, ParserMicromark, MarkdownItToken, MicromarkTokenType, MicromarkToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, RuleOnErrorFixInfoNormalized, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintContentCallback, LintCallback, Configuration, ConfigurationStrict, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
12
12
  }
13
13
  /**
14
14
  * Lint specified Markdown files synchronously.
@@ -50,9 +50,22 @@ declare namespace promises {
50
50
  export { readConfigPromise as readConfig };
51
51
  }
52
52
  /**
53
- * Function to get an instance of the markdown-it parser.
53
+ * Applies the specified fix to a Markdown content line.
54
+ *
55
+ * @param {string} line Line of Markdown content.
56
+ * @param {RuleOnErrorFixInfo} fixInfo RuleOnErrorFixInfo instance.
57
+ * @param {string} [lineEnding] Line ending to use.
58
+ * @returns {string | null} Fixed content or null if deleted.
59
+ */
60
+ declare function applyFix(line: string, fixInfo: RuleOnErrorFixInfo, lineEnding?: string): string | null;
61
+ /**
62
+ * Applies as many of the specified fixes as possible to Markdown content.
63
+ *
64
+ * @param {string} input Lines of Markdown content.
65
+ * @param {RuleOnErrorInfo[]} errors RuleOnErrorInfo instances.
66
+ * @returns {string} Fixed content.
54
67
  */
55
- type GetMarkdownIt = () => any;
68
+ declare function applyFixes(input: string, errors: RuleOnErrorInfo[]): string;
56
69
  /**
57
70
  * Function to implement rule logic.
58
71
  */
@@ -81,6 +94,10 @@ type RuleParams = {
81
94
  * Rule configuration.
82
95
  */
83
96
  config: RuleConfiguration;
97
+ /**
98
+ * Version of the markdownlint library.
99
+ */
100
+ version: string;
84
101
  };
85
102
  /**
86
103
  * Markdown parser data.
@@ -270,6 +287,27 @@ type RuleOnErrorFixInfo = {
270
287
  */
271
288
  insertText?: string;
272
289
  };
290
+ /**
291
+ * RuleOnErrorInfo with all optional properties present.
292
+ */
293
+ type RuleOnErrorFixInfoNormalized = {
294
+ /**
295
+ * Line number (1-based).
296
+ */
297
+ lineNumber: number;
298
+ /**
299
+ * Column of the fix (1-based).
300
+ */
301
+ editColumn: number;
302
+ /**
303
+ * Count of characters to delete.
304
+ */
305
+ deleteCount: number;
306
+ /**
307
+ * Text to insert (after deleting).
308
+ */
309
+ insertText: string;
310
+ };
273
311
  /**
274
312
  * Rule definition.
275
313
  */
@@ -4,8 +4,8 @@
4
4
 
5
5
  const path = require("node:path");
6
6
  const { promisify } = require("node:util");
7
- const micromark = require("../helpers/micromark.cjs");
8
- // const { deprecatedRuleNames } = require("./constants");
7
+ const micromark = require("../helpers/micromark-parse.cjs");
8
+ const { version } = require("./constants");
9
9
  const rules = require("./rules");
10
10
  const helpers = require("../helpers");
11
11
  const cache = require("./cache");
@@ -58,7 +58,7 @@ function validateRuleList(ruleList, synchronous) {
58
58
  !result &&
59
59
  (rule.parser !== undefined) &&
60
60
  (rule.parser !== "markdownit") &&
61
- !((customIndex < 0) && (rule.parser === "micromark")) &&
61
+ (rule.parser !== "micromark") &&
62
62
  (rule.parser !== "none")
63
63
  ) {
64
64
  result = newError("parser", rule.parser);
@@ -197,87 +197,6 @@ function removeFrontMatter(content, frontMatter) {
197
197
  };
198
198
  }
199
199
 
200
- /**
201
- * Freeze all freeze-able members of a token and its children.
202
- *
203
- * @param {MarkdownItToken} token A markdown-it token.
204
- * @returns {void}
205
- */
206
- function freezeToken(token) {
207
- if (token.attrs) {
208
- for (const attr of token.attrs) {
209
- Object.freeze(attr);
210
- }
211
- Object.freeze(token.attrs);
212
- }
213
- if (token.children) {
214
- for (const child of token.children) {
215
- freezeToken(child);
216
- }
217
- Object.freeze(token.children);
218
- }
219
- if (token.map) {
220
- Object.freeze(token.map);
221
- }
222
- Object.freeze(token);
223
- }
224
-
225
- /**
226
- * Annotate tokens with line/lineNumber and freeze them.
227
- *
228
- * @param {import("markdown-it").Token[]} tokens Array of markdown-it tokens.
229
- * @param {string[]} lines Lines of Markdown content.
230
- * @returns {void}
231
- */
232
- function annotateAndFreezeTokens(tokens, lines) {
233
- let trMap = null;
234
- // eslint-disable-next-line jsdoc/valid-types
235
- /** @type MarkdownItToken[] */
236
- // @ts-ignore
237
- const markdownItTokens = tokens;
238
- for (const token of markdownItTokens) {
239
- // Provide missing maps for table content
240
- if (token.type === "tr_open") {
241
- trMap = token.map;
242
- } else if (token.type === "tr_close") {
243
- trMap = null;
244
- }
245
- if (!token.map && trMap) {
246
- token.map = [ ...trMap ];
247
- }
248
- // Update token metadata
249
- if (token.map) {
250
- token.line = lines[token.map[0]];
251
- token.lineNumber = token.map[0] + 1;
252
- // Trim bottom of token to exclude whitespace lines
253
- while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
254
- token.map[1]--;
255
- }
256
- }
257
- // Annotate children with lineNumber
258
- if (token.children) {
259
- const codeSpanExtraLines = [];
260
- if (token.children.some((child) => child.type === "code_inline")) {
261
- helpers.forEachInlineCodeSpan(token.content, (code) => {
262
- codeSpanExtraLines.push(code.split(helpers.newLineRe).length - 1);
263
- });
264
- }
265
- let lineNumber = token.lineNumber;
266
- for (const child of token.children) {
267
- child.lineNumber = lineNumber;
268
- child.line = lines[lineNumber - 1];
269
- if ((child.type === "softbreak") || (child.type === "hardbreak")) {
270
- lineNumber++;
271
- } else if (child.type === "code_inline") {
272
- lineNumber += codeSpanExtraLines.shift();
273
- }
274
- }
275
- }
276
- freezeToken(token);
277
- }
278
- Object.freeze(tokens);
279
- }
280
-
281
200
  /**
282
201
  * Map rule names/tags to canonical rule name.
283
202
  *
@@ -533,7 +452,7 @@ function getEnabledRulesPerLineNumber(
533
452
  * names.
534
453
  * @param {string} name Identifier for the content.
535
454
  * @param {string} content Markdown content.
536
- * @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
455
+ * @param {Plugin[]} markdownItPlugins Additional plugins.
537
456
  * @param {Configuration} config Configuration object.
538
457
  * @param {ConfigurationParser[] | null} configParsers Configuration parsers.
539
458
  * @param {RegExp | null} frontMatter Regular expression for front matter.
@@ -548,7 +467,7 @@ function lintContent(
548
467
  aliasToRuleNames,
549
468
  name,
550
469
  content,
551
- getMarkdownIt,
470
+ markdownItPlugins,
552
471
  config,
553
472
  configParsers,
554
473
  frontMatter,
@@ -576,14 +495,20 @@ function lintContent(
576
495
  const needMarkdownItTokens = enabledRuleList.some(
577
496
  (rule) => (rule.parser === "markdownit") || (rule.parser === undefined)
578
497
  );
498
+ const customRulesPresent = (ruleList.length !== rules.length);
579
499
  // Parse content into parser tokens
580
- const markdownitTokens = needMarkdownItTokens ? getMarkdownIt().parse(content, {}) : [];
581
- const micromarkTokens = micromark.parse(content);
500
+ const micromarkTokens = micromark.parse(
501
+ content,
502
+ { "freezeTokens": customRulesPresent }
503
+ );
582
504
  // Hide the content of HTML comments from rules
505
+ const preClearedContent = content;
583
506
  content = helpers.clearHtmlCommentText(content);
584
- // Parse content into lines and update markdown-it tokens
507
+ // Parse content into lines and get markdown-it tokens
585
508
  const lines = content.split(helpers.newLineRe);
586
- annotateAndFreezeTokens(markdownitTokens, lines);
509
+ const markdownitTokens = needMarkdownItTokens ?
510
+ require("./markdownit.cjs").getMarkdownItTokens(markdownItPlugins, preClearedContent, lines) :
511
+ [];
587
512
  // Create (frozen) parameters for rules
588
513
  /** @type {MarkdownParsers} */
589
514
  // @ts-ignore
@@ -604,6 +529,7 @@ function lintContent(
604
529
  const parsersNone = Object.freeze({});
605
530
  const paramsBase = {
606
531
  name,
532
+ version,
607
533
  "lines": Object.freeze(lines),
608
534
  "frontMatterLines": Object.freeze(frontMatterLines)
609
535
  };
@@ -836,7 +762,7 @@ function lintContent(
836
762
  * @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
837
763
  * names.
838
764
  * @param {string} file Path of file to lint.
839
- * @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
765
+ * @param {Plugin[]} markdownItPlugins Additional plugins.
840
766
  * @param {Configuration} config Configuration object.
841
767
  * @param {ConfigurationParser[] | null} configParsers Configuration parsers.
842
768
  * @param {RegExp | null} frontMatter Regular expression for front matter.
@@ -852,7 +778,7 @@ function lintFile(
852
778
  ruleList,
853
779
  aliasToRuleNames,
854
780
  file,
855
- getMarkdownIt,
781
+ markdownItPlugins,
856
782
  config,
857
783
  configParsers,
858
784
  frontMatter,
@@ -872,7 +798,7 @@ function lintFile(
872
798
  aliasToRuleNames,
873
799
  file,
874
800
  content,
875
- getMarkdownIt,
801
+ markdownItPlugins,
876
802
  config,
877
803
  configParsers,
878
804
  frontMatter,
@@ -932,21 +858,14 @@ function lintInput(options, synchronous, callback) {
932
858
  const config = options.config || { "default": true };
933
859
  const configParsers = options.configParsers || null;
934
860
  const frontMatter = (options.frontMatter === undefined) ?
935
- helpers.frontMatterRe : options.frontMatter;
861
+ helpers.frontMatterRe :
862
+ options.frontMatter;
936
863
  const handleRuleFailures = !!options.handleRuleFailures;
937
864
  const noInlineConfig = !!options.noInlineConfig;
938
865
  const resultVersion = (options.resultVersion === undefined) ?
939
- 3 : options.resultVersion;
940
- const getMarkdownIt = () => {
941
- const markdownit = require("markdown-it");
942
- const md = markdownit({ "html": true });
943
- const markdownItPlugins = options.markdownItPlugins || [];
944
- for (const plugin of markdownItPlugins) {
945
- // @ts-ignore
946
- md.use(...plugin);
947
- }
948
- return md;
949
- };
866
+ 3 :
867
+ options.resultVersion;
868
+ const markdownItPlugins = options.markdownItPlugins || [];
950
869
  const fs = options.fs || require("node:fs");
951
870
  const aliasToRuleNames = mapAliasToRuleNames(ruleList);
952
871
  const results = newResults(ruleList);
@@ -978,7 +897,7 @@ function lintInput(options, synchronous, callback) {
978
897
  ruleList,
979
898
  aliasToRuleNames,
980
899
  currentItem,
981
- getMarkdownIt,
900
+ markdownItPlugins,
982
901
  config,
983
902
  configParsers,
984
903
  frontMatter,
@@ -997,7 +916,7 @@ function lintInput(options, synchronous, callback) {
997
916
  aliasToRuleNames,
998
917
  currentItem,
999
918
  strings[currentItem] || "",
1000
- getMarkdownIt,
919
+ markdownItPlugins,
1001
920
  config,
1002
921
  configParsers,
1003
922
  frontMatter,
@@ -1289,13 +1208,126 @@ function readConfigSync(file, parsers, fs) {
1289
1208
  return config;
1290
1209
  }
1291
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
+
1292
1324
  /**
1293
1325
  * Gets the (semantic) version of the library.
1294
1326
  *
1295
1327
  * @returns {string} SemVer string.
1296
1328
  */
1297
1329
  function getVersion() {
1298
- return require("./constants").version;
1330
+ return version;
1299
1331
  }
1300
1332
 
1301
1333
  // Export a/synchronous/Promise APIs
@@ -1308,17 +1340,12 @@ markdownlint.promises = {
1308
1340
  "extendConfig": extendConfigPromise,
1309
1341
  "readConfig": readConfigPromise
1310
1342
  };
1343
+ markdownlint.applyFix = applyFix;
1344
+ markdownlint.applyFixes = applyFixes;
1311
1345
  module.exports = markdownlint;
1312
1346
 
1313
1347
  // Type declarations
1314
1348
 
1315
- /**
1316
- * Function to get an instance of the markdown-it parser.
1317
- *
1318
- * @callback GetMarkdownIt
1319
- * @returns {import("markdown-it")}
1320
- */
1321
-
1322
1349
  /**
1323
1350
  * Function to implement rule logic.
1324
1351
  *
@@ -1339,6 +1366,7 @@ module.exports = markdownlint;
1339
1366
  * @property {readonly string[]} lines File/string lines.
1340
1367
  * @property {readonly string[]} frontMatterLines Front matter lines.
1341
1368
  * @property {RuleConfiguration} config Rule configuration.
1369
+ * @property {string} version Version of the markdownlint library.
1342
1370
  */
1343
1371
 
1344
1372
  /* eslint-enable jsdoc/valid-types */
@@ -1432,6 +1460,16 @@ module.exports = markdownlint;
1432
1460
  * @property {string} [insertText] Text to insert (after deleting).
1433
1461
  */
1434
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
+
1435
1473
  /**
1436
1474
  * Rule definition.
1437
1475
  *
package/lib/md001.js CHANGED
@@ -3,7 +3,7 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorDetailIf } = require("../helpers");
6
- const { getHeadingLevel } = require("../helpers/micromark.cjs");
6
+ const { getHeadingLevel } = require("../helpers/micromark-helpers.cjs");
7
7
  const { filterByTypesCached } = require("./cache");
8
8
 
9
9
  // eslint-disable-next-line jsdoc/valid-types
package/lib/md003.js CHANGED
@@ -3,7 +3,7 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorDetailIf } = require("../helpers");
6
- const { getHeadingLevel, getHeadingStyle } = require("../helpers/micromark.cjs");
6
+ const { getHeadingLevel, getHeadingStyle } = require("../helpers/micromark-helpers.cjs");
7
7
  const { filterByTypesCached } = require("./cache");
8
8
 
9
9
  // eslint-disable-next-line jsdoc/valid-types
package/lib/md004.js CHANGED
@@ -3,7 +3,7 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorDetailIf } = require("../helpers");
6
- const { getDescendantsByType, getTokenParentOfType } = require("../helpers/micromark.cjs");
6
+ const { getDescendantsByType, getParentOfType } = require("../helpers/micromark-helpers.cjs");
7
7
  const { filterByTypesCached } = require("./cache");
8
8
 
9
9
  const markerToStyle = {
@@ -43,10 +43,10 @@ module.exports = {
43
43
  for (const listUnordered of filterByTypesCached([ "listUnordered" ])) {
44
44
  let nesting = 0;
45
45
  if (style === "sublist") {
46
- /** @type {import("../helpers/micromark.cjs").Token | null} */
46
+ /** @type {import("../helpers/micromark-helpers.cjs").Token | null} */
47
47
  let parent = listUnordered;
48
48
  // @ts-ignore
49
- while ((parent = getTokenParentOfType(parent, [ "listOrdered", "listUnordered" ]))) {
49
+ while ((parent = getParentOfType(parent, [ "listOrdered", "listUnordered" ]))) {
50
50
  nesting++;
51
51
  }
52
52
  }