markdownlint 0.34.0 → 0.35.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 (76) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +25 -10
  3. package/demo/markdownlint-browser.js +1144 -1166
  4. package/doc/CustomRules.md +11 -6
  5. package/doc/Rules.md +66 -0
  6. package/doc/md034.md +5 -0
  7. package/doc/md044.md +10 -0
  8. package/doc/md058.md +48 -0
  9. package/helpers/README.md +3 -29
  10. package/helpers/helpers.js +56 -288
  11. package/helpers/micromark.cjs +111 -12
  12. package/helpers/package.json +1 -1
  13. package/lib/cache.js +62 -13
  14. package/lib/configuration-strict.d.ts +1185 -0
  15. package/lib/configuration.d.ts +3 -1173
  16. package/lib/constants.js +3 -2
  17. package/lib/markdownlint.d.ts +65 -56
  18. package/lib/markdownlint.js +59 -37
  19. package/lib/md001.js +15 -9
  20. package/lib/md003.js +14 -9
  21. package/lib/md004.js +55 -48
  22. package/lib/md005.js +4 -13
  23. package/lib/md007.js +5 -9
  24. package/lib/md009.js +50 -44
  25. package/lib/md010.js +48 -33
  26. package/lib/md011.js +16 -8
  27. package/lib/md012.js +17 -9
  28. package/lib/md013.js +44 -38
  29. package/lib/md014.js +35 -38
  30. package/lib/md018.js +17 -9
  31. package/lib/md019-md021.js +80 -0
  32. package/lib/md020.js +13 -7
  33. package/lib/md022.js +6 -16
  34. package/lib/md023.js +22 -23
  35. package/lib/md024.js +12 -9
  36. package/lib/md025.js +15 -10
  37. package/lib/md026.js +2 -7
  38. package/lib/md027.js +5 -10
  39. package/lib/md028.js +3 -8
  40. package/lib/md029.js +40 -34
  41. package/lib/md030.js +4 -10
  42. package/lib/md031.js +43 -23
  43. package/lib/md032.js +16 -26
  44. package/lib/md033.js +3 -8
  45. package/lib/md034.js +5 -10
  46. package/lib/md035.js +2 -7
  47. package/lib/md036.js +3 -2
  48. package/lib/md037.js +2 -7
  49. package/lib/md038.js +3 -9
  50. package/lib/md039.js +66 -54
  51. package/lib/md040.js +3 -8
  52. package/lib/md041.js +1 -1
  53. package/lib/md042.js +48 -38
  54. package/lib/md043.js +20 -14
  55. package/lib/md044.js +3 -8
  56. package/lib/md045.js +3 -8
  57. package/lib/md046.js +2 -8
  58. package/lib/md048.js +3 -7
  59. package/lib/md049-md050.js +2 -7
  60. package/lib/md051.js +6 -12
  61. package/lib/md052.js +2 -2
  62. package/lib/md053.js +2 -2
  63. package/lib/md054.js +8 -13
  64. package/lib/md055.js +7 -8
  65. package/lib/md056.js +15 -11
  66. package/lib/md058.js +43 -0
  67. package/lib/rules.js +11 -4
  68. package/package.json +19 -17
  69. package/schema/.markdownlint.jsonc +54 -51
  70. package/schema/.markdownlint.yaml +53 -50
  71. package/schema/ValidatingConfiguration.md +6 -8
  72. package/schema/markdownlint-config-schema-strict.json +1819 -0
  73. package/schema/markdownlint-config-schema.json +115 -105
  74. package/style/prettier.json +1 -0
  75. package/lib/md019.js +0 -42
  76. package/lib/md021.js +0 -65
package/lib/constants.js CHANGED
@@ -8,7 +8,8 @@ module.exports.fixableRuleNames = [
8
8
  "MD012", "MD014", "MD018", "MD019", "MD020", "MD021",
9
9
  "MD022", "MD023", "MD026", "MD027", "MD030", "MD031",
10
10
  "MD032", "MD034", "MD037", "MD038", "MD039", "MD044",
11
- "MD047", "MD049", "MD050", "MD051", "MD053", "MD054"
11
+ "MD047", "MD049", "MD050", "MD051", "MD053", "MD054",
12
+ "MD058"
12
13
  ];
13
14
  module.exports.homepage = "https://github.com/DavidAnson/markdownlint";
14
- module.exports.version = "0.34.0";
15
+ module.exports.version = "0.35.0";
@@ -8,63 +8,8 @@ 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, RuleFunction, RuleParams, MarkdownParsers, ParserMarkdownIt, ParserMicromark, MarkdownItToken, MicromarkTokenType, MicromarkToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintContentCallback, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
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 };
12
12
  }
13
- /**
14
- * Configuration options.
15
- */
16
- type Options = {
17
- /**
18
- * Configuration object.
19
- */
20
- config?: Configuration;
21
- /**
22
- * Configuration parsers.
23
- */
24
- configParsers?: ConfigurationParser[];
25
- /**
26
- * Custom rules.
27
- */
28
- customRules?: Rule[] | Rule;
29
- /**
30
- * Files to lint.
31
- */
32
- files?: string[] | string;
33
- /**
34
- * Front matter pattern.
35
- */
36
- frontMatter?: RegExp | null;
37
- /**
38
- * File system implementation.
39
- */
40
- fs?: any;
41
- /**
42
- * True to catch exceptions.
43
- */
44
- handleRuleFailures?: boolean;
45
- /**
46
- * Additional plugins.
47
- */
48
- markdownItPlugins?: Plugin[];
49
- /**
50
- * True to ignore HTML directives.
51
- */
52
- noInlineConfig?: boolean;
53
- /**
54
- * Results object version.
55
- */
56
- resultVersion?: number;
57
- /**
58
- * Strings to lint.
59
- */
60
- strings?: {
61
- [x: string]: string;
62
- };
63
- };
64
- /**
65
- * Called with the result of the lint function.
66
- */
67
- type LintCallback = (error: Error | null, results?: LintResults) => void;
68
13
  /**
69
14
  * Lint specified Markdown files synchronously.
70
15
  *
@@ -104,6 +49,10 @@ declare namespace promises {
104
49
  export { extendConfigPromise as extendConfig };
105
50
  export { readConfigPromise as readConfig };
106
51
  }
52
+ /**
53
+ * Function to get an instance of the markdown-it parser.
54
+ */
55
+ type GetMarkdownIt = () => any;
107
56
  /**
108
57
  * Function to implement rule logic.
109
58
  */
@@ -354,6 +303,57 @@ type Rule = {
354
303
  */
355
304
  function: RuleFunction;
356
305
  };
306
+ /**
307
+ * Configuration options.
308
+ */
309
+ type Options = {
310
+ /**
311
+ * Configuration object.
312
+ */
313
+ config?: Configuration;
314
+ /**
315
+ * Configuration parsers.
316
+ */
317
+ configParsers?: ConfigurationParser[];
318
+ /**
319
+ * Custom rules.
320
+ */
321
+ customRules?: Rule[] | Rule;
322
+ /**
323
+ * Files to lint.
324
+ */
325
+ files?: string[] | string;
326
+ /**
327
+ * Front matter pattern.
328
+ */
329
+ frontMatter?: RegExp | null;
330
+ /**
331
+ * File system implementation.
332
+ */
333
+ fs?: any;
334
+ /**
335
+ * True to catch exceptions.
336
+ */
337
+ handleRuleFailures?: boolean;
338
+ /**
339
+ * Additional plugins.
340
+ */
341
+ markdownItPlugins?: Plugin[];
342
+ /**
343
+ * True to ignore HTML directives.
344
+ */
345
+ noInlineConfig?: boolean;
346
+ /**
347
+ * Results object version.
348
+ */
349
+ resultVersion?: number;
350
+ /**
351
+ * Strings to lint.
352
+ */
353
+ strings?: {
354
+ [x: string]: string;
355
+ };
356
+ };
357
357
  /**
358
358
  * A markdown-it plugin.
359
359
  */
@@ -430,11 +430,20 @@ type FixInfo = {
430
430
  * Called with the result of linting a string or document.
431
431
  */
432
432
  type LintContentCallback = (error: Error | null, result?: LintError[]) => void;
433
+ /**
434
+ * Called with the result of the lint function.
435
+ */
436
+ type LintCallback = (error: Error | null, results?: LintResults) => void;
433
437
  /**
434
438
  * Configuration object for linting rules. For the JSON schema, see
435
439
  * {@link ../schema/markdownlint-config-schema.json}.
436
440
  */
437
441
  type Configuration = import("./configuration").Configuration;
442
+ /**
443
+ * Configuration object for linting rules strictly. For the JSON schema, see
444
+ * {@link ../schema/markdownlint-config-schema-strict.json}.
445
+ */
446
+ type ConfigurationStrict = import("./configuration-strict").ConfigurationStrict;
438
447
  /**
439
448
  * Rule configuration.
440
449
  */
@@ -4,7 +4,6 @@
4
4
 
5
5
  const path = require("node:path");
6
6
  const { promisify } = require("node:util");
7
- const markdownit = require("markdown-it");
8
7
  const micromark = require("../helpers/micromark.cjs");
9
8
  // const { deprecatedRuleNames } = require("./constants");
10
9
  const rules = require("./rules");
@@ -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 no-inner-declarations, jsdoc/require-jsdoc
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}'.`);
@@ -226,13 +225,17 @@ function freezeToken(token) {
226
225
  /**
227
226
  * Annotate tokens with line/lineNumber and freeze them.
228
227
  *
229
- * @param {MarkdownItToken[]} tokens Array of markdown-it tokens.
228
+ * @param {import("markdown-it").Token[]} tokens Array of markdown-it tokens.
230
229
  * @param {string[]} lines Lines of Markdown content.
231
230
  * @returns {void}
232
231
  */
233
232
  function annotateAndFreezeTokens(tokens, lines) {
234
233
  let trMap = null;
235
- for (const token of tokens) {
234
+ // eslint-disable-next-line jsdoc/valid-types
235
+ /** @type MarkdownItToken[] */
236
+ // @ts-ignore
237
+ const markdownItTokens = tokens;
238
+ for (const token of markdownItTokens) {
236
239
  // Provide missing maps for table content
237
240
  if (token.type === "tr_open") {
238
241
  trMap = token.map;
@@ -507,10 +510,18 @@ function getEnabledRulesPerLineNumber(
507
510
  handleInlineConfig(lines, enableDisableFile);
508
511
  handleInlineConfig(lines, captureRestoreEnableDisable, updateLineState);
509
512
  handleInlineConfig(lines, disableLineNextLine);
513
+ // Create the list of rules that are used at least once
514
+ const enabledRuleList = [];
515
+ for (const [ index, ruleName ] of allRuleNames.entries()) {
516
+ if (enabledRulesPerLineNumber.some((enabledRulesForLine) => enabledRulesForLine[ruleName])) {
517
+ enabledRuleList.push(ruleList[index]);
518
+ }
519
+ }
510
520
  // Return results
511
521
  return {
512
522
  effectiveConfig,
513
- enabledRulesPerLineNumber
523
+ enabledRulesPerLineNumber,
524
+ enabledRuleList
514
525
  };
515
526
  }
516
527
 
@@ -522,7 +533,7 @@ function getEnabledRulesPerLineNumber(
522
533
  * names.
523
534
  * @param {string} name Identifier for the content.
524
535
  * @param {string} content Markdown content.
525
- * @param {Object} md Instance of markdown-it.
536
+ * @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
526
537
  * @param {Configuration} config Configuration object.
527
538
  * @param {ConfigurationParser[] | null} configParsers Configuration parsers.
528
539
  * @param {RegExp | null} frontMatter Regular expression for front matter.
@@ -537,7 +548,7 @@ function lintContent(
537
548
  aliasToRuleNames,
538
549
  name,
539
550
  content,
540
- md,
551
+ getMarkdownIt,
541
552
  config,
542
553
  configParsers,
543
554
  frontMatter,
@@ -552,7 +563,7 @@ function lintContent(
552
563
  const { frontMatterLines } = removeFrontMatterResult;
553
564
  content = removeFrontMatterResult.content;
554
565
  // Get enabled rules per line (with HTML comments present)
555
- const { effectiveConfig, enabledRulesPerLineNumber } =
566
+ const { effectiveConfig, enabledRulesPerLineNumber, enabledRuleList } =
556
567
  getEnabledRulesPerLineNumber(
557
568
  ruleList,
558
569
  content.split(helpers.newLineRe),
@@ -562,8 +573,11 @@ function lintContent(
562
573
  configParsers,
563
574
  aliasToRuleNames
564
575
  );
576
+ const needMarkdownItTokens = enabledRuleList.some(
577
+ (rule) => (rule.parser === "markdownit") || (rule.parser === undefined)
578
+ );
565
579
  // Parse content into parser tokens
566
- const markdownitTokens = md.parse(content, {});
580
+ const markdownitTokens = needMarkdownItTokens ? getMarkdownIt().parse(content, {}) : [];
567
581
  const micromarkTokens = micromark.parse(content);
568
582
  // Hide the content of HTML comments from rules
569
583
  content = helpers.clearHtmlCommentText(content);
@@ -590,23 +604,13 @@ function lintContent(
590
604
  const parsersNone = Object.freeze({});
591
605
  const paramsBase = {
592
606
  name,
593
- "parsers": parsersMarkdownIt,
594
607
  "lines": Object.freeze(lines),
595
608
  "frontMatterLines": Object.freeze(frontMatterLines)
596
609
  };
597
- const lineMetadata =
598
- helpers.getLineMetadata(paramsBase);
599
- const codeBlockAndSpanRanges =
600
- helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata);
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
610
+ cache.initialize({
611
+ ...paramsBase,
612
+ "parsers": parsersMicromark,
613
+ "config": null
610
614
  });
611
615
  // Function to run for each rule
612
616
  let results = [];
@@ -800,8 +804,8 @@ function lintContent(
800
804
  return results;
801
805
  }
802
806
  // Run all rules
803
- const ruleListAsync = ruleList.filter((rule) => rule.asynchronous);
804
- const ruleListSync = ruleList.filter((rule) => !rule.asynchronous);
807
+ const ruleListAsync = enabledRuleList.filter((rule) => rule.asynchronous);
808
+ const ruleListSync = enabledRuleList.filter((rule) => !rule.asynchronous);
805
809
  const ruleListAsyncFirst = [
806
810
  ...ruleListAsync,
807
811
  ...ruleListSync
@@ -821,7 +825,7 @@ function lintContent(
821
825
  } catch (error) {
822
826
  callbackError(error);
823
827
  } finally {
824
- cache.clear();
828
+ cache.initialize();
825
829
  }
826
830
  }
827
831
 
@@ -832,7 +836,7 @@ function lintContent(
832
836
  * @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
833
837
  * names.
834
838
  * @param {string} file Path of file to lint.
835
- * @param {Object} md Instance of markdown-it.
839
+ * @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
836
840
  * @param {Configuration} config Configuration object.
837
841
  * @param {ConfigurationParser[] | null} configParsers Configuration parsers.
838
842
  * @param {RegExp | null} frontMatter Regular expression for front matter.
@@ -848,7 +852,7 @@ function lintFile(
848
852
  ruleList,
849
853
  aliasToRuleNames,
850
854
  file,
851
- md,
855
+ getMarkdownIt,
852
856
  config,
853
857
  configParsers,
854
858
  frontMatter,
@@ -868,7 +872,7 @@ function lintFile(
868
872
  aliasToRuleNames,
869
873
  file,
870
874
  content,
871
- md,
875
+ getMarkdownIt,
872
876
  config,
873
877
  configParsers,
874
878
  frontMatter,
@@ -933,12 +937,16 @@ function lintInput(options, synchronous, callback) {
933
937
  const noInlineConfig = !!options.noInlineConfig;
934
938
  const resultVersion = (options.resultVersion === undefined) ?
935
939
  3 : options.resultVersion;
936
- const md = markdownit({ "html": true });
937
- const markdownItPlugins = options.markdownItPlugins || [];
938
- for (const plugin of markdownItPlugins) {
939
- // @ts-ignore
940
- md.use(...plugin);
941
- }
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
+ };
942
950
  const fs = options.fs || require("node:fs");
943
951
  const aliasToRuleNames = mapAliasToRuleNames(ruleList);
944
952
  const results = newResults(ruleList);
@@ -970,7 +978,7 @@ function lintInput(options, synchronous, callback) {
970
978
  ruleList,
971
979
  aliasToRuleNames,
972
980
  currentItem,
973
- md,
981
+ getMarkdownIt,
974
982
  config,
975
983
  configParsers,
976
984
  frontMatter,
@@ -989,7 +997,7 @@ function lintInput(options, synchronous, callback) {
989
997
  aliasToRuleNames,
990
998
  currentItem,
991
999
  strings[currentItem] || "",
992
- md,
1000
+ getMarkdownIt,
993
1001
  config,
994
1002
  configParsers,
995
1003
  frontMatter,
@@ -1304,6 +1312,13 @@ module.exports = markdownlint;
1304
1312
 
1305
1313
  // Type declarations
1306
1314
 
1315
+ /**
1316
+ * Function to get an instance of the markdown-it parser.
1317
+ *
1318
+ * @callback GetMarkdownIt
1319
+ * @returns {import("markdown-it")}
1320
+ */
1321
+
1307
1322
  /**
1308
1323
  * Function to implement rule logic.
1309
1324
  *
@@ -1517,6 +1532,13 @@ module.exports = markdownlint;
1517
1532
  * @typedef {import("./configuration").Configuration} Configuration
1518
1533
  */
1519
1534
 
1535
+ /**
1536
+ * Configuration object for linting rules strictly. For the JSON schema, see
1537
+ * {@link ../schema/markdownlint-config-schema-strict.json}.
1538
+ *
1539
+ * @typedef {import("./configuration-strict").ConfigurationStrict} ConfigurationStrict
1540
+ */
1541
+
1520
1542
  /**
1521
1543
  * Rule configuration object.
1522
1544
  *
package/lib/md001.js CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, filterTokens } = require("../helpers");
5
+ const { addErrorDetailIf } = require("../helpers");
6
+ const { getHeadingLevel } = require("../helpers/micromark.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": "markdownit",
15
+ "parser": "micromark",
14
16
  "function": function MD001(params, onError) {
15
- let prevLevel = 0;
16
- filterTokens(params, "heading_open", function forToken(token) {
17
- const level = Number.parseInt(token.tag.slice(1), 10);
18
- if (prevLevel && (level > prevLevel)) {
19
- addErrorDetailIf(onError, token.lineNumber,
20
- "h" + (prevLevel + 1), "h" + level);
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, filterTokens, headingStyleFor } =
6
- require("../helpers");
5
+ const { addErrorDetailIf } = require("../helpers");
6
+ const { getHeadingLevel, getHeadingStyle } = require("../helpers/micromark.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": "markdownit",
15
+ "parser": "micromark",
15
16
  "function": function MD003(params, onError) {
16
17
  let style = String(params.config.style || "consistent");
17
- filterTokens(params, "heading_open", function forToken(token) {
18
- const styleForToken = headingStyleFor(token);
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 = /h[12]/.test(token.tag);
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(onError, token.lineNumber,
40
- expected, styleForToken);
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, listItemMarkerRe, unorderedListStyleFor } =
6
- require("../helpers");
7
- const { flattenedLists } = require("./cache");
5
+ const { addErrorDetailIf } = require("../helpers");
6
+ const { getDescendantsByType, getTokenParentOfType } = require("../helpers/micromark.cjs");
7
+ const { filterByTypesCached } = require("./cache");
8
8
 
9
- const expectedStyleToMarker = {
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 = Object.keys(expectedStyleToMarker);
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": "none",
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 list of flattenedLists()) {
33
- if (list.unordered) {
34
- if (expectedStyle === "consistent") {
35
- expectedStyle = unorderedListStyleFor(list.items[0]);
43
+ for (const listUnordered of filterByTypesCached([ "listUnordered" ])) {
44
+ let nesting = 0;
45
+ if (style === "sublist") {
46
+ /** @type {import("../helpers/micromark.cjs").Token | null} */
47
+ let parent = listUnordered;
48
+ // @ts-ignore
49
+ while ((parent = getTokenParentOfType(parent, [ "listOrdered", "listUnordered" ]))) {
50
+ nesting++;
36
51
  }
37
- for (const item of list.items) {
38
- const itemStyle = unorderedListStyleFor(item);
39
- if (style === "sublist") {
40
- const nesting = list.nesting;
41
- if (!nestingStyles[nesting]) {
42
- nestingStyles[nesting] =
43
- (itemStyle === nestingStyles[nesting - 1]) ?
44
- differentItemStyle[itemStyle] :
45
- itemStyle;
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
- addErrorDetailIf(
66
- onError,
67
- item.lineNumber,
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
  }
package/lib/md005.js CHANGED
@@ -3,7 +3,7 @@
3
3
  "use strict";
4
4
 
5
5
  const { addError, addErrorDetailIf } = require("../helpers");
6
- const { filterByTypes, inHtmlFlow } = require("../helpers/micromark.cjs");
6
+ const { filterByTypesCached } = require("./cache");
7
7
 
8
8
  // eslint-disable-next-line jsdoc/valid-types
9
9
  /** @type import("./markdownlint").Rule */
@@ -13,16 +13,7 @@ module.exports = {
13
13
  "tags": [ "bullet", "ul", "indentation" ],
14
14
  "parser": "micromark",
15
15
  "function": function MD005(params, onError) {
16
- // eslint-disable-next-line jsdoc/valid-types
17
- /** @type import("../helpers/micromark.cjs").Token[] */
18
- const micromarkTokens =
19
- // @ts-ignore
20
- params.parsers.micromark.tokens;
21
- const lists = filterByTypes(
22
- micromarkTokens,
23
- [ "listOrdered", "listUnordered" ]
24
- ).filter((list) => !inHtmlFlow(list));
25
- for (const list of lists) {
16
+ for (const list of filterByTypesCached([ "listOrdered", "listUnordered" ])) {
26
17
  const expectedIndent = list.startColumn - 1;
27
18
  let expectedEnd = 0;
28
19
  let endMatching = false;
@@ -38,8 +29,8 @@ module.exports = {
38
29
  lineNumber,
39
30
  expectedIndent,
40
31
  actualIndent,
41
- null,
42
- null,
32
+ undefined,
33
+ undefined,
43
34
  range
44
35
  // No fixInfo; MD007 handles this scenario better
45
36
  );