eslint-plugin-markdown-preferences 0.20.0 → 0.21.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/README.md CHANGED
@@ -68,6 +68,7 @@ export default [
68
68
  This plugin provides configs:
69
69
 
70
70
  - `*.configs.recommended` ... Recommended config provided by the plugin.
71
+ - `*.configs.standard` ... Enforces opinionated stylistic conventions. You can extend this to enforce any stylistic conventions you like.
71
72
 
72
73
  See [the rule list](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/) to get the `rules` that this plugin provides.
73
74
 
@@ -84,7 +85,8 @@ Is not supported.
84
85
  <!--RULES_SECTION_START-->
85
86
 
86
87
  The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) automatically fixes problems reported by rules which have a wrench 🔧 below.\
87
- The rules with the following star ⭐ are included in the configs.
88
+ The rules with the following ⭐ are included in the `recommended` config.\
89
+ The rules with the following 💄 are included in the `standard` config.
88
90
 
89
91
  <!--RULES_TABLE_START-->
90
92
 
@@ -94,12 +96,12 @@ The rules with the following star ⭐ are included in the configs.
94
96
 
95
97
  <!-- prettier-ignore-start -->
96
98
 
97
- | Rule ID | Description | Fixable | RECOMMENDED |
99
+ | Rule ID | Description | Fixable | Config |
98
100
  |:--------|:------------|:-------:|:-----------:|
99
101
  | [markdown-preferences/canonical-code-block-language](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/canonical-code-block-language.html) | enforce canonical language names in code blocks | 🔧 | |
100
102
  | [markdown-preferences/emoji-notation](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/emoji-notation.html) | enforce consistent emoji notation style in Markdown files. | 🔧 | |
101
103
  | [markdown-preferences/heading-casing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/heading-casing.html) | enforce consistent casing in headings. | 🔧 | |
102
- | [markdown-preferences/ordered-list-marker-start](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/ordered-list-marker-start.html) | enforce that ordered list markers start with 1 or 0 | 🔧 | |
104
+ | [markdown-preferences/ordered-list-marker-start](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/ordered-list-marker-start.html) | enforce that ordered list markers start with 1 or 0 | 🔧 | 💄 |
103
105
  | [markdown-preferences/prefer-inline-code-words](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/prefer-inline-code-words.html) | enforce the use of inline code for specific words. | 🔧 | |
104
106
  | [markdown-preferences/prefer-linked-words](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/prefer-linked-words.html) | enforce the specified word to be a link. | 🔧 | |
105
107
  | [markdown-preferences/table-header-casing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/table-header-casing.html) | enforce consistent casing in table header cells. | 🔧 | |
@@ -112,36 +114,37 @@ The rules with the following star ⭐ are included in the configs.
112
114
 
113
115
  <!-- prettier-ignore-start -->
114
116
 
115
- | Rule ID | Description | Fixable | RECOMMENDED |
117
+ | Rule ID | Description | Fixable | Config |
116
118
  |:--------|:------------|:-------:|:-----------:|
117
- | [markdown-preferences/atx-heading-closing-sequence-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/atx-heading-closing-sequence-length.html) | enforce consistent length for the closing sequence (trailing #s) in ATX headings. | 🔧 | |
118
- | [markdown-preferences/atx-heading-closing-sequence](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/atx-heading-closing-sequence.html) | enforce consistent use of closing sequence in ATX headings. | 🔧 | |
119
- | [markdown-preferences/blockquote-marker-alignment](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/blockquote-marker-alignment.html) | enforce consistent alignment of blockquote markers | 🔧 | |
120
- | [markdown-preferences/bullet-list-marker-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/bullet-list-marker-style.html) | enforce consistent bullet list (unordered list) marker style | 🔧 | |
121
- | [markdown-preferences/code-fence-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-length.html) | enforce consistent code fence length in fenced code blocks. | 🔧 | |
122
- | [markdown-preferences/code-fence-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-style.html) | enforce a consistent code fence style (backtick or tilde) in Markdown fenced code blocks. | 🔧 | |
119
+ | [markdown-preferences/atx-heading-closing-sequence-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/atx-heading-closing-sequence-length.html) | enforce consistent length for the closing sequence (trailing #s) in ATX headings. | 🔧 | 💄 |
120
+ | [markdown-preferences/atx-heading-closing-sequence](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/atx-heading-closing-sequence.html) | enforce consistent use of closing sequence in ATX headings. | 🔧 | 💄 |
121
+ | [markdown-preferences/blockquote-marker-alignment](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/blockquote-marker-alignment.html) | enforce consistent alignment of blockquote markers | 🔧 | ⭐💄 |
122
+ | [markdown-preferences/bullet-list-marker-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/bullet-list-marker-style.html) | enforce consistent bullet list (unordered list) marker style | 🔧 | 💄 |
123
+ | [markdown-preferences/code-fence-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-length.html) | enforce consistent code fence length in fenced code blocks. | 🔧 | 💄 |
124
+ | [markdown-preferences/code-fence-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-style.html) | enforce a consistent code fence style (backtick or tilde) in Markdown fenced code blocks. | 🔧 | 💄 |
123
125
  | [markdown-preferences/definitions-last](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/definitions-last.html) | require link definitions and footnote definitions to be placed at the end of the document | 🔧 | |
124
- | [markdown-preferences/emphasis-delimiters-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/emphasis-delimiters-style.html) | enforce a consistent delimiter style for emphasis and strong emphasis | 🔧 | |
125
- | [markdown-preferences/hard-linebreak-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/hard-linebreak-style.html) | enforce consistent hard linebreak style. | 🔧 | |
126
- | [markdown-preferences/level1-heading-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/level1-heading-style.html) | enforce consistent style for level 1 headings | 🔧 | |
127
- | [markdown-preferences/level2-heading-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/level2-heading-style.html) | enforce consistent style for level 2 headings | 🔧 | |
128
- | [markdown-preferences/list-marker-alignment](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/list-marker-alignment.html) | enforce consistent alignment of list markers | 🔧 | |
129
- | [markdown-preferences/no-laziness-blockquotes](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-laziness-blockquotes.html) | disallow laziness in blockquotes | | |
130
- | [markdown-preferences/no-multiple-empty-lines](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multiple-empty-lines.html) | disallow multiple empty lines in Markdown files. | 🔧 | |
131
- | [markdown-preferences/no-text-backslash-linebreak](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-text-backslash-linebreak.html) | disallow text backslash at the end of a line. | | |
132
- | [markdown-preferences/no-trailing-spaces](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-trailing-spaces.html) | disallow trailing whitespace at the end of lines in Markdown files. | 🔧 | |
133
- | [markdown-preferences/ordered-list-marker-sequence](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/ordered-list-marker-sequence.html) | enforce that ordered list markers use sequential numbers | 🔧 | |
134
- | [markdown-preferences/ordered-list-marker-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/ordered-list-marker-style.html) | enforce consistent ordered list marker style | 🔧 | |
135
- | [markdown-preferences/padding-line-between-blocks](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/padding-line-between-blocks.html) | require or disallow padding lines between blocks | 🔧 | |
136
- | [markdown-preferences/prefer-autolinks](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/prefer-autolinks.html) | enforce the use of autolinks for URLs | 🔧 | |
137
- | [markdown-preferences/prefer-fenced-code-blocks](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/prefer-fenced-code-blocks.html) | enforce the use of fenced code blocks over indented code blocks | 🔧 | |
126
+ | [markdown-preferences/emphasis-delimiters-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/emphasis-delimiters-style.html) | enforce a consistent delimiter style for emphasis and strong emphasis | 🔧 | 💄 |
127
+ | [markdown-preferences/hard-linebreak-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/hard-linebreak-style.html) | enforce consistent hard linebreak style. | 🔧 | ⭐💄 |
128
+ | [markdown-preferences/level1-heading-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/level1-heading-style.html) | enforce consistent style for level 1 headings | 🔧 | 💄 |
129
+ | [markdown-preferences/level2-heading-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/level2-heading-style.html) | enforce consistent style for level 2 headings | 🔧 | 💄 |
130
+ | [markdown-preferences/list-marker-alignment](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/list-marker-alignment.html) | enforce consistent alignment of list markers | 🔧 | ⭐💄 |
131
+ | [markdown-preferences/no-laziness-blockquotes](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-laziness-blockquotes.html) | disallow laziness in blockquotes | | ⭐💄 |
132
+ | [markdown-preferences/no-multi-spaces](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multi-spaces.html) | disallow multiple spaces | 🔧 | 💄 |
133
+ | [markdown-preferences/no-multiple-empty-lines](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multiple-empty-lines.html) | disallow multiple empty lines in Markdown files. | 🔧 | 💄 |
134
+ | [markdown-preferences/no-text-backslash-linebreak](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-text-backslash-linebreak.html) | disallow text backslash at the end of a line. | | ⭐💄 |
135
+ | [markdown-preferences/no-trailing-spaces](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-trailing-spaces.html) | disallow trailing whitespace at the end of lines in Markdown files. | 🔧 | 💄 |
136
+ | [markdown-preferences/ordered-list-marker-sequence](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/ordered-list-marker-sequence.html) | enforce that ordered list markers use sequential numbers | 🔧 | 💄 |
137
+ | [markdown-preferences/ordered-list-marker-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/ordered-list-marker-style.html) | enforce consistent ordered list marker style | 🔧 | 💄 |
138
+ | [markdown-preferences/padding-line-between-blocks](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/padding-line-between-blocks.html) | require or disallow padding lines between blocks | 🔧 | 💄 |
139
+ | [markdown-preferences/prefer-autolinks](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/prefer-autolinks.html) | enforce the use of autolinks for URLs | 🔧 | ⭐💄 |
140
+ | [markdown-preferences/prefer-fenced-code-blocks](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/prefer-fenced-code-blocks.html) | enforce the use of fenced code blocks over indented code blocks | 🔧 | ⭐💄 |
138
141
  | [markdown-preferences/prefer-link-reference-definitions](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/prefer-link-reference-definitions.html) | enforce using link reference definitions instead of inline links | 🔧 | |
139
- | [markdown-preferences/setext-heading-underline-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/setext-heading-underline-length.html) | enforce setext heading underline length | 🔧 | |
140
- | [markdown-preferences/sort-definitions](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/sort-definitions.html) | enforce a specific order for link definitions and footnote definitions | 🔧 | |
141
- | [markdown-preferences/strikethrough-delimiters-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/strikethrough-delimiters-style.html) | enforce a consistent delimiter style for strikethrough | 🔧 | |
142
- | [markdown-preferences/thematic-break-character-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/thematic-break-character-style.html) | enforce consistent character style for thematic breaks (horizontal rules) in Markdown. | 🔧 | |
143
- | [markdown-preferences/thematic-break-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/thematic-break-length.html) | enforce consistent length for thematic breaks (horizontal rules) in Markdown. | 🔧 | |
144
- | [markdown-preferences/thematic-break-sequence-pattern](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/thematic-break-sequence-pattern.html) | enforce consistent repeating patterns for thematic breaks (horizontal rules) in Markdown. | 🔧 | |
142
+ | [markdown-preferences/setext-heading-underline-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/setext-heading-underline-length.html) | enforce setext heading underline length | 🔧 | 💄 |
143
+ | [markdown-preferences/sort-definitions](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/sort-definitions.html) | enforce a specific order for link definitions and footnote definitions | 🔧 | 💄 |
144
+ | [markdown-preferences/strikethrough-delimiters-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/strikethrough-delimiters-style.html) | enforce a consistent delimiter style for strikethrough | 🔧 | 💄 |
145
+ | [markdown-preferences/thematic-break-character-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/thematic-break-character-style.html) | enforce consistent character style for thematic breaks (horizontal rules) in Markdown. | 🔧 | 💄 |
146
+ | [markdown-preferences/thematic-break-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/thematic-break-length.html) | enforce consistent length for thematic breaks (horizontal rules) in Markdown. | 🔧 | 💄 |
147
+ | [markdown-preferences/thematic-break-sequence-pattern](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/thematic-break-sequence-pattern.html) | enforce consistent repeating patterns for thematic breaks (horizontal rules) in Markdown. | 🔧 | 💄 |
145
148
 
146
149
  <!-- prettier-ignore-end -->
147
150
 
package/lib/index.d.ts CHANGED
@@ -90,6 +90,11 @@ interface RuleOptions {
90
90
  * @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-laziness-blockquotes.html
91
91
  */
92
92
  'markdown-preferences/no-laziness-blockquotes'?: Linter.RuleEntry<[]>;
93
+ /**
94
+ * disallow multiple spaces
95
+ * @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multi-spaces.html
96
+ */
97
+ 'markdown-preferences/no-multi-spaces'?: Linter.RuleEntry<[]>;
93
98
  /**
94
99
  * disallow multiple empty lines in Markdown files.
95
100
  * @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multiple-empty-lines.html
@@ -351,6 +356,20 @@ type MarkdownPreferencesThematicBreakSequencePattern = [] | [{
351
356
  pattern: (string | string | string);
352
357
  }];
353
358
  declare namespace recommended_d_exports {
359
+ export { files$1 as files, language$1 as language, languageOptions$1 as languageOptions, name$2 as name, plugins$1 as plugins, rules$2 as rules };
360
+ }
361
+ declare const name$2 = "markdown-preferences/recommended";
362
+ declare const files$1: string[];
363
+ declare const language$1 = "markdown/gfm";
364
+ declare const languageOptions$1: {
365
+ frontmatter: string;
366
+ };
367
+ declare const plugins$1: {
368
+ markdown: typeof markdown;
369
+ readonly "markdown-preferences": ESLint.Plugin;
370
+ };
371
+ declare const rules$2: Linter.RulesRecord;
372
+ declare namespace standard_d_exports {
354
373
  export { files, language, languageOptions, name$1 as name, plugins, rules$1 as rules };
355
374
  }
356
375
  declare const name$1 = "markdown-preferences/recommended";
@@ -368,11 +387,12 @@ declare namespace meta_d_exports {
368
387
  export { name, version };
369
388
  }
370
389
  declare const name: "eslint-plugin-markdown-preferences";
371
- declare const version: "0.20.0";
390
+ declare const version: "0.21.0";
372
391
  //#endregion
373
392
  //#region src/index.d.ts
374
393
  declare const configs: {
375
394
  recommended: typeof recommended_d_exports;
395
+ standard: typeof standard_d_exports;
376
396
  };
377
397
  declare const rules: Record<string, RuleDefinition>;
378
398
  declare const resources: {
@@ -383,6 +403,7 @@ declare const _default: {
383
403
  meta: typeof meta_d_exports;
384
404
  configs: {
385
405
  recommended: typeof recommended_d_exports;
406
+ standard: typeof standard_d_exports;
386
407
  };
387
408
  rules: Record<string, RuleDefinition<_eslint_core0.RuleDefinitionTypeOptions>>;
388
409
  resources: {
package/lib/index.js CHANGED
@@ -30,6 +30,12 @@ function createRule(ruleName, rule) {
30
30
  //#endregion
31
31
  //#region src/utils/ast.ts
32
32
  /**
33
+ * Get the parent of a node.
34
+ */
35
+ function getParent(sourceCode, node) {
36
+ return sourceCode.getParent(node);
37
+ }
38
+ /**
33
39
  * Get the kind of heading.
34
40
  */
35
41
  function getHeadingKind(sourceCode, node) {
@@ -150,6 +156,12 @@ function isSpaceOrTab(string) {
150
156
  function isPunctuation(char) {
151
157
  return /^[\p{P}\p{S}]+$/u.test(char);
152
158
  }
159
+ /**
160
+ * Check if the character is an ASCII control character
161
+ */
162
+ function isAsciiControlCharacter(char) {
163
+ return /^[\x00-\x1f\x7f]+$/u.test(char);
164
+ }
153
165
 
154
166
  //#endregion
155
167
  //#region src/utils/atx-heading.ts
@@ -387,7 +399,7 @@ var atx_heading_closing_sequence_length_default = createRule("atx-heading-closin
387
399
  type: "layout",
388
400
  docs: {
389
401
  description: "enforce consistent length for the closing sequence (trailing #s) in ATX headings.",
390
- categories: [],
402
+ categories: ["standard"],
391
403
  listCategory: "Stylistic"
392
404
  },
393
405
  fixable: "code",
@@ -413,7 +425,8 @@ var atx_heading_closing_sequence_length_default = createRule("atx-heading-closin
413
425
  },
414
426
  create(context) {
415
427
  const sourceCode = context.sourceCode;
416
- const option = Object.assign({ mode: "match-opening" }, context.options[0] || {});
428
+ const option = context.options[0] || {};
429
+ const mode = option.mode || "match-opening";
417
430
  /**
418
431
  * Verify the closing sequence length of an ATX heading.
419
432
  */
@@ -433,12 +446,12 @@ var atx_heading_closing_sequence_length_default = createRule("atx-heading-closin
433
446
  }
434
447
  });
435
448
  }
436
- if (option.mode === "match-opening") return { heading(node) {
449
+ if (mode === "match-opening") return { heading(node) {
437
450
  const parsed = parseATXHeading(sourceCode, node);
438
451
  if (!parsed || parsed.closingSequence == null) return;
439
452
  verifyATXHeadingClosingSequenceLength(parsed, node, node.depth);
440
453
  } };
441
- if (option.mode === "length") {
454
+ if (mode === "length") {
442
455
  const expected = option.length || 2;
443
456
  return { heading(node) {
444
457
  const parsed = parseATXHeading(sourceCode, node);
@@ -446,7 +459,7 @@ var atx_heading_closing_sequence_length_default = createRule("atx-heading-closin
446
459
  verifyATXHeadingClosingSequenceLength(parsed, node, expected);
447
460
  } };
448
461
  }
449
- if (option.mode === "fixed-line-length") {
462
+ if (mode === "fixed-line-length") {
450
463
  const totalLength = option.length || 80;
451
464
  return { heading(node) {
452
465
  const parsed = parseATXHeading(sourceCode, node);
@@ -454,7 +467,7 @@ var atx_heading_closing_sequence_length_default = createRule("atx-heading-closin
454
467
  verifyATXHeadingClosingSequenceLength(parsed, node, totalLength - getContentLength(parsed));
455
468
  } };
456
469
  }
457
- if (option.mode === "consistent") {
470
+ if (mode === "consistent") {
458
471
  let expectedLength = null;
459
472
  return { heading(node) {
460
473
  const parsed = parseATXHeading(sourceCode, node);
@@ -463,7 +476,7 @@ var atx_heading_closing_sequence_length_default = createRule("atx-heading-closin
463
476
  else verifyATXHeadingClosingSequenceLength(parsed, node, expectedLength);
464
477
  } };
465
478
  }
466
- if (option.mode === "consistent-line-length") {
479
+ if (mode === "consistent-line-length") {
467
480
  const headings = [];
468
481
  return {
469
482
  heading(node) {
@@ -526,7 +539,7 @@ var atx_heading_closing_sequence_default = createRule("atx-heading-closing-seque
526
539
  type: "layout",
527
540
  docs: {
528
541
  description: "enforce consistent use of closing sequence in ATX headings.",
529
- categories: [],
542
+ categories: ["standard"],
530
543
  listCategory: "Stylistic"
531
544
  },
532
545
  fixable: "code",
@@ -628,7 +641,7 @@ var blockquote_marker_alignment_default = createRule("blockquote-marker-alignmen
628
641
  type: "layout",
629
642
  docs: {
630
643
  description: "enforce consistent alignment of blockquote markers",
631
- categories: ["recommended"],
644
+ categories: ["recommended", "standard"],
632
645
  listCategory: "Stylistic"
633
646
  },
634
647
  fixable: "whitespace",
@@ -750,7 +763,7 @@ var bullet_list_marker_style_default = createRule("bullet-list-marker-style", {
750
763
  type: "layout",
751
764
  docs: {
752
765
  description: "enforce consistent bullet list (unordered list) marker style",
753
- categories: [],
766
+ categories: ["standard"],
754
767
  listCategory: "Stylistic"
755
768
  },
756
769
  fixable: "code",
@@ -926,7 +939,7 @@ function parseFencedCodeBlock(sourceCode, node) {
926
939
  }
927
940
  }
928
941
  };
929
- const language$1 = languageText ? {
942
+ const language$2 = languageText ? {
930
943
  text: languageText,
931
944
  range: [openingFence.range[1] + spaceAfterOpeningFenceLength, openingFence.range[1] + spaceAfterOpeningFenceLength + languageText.length],
932
945
  loc: {
@@ -940,23 +953,23 @@ function parseFencedCodeBlock(sourceCode, node) {
940
953
  }
941
954
  }
942
955
  } : null;
943
- const meta = language$1 && metaText ? {
956
+ const meta = language$2 && metaText ? {
944
957
  text: metaText,
945
- range: [language$1.range[1] + spaceAfterLanguageLength, language$1.range[1] + spaceAfterLanguageLength + metaText.length],
958
+ range: [language$2.range[1] + spaceAfterLanguageLength, language$2.range[1] + spaceAfterLanguageLength + metaText.length],
946
959
  loc: {
947
960
  start: {
948
- line: language$1.loc.end.line,
949
- column: language$1.loc.end.column + spaceAfterLanguageLength
961
+ line: language$2.loc.end.line,
962
+ column: language$2.loc.end.column + spaceAfterLanguageLength
950
963
  },
951
964
  end: {
952
- line: language$1.loc.end.line,
953
- column: language$1.loc.end.column + spaceAfterLanguageLength + metaText.length
965
+ line: language$2.loc.end.line,
966
+ column: language$2.loc.end.column + spaceAfterLanguageLength + metaText.length
954
967
  }
955
968
  }
956
969
  } : null;
957
970
  return {
958
971
  openingFence,
959
- language: language$1,
972
+ language: language$2,
960
973
  meta,
961
974
  closingFence: {
962
975
  text: closingFenceText,
@@ -1054,7 +1067,7 @@ var code_fence_length_default = createRule("code-fence-length", {
1054
1067
  type: "layout",
1055
1068
  docs: {
1056
1069
  description: "enforce consistent code fence length in fenced code blocks.",
1057
- categories: [],
1070
+ categories: ["standard"],
1058
1071
  listCategory: "Stylistic"
1059
1072
  },
1060
1073
  fixable: "code",
@@ -1104,7 +1117,7 @@ var code_fence_length_default = createRule("code-fence-length", {
1104
1117
  * Get the effective options for the given code block node.
1105
1118
  */
1106
1119
  function getOptionForCode(node) {
1107
- const override = options.overrides?.find((o) => o.lang === node.lang);
1120
+ const override = options.overrides ? [...options.overrides].reverse().find((o) => o.lang === node.lang) : null;
1108
1121
  return {
1109
1122
  length: override?.length ?? options.length ?? 3,
1110
1123
  fallbackLength: override?.fallbackLength ?? options.fallbackLength ?? "minimum"
@@ -1192,7 +1205,7 @@ var code_fence_style_default = createRule("code-fence-style", {
1192
1205
  type: "layout",
1193
1206
  docs: {
1194
1207
  description: "enforce a consistent code fence style (backtick or tilde) in Markdown fenced code blocks.",
1195
- categories: [],
1208
+ categories: ["standard"],
1196
1209
  listCategory: "Stylistic"
1197
1210
  },
1198
1211
  fixable: "code",
@@ -3229,13 +3242,13 @@ var EmojiData = class {
3229
3242
  get emojiToColon() {
3230
3243
  if (this._emojiToColon) return this._emojiToColon;
3231
3244
  const emojiToCode = this._emojiToColon = Object.create(null);
3232
- for (const [name$2, unicode] of this.entries) emojiToCode[unicode] = `:${name$2}:`;
3245
+ for (const [name$3, unicode] of this.entries) emojiToCode[unicode] = `:${name$3}:`;
3233
3246
  return emojiToCode;
3234
3247
  }
3235
3248
  get colonToEmoji() {
3236
3249
  if (this._colonToEmoji) return this._colonToEmoji;
3237
3250
  const colonToEmoji = this._colonToEmoji = Object.create(null);
3238
- for (const [name$2, unicode] of this.entries) colonToEmoji[`:${name$2}:`] = unicode;
3251
+ for (const [name$3, unicode] of this.entries) colonToEmoji[`:${name$3}:`] = unicode;
3239
3252
  return colonToEmoji;
3240
3253
  }
3241
3254
  };
@@ -3284,7 +3297,7 @@ var emoji_notation_default = createRule("emoji-notation", {
3284
3297
  const ignoreUnknown = options.ignoreUnknown ?? true;
3285
3298
  const ignoreList = (options.ignoreList || []).map((s) => toRegExp(s)).flatMap((re) => {
3286
3299
  const result = [re];
3287
- for (const [name$2, unicode] of emojiData.entries) if (re.test(`:${name$2}:`) || re.test(unicode)) result.push(toRegExp(`:${name$2}:`), toRegExp(unicode));
3300
+ for (const [name$3, unicode] of emojiData.entries) if (re.test(`:${name$3}:`) || re.test(unicode)) result.push(toRegExp(`:${name$3}:`), toRegExp(unicode));
3288
3301
  return result;
3289
3302
  });
3290
3303
  const isIgnoreBase = (s) => ignoreList.some((re) => re.test(s));
@@ -3365,7 +3378,7 @@ var emphasis_delimiters_style_default = createRule("emphasis-delimiters-style",
3365
3378
  type: "layout",
3366
3379
  docs: {
3367
3380
  description: "enforce a consistent delimiter style for emphasis and strong emphasis",
3368
- categories: [],
3381
+ categories: ["standard"],
3369
3382
  listCategory: "Stylistic"
3370
3383
  },
3371
3384
  fixable: "code",
@@ -3547,7 +3560,7 @@ var hard_linebreak_style_default = createRule("hard-linebreak-style", {
3547
3560
  type: "layout",
3548
3561
  docs: {
3549
3562
  description: "enforce consistent hard linebreak style.",
3550
- categories: ["recommended"],
3563
+ categories: ["recommended", "standard"],
3551
3564
  listCategory: "Stylistic"
3552
3565
  },
3553
3566
  fixable: "code",
@@ -4472,7 +4485,7 @@ var level1_heading_style_default = createRule("level1-heading-style", {
4472
4485
  type: "layout",
4473
4486
  docs: {
4474
4487
  description: "enforce consistent style for level 1 headings",
4475
- categories: [],
4488
+ categories: ["standard"],
4476
4489
  listCategory: "Stylistic"
4477
4490
  },
4478
4491
  fixable: "code",
@@ -4551,7 +4564,7 @@ var level2_heading_style_default = createRule("level2-heading-style", {
4551
4564
  type: "layout",
4552
4565
  docs: {
4553
4566
  description: "enforce consistent style for level 2 headings",
4554
- categories: [],
4567
+ categories: ["standard"],
4555
4568
  listCategory: "Stylistic"
4556
4569
  },
4557
4570
  fixable: "code",
@@ -4634,7 +4647,7 @@ var list_marker_alignment_default = createRule("list-marker-alignment", {
4634
4647
  type: "layout",
4635
4648
  docs: {
4636
4649
  description: "enforce consistent alignment of list markers",
4637
- categories: ["recommended"],
4650
+ categories: ["recommended", "standard"],
4638
4651
  listCategory: "Stylistic"
4639
4652
  },
4640
4653
  fixable: "whitespace",
@@ -4721,7 +4734,7 @@ var no_laziness_blockquotes_default = createRule("no-laziness-blockquotes", {
4721
4734
  type: "problem",
4722
4735
  docs: {
4723
4736
  description: "disallow laziness in blockquotes",
4724
- categories: ["recommended"],
4737
+ categories: ["recommended", "standard"],
4725
4738
  listCategory: "Stylistic"
4726
4739
  },
4727
4740
  fixable: void 0,
@@ -4821,6 +4834,547 @@ var no_laziness_blockquotes_default = createRule("no-laziness-blockquotes", {
4821
4834
  }
4822
4835
  });
4823
4836
 
4837
+ //#endregion
4838
+ //#region src/utils/link-definition.ts
4839
+ /**
4840
+ * Parse the link definition.
4841
+ */
4842
+ function parseLinkDefinition(sourceCode, node) {
4843
+ const text = sourceCode.getText(node);
4844
+ const parsed = parseLinkDefinitionFromText(text);
4845
+ if (!parsed) return null;
4846
+ const nodeRange = sourceCode.getRange(node);
4847
+ const labelRange = [nodeRange[0] + parsed.label.range[0], nodeRange[0] + parsed.label.range[1]];
4848
+ const destinationRange = [nodeRange[0] + parsed.destination.range[0], nodeRange[0] + parsed.destination.range[1]];
4849
+ return {
4850
+ label: {
4851
+ text: parsed.label.text,
4852
+ range: labelRange,
4853
+ loc: getSourceLocationFromRange(sourceCode, node, labelRange)
4854
+ },
4855
+ destination: {
4856
+ type: parsed.destination.type,
4857
+ text: parsed.destination.text,
4858
+ range: destinationRange,
4859
+ loc: getSourceLocationFromRange(sourceCode, node, destinationRange)
4860
+ },
4861
+ title: parsed.title ? {
4862
+ type: parsed.title.type,
4863
+ text: parsed.title.text,
4864
+ range: [nodeRange[0] + parsed.title.range[0], nodeRange[0] + parsed.title.range[1]],
4865
+ loc: getSourceLocationFromRange(sourceCode, node, [nodeRange[0] + parsed.title.range[0], nodeRange[0] + parsed.title.range[1]])
4866
+ } : null
4867
+ };
4868
+ }
4869
+ /**
4870
+ * Parse the link definition from the given text.
4871
+ */
4872
+ function parseLinkDefinitionFromText(text) {
4873
+ let index = 0;
4874
+ if (text[index] !== "[") return null;
4875
+ const labelStartIndex = index;
4876
+ index++;
4877
+ if (!skipUntilEnd((c) => c === "]")) return null;
4878
+ index++;
4879
+ const labelRange = [labelStartIndex, index];
4880
+ if (!text.slice(labelRange[0] + 1, labelRange[1] - 1).trim()) return null;
4881
+ if (text[index] !== ":") return null;
4882
+ const label = {
4883
+ text: text.slice(...labelRange),
4884
+ range: labelRange
4885
+ };
4886
+ index++;
4887
+ skipSpaces();
4888
+ let destination;
4889
+ const destinationStartIndex = index;
4890
+ if (text[index] === "<") {
4891
+ index++;
4892
+ if (!skipUntilEnd((c) => c === ">")) return null;
4893
+ index++;
4894
+ const destinationRange = [destinationStartIndex, index];
4895
+ destination = {
4896
+ type: "angle-bracketed",
4897
+ text: text.slice(...destinationRange),
4898
+ range: destinationRange
4899
+ };
4900
+ } else {
4901
+ if (text.length <= index) return null;
4902
+ skipUntilEnd((c) => isWhitespace(c) || isAsciiControlCharacter(c));
4903
+ const destinationRange = [destinationStartIndex, index];
4904
+ destination = {
4905
+ type: "plain",
4906
+ text: text.slice(...destinationRange),
4907
+ range: destinationRange
4908
+ };
4909
+ }
4910
+ skipSpaces();
4911
+ if (text.length <= index) return {
4912
+ label,
4913
+ destination,
4914
+ title: null
4915
+ };
4916
+ let title;
4917
+ const titleStartIndex = index;
4918
+ const startChar = text[index];
4919
+ if (startChar === "'" || startChar === "\"" || startChar === "(") {
4920
+ index++;
4921
+ const endChar = startChar === "(" ? ")" : startChar;
4922
+ if (!skipUntilEnd((c) => c === endChar)) return null;
4923
+ index++;
4924
+ const titleRange = [titleStartIndex, index];
4925
+ title = {
4926
+ type: startChar === "'" ? "single-quoted" : startChar === "\"" ? "double-quoted" : "parenthesized",
4927
+ text: text.slice(...titleRange),
4928
+ range: titleRange
4929
+ };
4930
+ } else return null;
4931
+ skipSpaces();
4932
+ if (index < text.length) return null;
4933
+ return {
4934
+ label,
4935
+ destination,
4936
+ title
4937
+ };
4938
+ /**
4939
+ * Skip spaces
4940
+ */
4941
+ function skipSpaces() {
4942
+ while (index < text.length && isWhitespace(text[index])) index++;
4943
+ }
4944
+ /**
4945
+ * Skip until the end by the given condition
4946
+ */
4947
+ function skipUntilEnd(checkEnd) {
4948
+ while (index < text.length) {
4949
+ const c = text[index];
4950
+ if (checkEnd(c)) return true;
4951
+ index++;
4952
+ if (c !== "\\") continue;
4953
+ if (index < text.length && (c === "\\" || checkEnd(text[index]))) index++;
4954
+ }
4955
+ return false;
4956
+ }
4957
+ }
4958
+
4959
+ //#endregion
4960
+ //#region src/utils/link.ts
4961
+ /**
4962
+ * Parse the inline link.
4963
+ */
4964
+ function parseInlineLink(sourceCode, node) {
4965
+ const kind = getLinkKind(sourceCode, node);
4966
+ if (kind !== "inline") return null;
4967
+ const nodeRange = sourceCode.getRange(node);
4968
+ let labelRange;
4969
+ if (node.children.length === 0) labelRange = [nodeRange[0], sourceCode.text.indexOf("]", nodeRange[0]) + 1];
4970
+ else {
4971
+ const lastChildRange = sourceCode.getRange(node.children[node.children.length - 1]);
4972
+ labelRange = [nodeRange[0], sourceCode.text.indexOf("]", lastChildRange[1]) + 1];
4973
+ }
4974
+ const parsed = parseInlineLinkDestAndTitleFromText(sourceCode.text.slice(labelRange[1], nodeRange[1]));
4975
+ if (!parsed) return null;
4976
+ const destinationRange = [labelRange[1] + parsed.destination.range[0], labelRange[1] + parsed.destination.range[1]];
4977
+ return {
4978
+ label: {
4979
+ range: labelRange,
4980
+ loc: getSourceLocationFromRange(sourceCode, node, labelRange)
4981
+ },
4982
+ destination: {
4983
+ type: parsed.destination.type,
4984
+ text: parsed.destination.text,
4985
+ range: destinationRange,
4986
+ loc: getSourceLocationFromRange(sourceCode, node, destinationRange)
4987
+ },
4988
+ title: parsed.title ? {
4989
+ type: parsed.title.type,
4990
+ text: parsed.title.text,
4991
+ range: [labelRange[0] + parsed.title.range[0], labelRange[0] + parsed.title.range[1]],
4992
+ loc: getSourceLocationFromRange(sourceCode, node, [labelRange[0] + parsed.title.range[0], labelRange[0] + parsed.title.range[1]])
4993
+ } : null
4994
+ };
4995
+ }
4996
+ /**
4997
+ * Parse the inline link destination and link title from the given text.
4998
+ */
4999
+ function parseInlineLinkDestAndTitleFromText(text) {
5000
+ let index = 0;
5001
+ if (text[index] !== "(") return null;
5002
+ index++;
5003
+ skipSpaces();
5004
+ let destination;
5005
+ const destinationStartIndex = index;
5006
+ if (text[index] === "<") {
5007
+ index++;
5008
+ if (!skipUntilEnd((c) => c === ">")) return null;
5009
+ index++;
5010
+ const destinationRange = [destinationStartIndex, index];
5011
+ destination = {
5012
+ type: "angle-bracketed",
5013
+ text: text.slice(...destinationRange),
5014
+ range: destinationRange
5015
+ };
5016
+ } else {
5017
+ if (text.length <= index) return null;
5018
+ skipUntilEnd((c) => isWhitespace(c) || isAsciiControlCharacter(c) || c === ")");
5019
+ const destinationRange = [destinationStartIndex, index];
5020
+ destination = {
5021
+ type: "plain",
5022
+ text: text.slice(...destinationRange),
5023
+ range: destinationRange
5024
+ };
5025
+ }
5026
+ skipSpaces();
5027
+ if (text[index] === ")") {
5028
+ index++;
5029
+ skipSpaces();
5030
+ if (index < text.length) return null;
5031
+ return {
5032
+ destination,
5033
+ title: null
5034
+ };
5035
+ }
5036
+ if (text.length <= index) return null;
5037
+ let title;
5038
+ const titleStartIndex = index;
5039
+ const startChar = text[index];
5040
+ if (startChar === "'" || startChar === "\"" || startChar === "(") {
5041
+ index++;
5042
+ const endChar = startChar === "(" ? ")" : startChar;
5043
+ if (!skipUntilEnd((c) => c === endChar)) return null;
5044
+ index++;
5045
+ const titleRange = [titleStartIndex, index];
5046
+ title = {
5047
+ type: startChar === "'" ? "single-quoted" : startChar === "\"" ? "double-quoted" : "parenthesized",
5048
+ text: text.slice(...titleRange),
5049
+ range: titleRange
5050
+ };
5051
+ } else return null;
5052
+ skipSpaces();
5053
+ if (text[index] !== ")") return null;
5054
+ index++;
5055
+ skipSpaces();
5056
+ if (index < text.length) return null;
5057
+ return {
5058
+ destination,
5059
+ title
5060
+ };
5061
+ /**
5062
+ * Skip spaces
5063
+ */
5064
+ function skipSpaces() {
5065
+ while (index < text.length && isWhitespace(text[index])) index++;
5066
+ }
5067
+ /**
5068
+ * Skip until the end by the given condition
5069
+ */
5070
+ function skipUntilEnd(checkEnd) {
5071
+ while (index < text.length) {
5072
+ const c = text[index];
5073
+ if (checkEnd(c)) return true;
5074
+ index++;
5075
+ if (c !== "\\") continue;
5076
+ if (index < text.length && (c === "\\" || checkEnd(text[index]))) index++;
5077
+ }
5078
+ return false;
5079
+ }
5080
+ }
5081
+
5082
+ //#endregion
5083
+ //#region src/utils/image.ts
5084
+ /**
5085
+ * Parse the image.
5086
+ */
5087
+ function parseImage(sourceCode, node) {
5088
+ const text = sourceCode.getText(node);
5089
+ const parsed = parseImageFromText(text);
5090
+ if (!parsed) return null;
5091
+ const nodeRange = sourceCode.getRange(node);
5092
+ const labelRange = [nodeRange[0] + parsed.label.range[0], nodeRange[0] + parsed.label.range[1]];
5093
+ const destinationRange = [nodeRange[0] + parsed.destination.range[0], nodeRange[0] + parsed.destination.range[1]];
5094
+ return {
5095
+ label: {
5096
+ range: labelRange,
5097
+ loc: getSourceLocationFromRange(sourceCode, node, labelRange)
5098
+ },
5099
+ destination: {
5100
+ type: parsed.destination.type,
5101
+ text: parsed.destination.text,
5102
+ range: destinationRange,
5103
+ loc: getSourceLocationFromRange(sourceCode, node, destinationRange)
5104
+ },
5105
+ title: parsed.title ? {
5106
+ type: parsed.title.type,
5107
+ text: parsed.title.text,
5108
+ range: [nodeRange[0] + parsed.title.range[0], nodeRange[0] + parsed.title.range[1]],
5109
+ loc: getSourceLocationFromRange(sourceCode, node, [nodeRange[0] + parsed.title.range[0], nodeRange[0] + parsed.title.range[1]])
5110
+ } : null
5111
+ };
5112
+ }
5113
+ /**
5114
+ * Parse the image from the given text.
5115
+ */
5116
+ function parseImageFromText(text) {
5117
+ if (!text.startsWith("![")) return null;
5118
+ let index = text.length - 1;
5119
+ skipSpaces();
5120
+ if (text[index] !== ")") return null;
5121
+ index--;
5122
+ skipSpaces();
5123
+ let title = null;
5124
+ const titleEndIndex = index + 1;
5125
+ const endChar = text[index];
5126
+ if (endChar === "'" || endChar === "\"" || endChar === ")") {
5127
+ index--;
5128
+ const startChar = endChar === ")" ? "(" : endChar;
5129
+ if (skipUntilStart((c) => c === startChar)) {
5130
+ const titleRange = [index, titleEndIndex];
5131
+ index--;
5132
+ title = {
5133
+ type: startChar === "'" ? "single-quoted" : startChar === "\"" ? "double-quoted" : "parenthesized",
5134
+ text: text.slice(...titleRange),
5135
+ range: titleRange
5136
+ };
5137
+ skipSpaces();
5138
+ }
5139
+ }
5140
+ if (title == null) index = titleEndIndex - 1;
5141
+ let destination;
5142
+ const destinationEndIndex = index + 1;
5143
+ if (text[index] === ">") {
5144
+ index--;
5145
+ if (!skipUntilStart((c) => c === "<")) return null;
5146
+ const destinationRange = [index, destinationEndIndex];
5147
+ index--;
5148
+ destination = {
5149
+ type: "angle-bracketed",
5150
+ text: text.slice(...destinationRange),
5151
+ range: destinationRange
5152
+ };
5153
+ } else {
5154
+ if (text.length <= index) return null;
5155
+ skipUntilStart((c) => isWhitespace(c) || isAsciiControlCharacter(c) || c === "(");
5156
+ const destinationRange = [index + 1, destinationEndIndex];
5157
+ destination = {
5158
+ type: "plain",
5159
+ text: text.slice(...destinationRange),
5160
+ range: destinationRange
5161
+ };
5162
+ }
5163
+ skipSpaces();
5164
+ if (text[index] !== "(") return null;
5165
+ index--;
5166
+ if (text[index] !== "]") return null;
5167
+ const labelRange = [1, index + 1];
5168
+ return {
5169
+ label: { range: labelRange },
5170
+ destination,
5171
+ title
5172
+ };
5173
+ /**
5174
+ * Skip spaces
5175
+ */
5176
+ function skipSpaces() {
5177
+ while (index >= 0 && isWhitespace(text[index])) index--;
5178
+ }
5179
+ /**
5180
+ * Skip until the start by the given condition
5181
+ */
5182
+ function skipUntilStart(checkStart) {
5183
+ while (index >= 0) {
5184
+ const c = text[index];
5185
+ if (checkStart(c)) {
5186
+ if (index > 1) {
5187
+ let escapeText = "";
5188
+ while (text.endsWith(`${escapeText}\\`, index)) escapeText += "\\";
5189
+ if (escapeText.length % 2 === 1) {
5190
+ index -= escapeText.length + 1;
5191
+ continue;
5192
+ }
5193
+ }
5194
+ return true;
5195
+ }
5196
+ index--;
5197
+ }
5198
+ return false;
5199
+ }
5200
+ }
5201
+
5202
+ //#endregion
5203
+ //#region src/rules/no-multi-spaces.ts
5204
+ var no_multi_spaces_default = createRule("no-multi-spaces", {
5205
+ meta: {
5206
+ type: "layout",
5207
+ docs: {
5208
+ description: "disallow multiple spaces",
5209
+ categories: ["standard"],
5210
+ listCategory: "Stylistic"
5211
+ },
5212
+ fixable: "whitespace",
5213
+ hasSuggestions: false,
5214
+ schema: [],
5215
+ messages: { multipleSpaces: "Multiple spaces are not allowed." }
5216
+ },
5217
+ create(context) {
5218
+ const sourceCode = context.sourceCode;
5219
+ return {
5220
+ definition: verifyLinkDefinition,
5221
+ footnoteDefinition: verifyFootnoteDefinition,
5222
+ heading: verifyHeading,
5223
+ image: verifyImage,
5224
+ imageReference: verifyImageReference,
5225
+ link: verifyLink,
5226
+ linkReference: verifyLinkReference,
5227
+ listItem: verifyListItem,
5228
+ text: verifyText
5229
+ };
5230
+ /**
5231
+ * Verify a text node.
5232
+ */
5233
+ function verifyText(node) {
5234
+ verifyTextInNode(node);
5235
+ }
5236
+ /**
5237
+ * Verify a definition node.
5238
+ */
5239
+ function verifyLinkDefinition(node) {
5240
+ const parsed = parseLinkDefinition(sourceCode, node);
5241
+ if (!parsed) return;
5242
+ const nodeRange = sourceCode.getRange(node);
5243
+ verifyTextInRange(node, [nodeRange[0], parsed.destination.range[0]]);
5244
+ verifyTextInRange(node, [parsed.destination.range[1], nodeRange[1]]);
5245
+ }
5246
+ /**
5247
+ * Verify a footnote definition node
5248
+ */
5249
+ function verifyFootnoteDefinition(node) {
5250
+ verifyTextOutsideChildren(node);
5251
+ }
5252
+ /**
5253
+ * Verify a heading node
5254
+ */
5255
+ function verifyHeading(node) {
5256
+ verifyTextOutsideChildren(node);
5257
+ }
5258
+ /**
5259
+ * Verify an image node
5260
+ */
5261
+ function verifyImage(node) {
5262
+ const parsed = parseImage(sourceCode, node);
5263
+ if (!parsed) return;
5264
+ const nodeRange = sourceCode.getRange(node);
5265
+ verifyTextInRange(node, [nodeRange[0], parsed.destination.range[0]]);
5266
+ verifyTextInRange(node, [parsed.destination.range[1], nodeRange[1]]);
5267
+ }
5268
+ /**
5269
+ * Verify an image reference node
5270
+ */
5271
+ function verifyImageReference(node) {
5272
+ verifyTextInNode(node);
5273
+ }
5274
+ /**
5275
+ * Verify a link node
5276
+ */
5277
+ function verifyLink(node) {
5278
+ const parsed = parseInlineLink(sourceCode, node);
5279
+ if (!parsed) return;
5280
+ const nodeRange = sourceCode.getRange(node);
5281
+ if (node.children.length > 0) {
5282
+ const first = node.children[0];
5283
+ const last = node.children[node.children.length - 1];
5284
+ const firstRange = sourceCode.getRange(first);
5285
+ const lastRange = sourceCode.getRange(last);
5286
+ verifyTextInRange(node, [nodeRange[0], firstRange[0]]);
5287
+ verifyTextInRange(node, [lastRange[1], parsed.destination.range[0]]);
5288
+ } else verifyTextInRange(node, [nodeRange[0], parsed.destination.range[0]]);
5289
+ verifyTextInRange(node, [parsed.destination.range[1], nodeRange[1]]);
5290
+ }
5291
+ /**
5292
+ * Verify a link reference node
5293
+ */
5294
+ function verifyLinkReference(node) {
5295
+ verifyTextOutsideChildren(node);
5296
+ }
5297
+ /**
5298
+ * Verify a list item node
5299
+ */
5300
+ function verifyListItem(node) {
5301
+ verifyTextOutsideChildren(node);
5302
+ }
5303
+ /**
5304
+ * Verify spaces in a node
5305
+ */
5306
+ function verifyTextInNode(node) {
5307
+ const nodeRange = sourceCode.getRange(node);
5308
+ verifyTextInRange(node, nodeRange);
5309
+ }
5310
+ /**
5311
+ * Verify spaces in a node excluding children
5312
+ */
5313
+ function verifyTextOutsideChildren(node) {
5314
+ const nodeRange = sourceCode.getRange(node);
5315
+ if (node.children.length > 0) {
5316
+ const first = node.children[0];
5317
+ const last = node.children[node.children.length - 1];
5318
+ const firstRange = sourceCode.getRange(first);
5319
+ const lastRange = sourceCode.getRange(last);
5320
+ verifyTextInRange(node, [nodeRange[0], firstRange[0]]);
5321
+ verifyTextInRange(node, [lastRange[1], nodeRange[1]]);
5322
+ } else verifyTextInRange(node, nodeRange);
5323
+ }
5324
+ /**
5325
+ * Verify spaces in a node
5326
+ */
5327
+ function verifyTextInRange(node, textRange) {
5328
+ const nodeRange = sourceCode.getRange(node);
5329
+ const text = sourceCode.text.slice(...textRange);
5330
+ const reSpaces = /\s{2,}|\n/gu;
5331
+ let match;
5332
+ while ((match = reSpaces.exec(text)) !== null) {
5333
+ const spaces = match[0];
5334
+ if (spaces.includes("\n")) {
5335
+ let c = "";
5336
+ while ((c = text[reSpaces.lastIndex]) && (c === ">" || !c.trim())) reSpaces.lastIndex++;
5337
+ continue;
5338
+ }
5339
+ if (spaces.length < 2) continue;
5340
+ const start = textRange[0] + match.index;
5341
+ const end = start + spaces.length;
5342
+ const range = [start, end];
5343
+ if (nodeRange[0] === range[0]) {
5344
+ let isIndentation = true;
5345
+ for (let index = nodeRange[0] - 1; index >= 0; index--) {
5346
+ const c = sourceCode.text[index];
5347
+ if (c === "\n") break;
5348
+ if (isWhitespace(c)) continue;
5349
+ isIndentation = false;
5350
+ break;
5351
+ }
5352
+ if (isIndentation) continue;
5353
+ }
5354
+ if (nodeRange[1] === range[1]) {
5355
+ let isTrailingSpaces = true;
5356
+ for (let index = nodeRange[1]; index < sourceCode.text.length; index++) {
5357
+ const c = sourceCode.text[index];
5358
+ if (c === "\n") break;
5359
+ if (isWhitespace(c)) continue;
5360
+ isTrailingSpaces = false;
5361
+ break;
5362
+ }
5363
+ if (isTrailingSpaces) continue;
5364
+ }
5365
+ context.report({
5366
+ node,
5367
+ messageId: "multipleSpaces",
5368
+ loc: getSourceLocationFromRange(sourceCode, node, range),
5369
+ fix(fixer) {
5370
+ return fixer.replaceTextRange(range, " ");
5371
+ }
5372
+ });
5373
+ }
5374
+ }
5375
+ }
5376
+ });
5377
+
4824
5378
  //#endregion
4825
5379
  //#region src/rules/no-multiple-empty-lines.ts
4826
5380
  var no_multiple_empty_lines_default = createRule("no-multiple-empty-lines", {
@@ -4828,7 +5382,7 @@ var no_multiple_empty_lines_default = createRule("no-multiple-empty-lines", {
4828
5382
  type: "layout",
4829
5383
  docs: {
4830
5384
  description: "disallow multiple empty lines in Markdown files.",
4831
- categories: null,
5385
+ categories: ["standard"],
4832
5386
  listCategory: "Stylistic"
4833
5387
  },
4834
5388
  fixable: "whitespace",
@@ -4987,7 +5541,7 @@ var no_text_backslash_linebreak_default = createRule("no-text-backslash-linebrea
4987
5541
  type: "layout",
4988
5542
  docs: {
4989
5543
  description: "disallow text backslash at the end of a line.",
4990
- categories: ["recommended"],
5544
+ categories: ["recommended", "standard"],
4991
5545
  listCategory: "Stylistic"
4992
5546
  },
4993
5547
  fixable: void 0,
@@ -5032,7 +5586,7 @@ var no_trailing_spaces_default = createRule("no-trailing-spaces", {
5032
5586
  type: "layout",
5033
5587
  docs: {
5034
5588
  description: "disallow trailing whitespace at the end of lines in Markdown files.",
5035
- categories: [],
5589
+ categories: ["standard"],
5036
5590
  listCategory: "Stylistic"
5037
5591
  },
5038
5592
  fixable: "whitespace",
@@ -5093,7 +5647,31 @@ var no_trailing_spaces_default = createRule("no-trailing-spaces", {
5093
5647
  html(node) {
5094
5648
  if (htmlComment.test(node.value)) comments.push(node);
5095
5649
  },
5096
- "break, code, inlineCode, text, yaml, toml, json"(node) {
5650
+ break(node) {
5651
+ ignoreNodes.push(node);
5652
+ const range = sourceCode.getRange(node);
5653
+ let trailingSpaceCount = 0;
5654
+ for (let index = range[1] - 1; index >= range[0]; index--) {
5655
+ const c = sourceCode.text[index];
5656
+ if (c === "\n" || c === "\r") {
5657
+ trailingSpaceCount = 0;
5658
+ continue;
5659
+ }
5660
+ if (!isWhitespace(c)) return;
5661
+ trailingSpaceCount++;
5662
+ }
5663
+ const extraSpaces = trailingSpaceCount - 2;
5664
+ if (extraSpaces <= 0) return;
5665
+ context.report({
5666
+ node,
5667
+ loc: getSourceLocationFromRange(sourceCode, node, [range[0], range[0] + extraSpaces]),
5668
+ messageId: "trailingSpace",
5669
+ fix(fixer) {
5670
+ return fixer.removeRange([range[0], range[0] + extraSpaces]);
5671
+ }
5672
+ });
5673
+ },
5674
+ "code, inlineCode, text, yaml, toml, json"(node) {
5097
5675
  ignoreNodes.push(node);
5098
5676
  },
5099
5677
  "root:exit"() {
@@ -5134,7 +5712,7 @@ var ordered_list_marker_sequence_default = createRule("ordered-list-marker-seque
5134
5712
  type: "layout",
5135
5713
  docs: {
5136
5714
  description: "enforce that ordered list markers use sequential numbers",
5137
- categories: [],
5715
+ categories: ["standard"],
5138
5716
  listCategory: "Stylistic"
5139
5717
  },
5140
5718
  fixable: "code",
@@ -5250,7 +5828,7 @@ var ordered_list_marker_start_default = createRule("ordered-list-marker-start",
5250
5828
  type: "layout",
5251
5829
  docs: {
5252
5830
  description: "enforce that ordered list markers start with 1 or 0",
5253
- categories: [],
5831
+ categories: ["standard"],
5254
5832
  listCategory: "Preference"
5255
5833
  },
5256
5834
  fixable: "code",
@@ -5376,7 +5954,7 @@ var ordered_list_marker_style_default = createRule("ordered-list-marker-style",
5376
5954
  type: "layout",
5377
5955
  docs: {
5378
5956
  description: "enforce consistent ordered list marker style",
5379
- categories: [],
5957
+ categories: ["standard"],
5380
5958
  listCategory: "Stylistic"
5381
5959
  },
5382
5960
  fixable: "code",
@@ -5597,7 +6175,7 @@ var padding_line_between_blocks_default = createRule("padding-line-between-block
5597
6175
  type: "layout",
5598
6176
  docs: {
5599
6177
  description: "require or disallow padding lines between blocks",
5600
- categories: ["recommended"],
6178
+ categories: ["standard"],
5601
6179
  listCategory: "Stylistic"
5602
6180
  },
5603
6181
  fixable: "whitespace",
@@ -5633,7 +6211,32 @@ var padding_line_between_blocks_default = createRule("padding-line-between-block
5633
6211
  },
5634
6212
  create(context) {
5635
6213
  const sourceCode = context.sourceCode;
5636
- const options = [...context.options || []].reverse();
6214
+ const originalOptions = context.options?.length ? context.options : [
6215
+ {
6216
+ prev: "*",
6217
+ next: "*",
6218
+ blankLine: "always"
6219
+ },
6220
+ {
6221
+ prev: "link-definition",
6222
+ next: "link-definition",
6223
+ blankLine: "never"
6224
+ },
6225
+ {
6226
+ prev: "footnote-definition",
6227
+ next: "footnote-definition",
6228
+ blankLine: "never"
6229
+ },
6230
+ {
6231
+ prev: "paragraph",
6232
+ next: {
6233
+ type: "list",
6234
+ in: "list"
6235
+ },
6236
+ blankLine: "never"
6237
+ }
6238
+ ];
6239
+ const options = [...originalOptions].reverse();
5637
6240
  const containerStack = [];
5638
6241
  /**
5639
6242
  * Check if the actual type matches the expected type pattern
@@ -5773,7 +6376,7 @@ var prefer_autolinks_default = createRule("prefer-autolinks", {
5773
6376
  type: "layout",
5774
6377
  docs: {
5775
6378
  description: "enforce the use of autolinks for URLs",
5776
- categories: ["recommended"],
6379
+ categories: ["recommended", "standard"],
5777
6380
  listCategory: "Stylistic"
5778
6381
  },
5779
6382
  fixable: "code",
@@ -5811,7 +6414,7 @@ var prefer_fenced_code_blocks_default = createRule("prefer-fenced-code-blocks",
5811
6414
  type: "layout",
5812
6415
  docs: {
5813
6416
  description: "enforce the use of fenced code blocks over indented code blocks",
5814
- categories: ["recommended"],
6417
+ categories: ["recommended", "standard"],
5815
6418
  listCategory: "Stylistic"
5816
6419
  },
5817
6420
  fixable: "code",
@@ -6324,7 +6927,7 @@ var setext_heading_underline_length_default = createRule("setext-heading-underli
6324
6927
  type: "layout",
6325
6928
  docs: {
6326
6929
  description: "enforce setext heading underline length",
6327
- categories: [],
6930
+ categories: ["standard"],
6328
6931
  listCategory: "Stylistic"
6329
6932
  },
6330
6933
  fixable: "whitespace",
@@ -6562,7 +7165,7 @@ var sort_definitions_default = createRule("sort-definitions", {
6562
7165
  type: "layout",
6563
7166
  docs: {
6564
7167
  description: "enforce a specific order for link definitions and footnote definitions",
6565
- categories: [],
7168
+ categories: ["standard"],
6566
7169
  listCategory: "Stylistic"
6567
7170
  },
6568
7171
  fixable: "code",
@@ -6678,7 +7281,7 @@ var sort_definitions_default = createRule("sort-definitions", {
6678
7281
  return {
6679
7282
  "*"(node) {
6680
7283
  const last = group.at(-1);
6681
- if (last && (node.type !== "definition" && node.type !== "footnoteDefinition" || sourceCode.getParent(node) !== sourceCode.getParent(last))) {
7284
+ if (last && (node.type !== "definition" && node.type !== "footnoteDefinition" || getParent(sourceCode, node) !== getParent(sourceCode, last))) {
6682
7285
  const range = sourceCode.getRange(node);
6683
7286
  const lastDefinitionRange = sourceCode.getRange(last);
6684
7287
  if (lastDefinitionRange[1] <= range[0]) {
@@ -6765,7 +7368,7 @@ var sort_definitions_default = createRule("sort-definitions", {
6765
7368
  }
6766
7369
  /** Compile matcher */
6767
7370
  function compileMatcher(pattern) {
6768
- const rules$3 = [];
7371
+ const rules$4 = [];
6769
7372
  for (const p of pattern) {
6770
7373
  let negative, patternStr;
6771
7374
  if (p.startsWith("!")) {
@@ -6776,11 +7379,11 @@ var sort_definitions_default = createRule("sort-definitions", {
6776
7379
  patternStr = p;
6777
7380
  }
6778
7381
  const regex = toRegExp(patternStr);
6779
- if (isRegExp(patternStr)) rules$3.push({
7382
+ if (isRegExp(patternStr)) rules$4.push({
6780
7383
  negative,
6781
7384
  match: (node) => regex.test(getDefinitionText(node))
6782
7385
  });
6783
- else rules$3.push({
7386
+ else rules$4.push({
6784
7387
  negative,
6785
7388
  match: (node) => {
6786
7389
  if (node.label === patternStr || node.identifier === patternStr) return true;
@@ -6797,8 +7400,8 @@ var sort_definitions_default = createRule("sort-definitions", {
6797
7400
  });
6798
7401
  }
6799
7402
  return (node) => {
6800
- let result = Boolean(rules$3[0]?.negative);
6801
- for (const { negative, match } of rules$3) {
7403
+ let result = Boolean(rules$4[0]?.negative);
7404
+ for (const { negative, match } of rules$4) {
6802
7405
  if (result === !negative) continue;
6803
7406
  if (match(node)) result = !negative;
6804
7407
  }
@@ -6845,7 +7448,7 @@ var strikethrough_delimiters_style_default = createRule("strikethrough-delimiter
6845
7448
  type: "layout",
6846
7449
  docs: {
6847
7450
  description: "enforce a consistent delimiter style for strikethrough",
6848
- categories: [],
7451
+ categories: ["standard"],
6849
7452
  listCategory: "Stylistic"
6850
7453
  },
6851
7454
  fixable: "code",
@@ -7021,7 +7624,7 @@ var thematic_break_character_style_default = createRule("thematic-break-characte
7021
7624
  type: "layout",
7022
7625
  docs: {
7023
7626
  description: "enforce consistent character style for thematic breaks (horizontal rules) in Markdown.",
7024
- categories: [],
7627
+ categories: ["standard"],
7025
7628
  listCategory: "Stylistic"
7026
7629
  },
7027
7630
  fixable: "code",
@@ -7102,7 +7705,7 @@ var thematic_break_length_default = createRule("thematic-break-length", {
7102
7705
  type: "layout",
7103
7706
  docs: {
7104
7707
  description: "enforce consistent length for thematic breaks (horizontal rules) in Markdown.",
7105
- categories: [],
7708
+ categories: ["standard"],
7106
7709
  listCategory: "Stylistic"
7107
7710
  },
7108
7711
  fixable: "code",
@@ -7169,7 +7772,7 @@ var thematic_break_sequence_pattern_default = createRule("thematic-break-sequenc
7169
7772
  type: "layout",
7170
7773
  docs: {
7171
7774
  description: "enforce consistent repeating patterns for thematic breaks (horizontal rules) in Markdown.",
7172
- categories: [],
7775
+ categories: ["standard"],
7173
7776
  listCategory: "Stylistic"
7174
7777
  },
7175
7778
  fixable: "code",
@@ -7244,6 +7847,7 @@ const rules$1 = [
7244
7847
  level2_heading_style_default,
7245
7848
  list_marker_alignment_default,
7246
7849
  no_laziness_blockquotes_default,
7850
+ no_multi_spaces_default,
7247
7851
  no_multiple_empty_lines_default,
7248
7852
  no_text_backslash_linebreak_default,
7249
7853
  no_trailing_spaces_default,
@@ -7269,6 +7873,37 @@ const rules$1 = [
7269
7873
  //#region src/configs/recommended.ts
7270
7874
  var recommended_exports = {};
7271
7875
  __export(recommended_exports, {
7876
+ files: () => files$1,
7877
+ language: () => language$1,
7878
+ languageOptions: () => languageOptions$1,
7879
+ name: () => name$2,
7880
+ plugins: () => plugins$1,
7881
+ rules: () => rules$3
7882
+ });
7883
+ const name$2 = "markdown-preferences/recommended";
7884
+ const files$1 = ["*.md", "**/*.md"];
7885
+ const language$1 = "markdown/gfm";
7886
+ const languageOptions$1 = { frontmatter: "yaml" };
7887
+ const plugins$1 = {
7888
+ markdown,
7889
+ get "markdown-preferences"() {
7890
+ return src_default;
7891
+ }
7892
+ };
7893
+ const rules$3 = {
7894
+ "markdown-preferences/blockquote-marker-alignment": "error",
7895
+ "markdown-preferences/hard-linebreak-style": "error",
7896
+ "markdown-preferences/list-marker-alignment": "error",
7897
+ "markdown-preferences/no-laziness-blockquotes": "error",
7898
+ "markdown-preferences/no-text-backslash-linebreak": "error",
7899
+ "markdown-preferences/prefer-autolinks": "error",
7900
+ "markdown-preferences/prefer-fenced-code-blocks": "error"
7901
+ };
7902
+
7903
+ //#endregion
7904
+ //#region src/configs/standard.ts
7905
+ var standard_exports = {};
7906
+ __export(standard_exports, {
7272
7907
  files: () => files,
7273
7908
  language: () => language,
7274
7909
  languageOptions: () => languageOptions,
@@ -7287,14 +7922,34 @@ const plugins = {
7287
7922
  }
7288
7923
  };
7289
7924
  const rules$2 = {
7925
+ "markdown-preferences/atx-heading-closing-sequence-length": "error",
7926
+ "markdown-preferences/atx-heading-closing-sequence": "error",
7290
7927
  "markdown-preferences/blockquote-marker-alignment": "error",
7928
+ "markdown-preferences/bullet-list-marker-style": "error",
7929
+ "markdown-preferences/code-fence-length": "error",
7930
+ "markdown-preferences/code-fence-style": "error",
7931
+ "markdown-preferences/emphasis-delimiters-style": "error",
7291
7932
  "markdown-preferences/hard-linebreak-style": "error",
7933
+ "markdown-preferences/level1-heading-style": "error",
7934
+ "markdown-preferences/level2-heading-style": "error",
7292
7935
  "markdown-preferences/list-marker-alignment": "error",
7293
7936
  "markdown-preferences/no-laziness-blockquotes": "error",
7937
+ "markdown-preferences/no-multi-spaces": "error",
7938
+ "markdown-preferences/no-multiple-empty-lines": "error",
7294
7939
  "markdown-preferences/no-text-backslash-linebreak": "error",
7940
+ "markdown-preferences/no-trailing-spaces": "error",
7941
+ "markdown-preferences/ordered-list-marker-sequence": "error",
7942
+ "markdown-preferences/ordered-list-marker-start": "error",
7943
+ "markdown-preferences/ordered-list-marker-style": "error",
7295
7944
  "markdown-preferences/padding-line-between-blocks": "error",
7296
7945
  "markdown-preferences/prefer-autolinks": "error",
7297
- "markdown-preferences/prefer-fenced-code-blocks": "error"
7946
+ "markdown-preferences/prefer-fenced-code-blocks": "error",
7947
+ "markdown-preferences/setext-heading-underline-length": "error",
7948
+ "markdown-preferences/sort-definitions": "error",
7949
+ "markdown-preferences/strikethrough-delimiters-style": "error",
7950
+ "markdown-preferences/thematic-break-character-style": "error",
7951
+ "markdown-preferences/thematic-break-length": "error",
7952
+ "markdown-preferences/thematic-break-sequence-pattern": "error"
7298
7953
  };
7299
7954
 
7300
7955
  //#endregion
@@ -7305,11 +7960,14 @@ __export(meta_exports, {
7305
7960
  version: () => version
7306
7961
  });
7307
7962
  const name = "eslint-plugin-markdown-preferences";
7308
- const version = "0.20.0";
7963
+ const version = "0.21.0";
7309
7964
 
7310
7965
  //#endregion
7311
7966
  //#region src/index.ts
7312
- const configs = { recommended: recommended_exports };
7967
+ const configs = {
7968
+ recommended: recommended_exports,
7969
+ standard: standard_exports
7970
+ };
7313
7971
  const rules = rules$1.reduce((obj, r) => {
7314
7972
  obj[r.meta.docs.ruleName] = r;
7315
7973
  return obj;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-markdown-preferences",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "ESLint plugin that enforces our markdown preferences",
5
5
  "type": "module",
6
6
  "exports": {