eslint-markdown 0.12.0 → 0.13.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.
@@ -37,6 +37,7 @@ export default function all(plugin: ESLint.Plugin): {
37
37
  readonly 'md/no-irregular-dash': "error";
38
38
  readonly 'md/no-irregular-whitespace': "error";
39
39
  readonly 'md/no-tab': "error";
40
+ readonly 'md/no-trailing-heading-punctuation': "error";
40
41
  readonly 'md/no-url-trailing-slash': "error";
41
42
  readonly 'md/require-heading-id': "error";
42
43
  readonly 'md/require-image-title': "error";
@@ -45,6 +45,7 @@ export default function all(plugin) {
45
45
  'md/no-irregular-dash': 'error',
46
46
  'md/no-irregular-whitespace': 'error',
47
47
  'md/no-tab': 'error',
48
+ 'md/no-trailing-heading-punctuation': 'error',
48
49
  'md/no-url-trailing-slash': 'error',
49
50
  'md/require-heading-id': 'error',
50
51
  'md/require-image-title': 'error',
@@ -25,5 +25,6 @@ export default function stylistic(plugin: ESLint.Plugin): {
25
25
  readonly 'md/consistent-unordered-list-style': "error";
26
26
  readonly 'md/no-consecutive-blank-line': "error";
27
27
  readonly 'md/no-tab': "error";
28
+ readonly 'md/no-trailing-heading-punctuation': "error";
28
29
  };
29
30
  };
@@ -33,6 +33,7 @@ export default function stylistic(plugin) {
33
33
  'md/consistent-unordered-list-style': 'error',
34
34
  'md/no-consecutive-blank-line': 'error',
35
35
  'md/no-tab': 'error',
36
+ 'md/no-trailing-heading-punctuation': 'error',
36
37
  },
37
38
  };
38
39
  }
@@ -2,4 +2,5 @@ import escapeStringRegexp from './escape-string-regexp.js';
2
2
  import getElementsByTagName from './html.js';
3
3
  import isBlankLine from './is-blank-line.js';
4
4
  import SkipRanges from './skip-ranges.js';
5
- export { escapeStringRegexp, getElementsByTagName, isBlankLine, SkipRanges };
5
+ import testRegexStateless from './test-regex-stateless.js';
6
+ export { escapeStringRegexp, getElementsByTagName, isBlankLine, SkipRanges, testRegexStateless, };
@@ -2,4 +2,5 @@ import escapeStringRegexp from './escape-string-regexp.js';
2
2
  import getElementsByTagName from './html.js';
3
3
  import isBlankLine from './is-blank-line.js';
4
4
  import SkipRanges from './skip-ranges.js';
5
- export { escapeStringRegexp, getElementsByTagName, isBlankLine, SkipRanges };
5
+ import testRegexStateless from './test-regex-stateless.js';
6
+ export { escapeStringRegexp, getElementsByTagName, isBlankLine, SkipRanges, testRegexStateless, };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @fileoverview Utility to test a regular expression without mutating its state.
3
+ */
4
+ /**
5
+ * Tests a regex without mutating the state stored in its `lastIndex`.
6
+ * @param regex Regex to test.
7
+ * @param text Text to test.
8
+ * @returns Whether the regex matches the text.
9
+ */
10
+ export default function testRegexStateless(regex: RegExp, text: string): boolean;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview Utility to test a regular expression without mutating its state.
3
+ */
4
+ // --------------------------------------------------------------------------------
5
+ // Helper
6
+ // --------------------------------------------------------------------------------
7
+ const statefulRegexFlagRegex = /[gy]/u;
8
+ // --------------------------------------------------------------------------------
9
+ // Export
10
+ // --------------------------------------------------------------------------------
11
+ /**
12
+ * Tests a regex without mutating the state stored in its `lastIndex`.
13
+ * @param regex Regex to test.
14
+ * @param text Text to test.
15
+ * @returns Whether the regex matches the text.
16
+ */
17
+ export default function testRegexStateless(regex, text) {
18
+ return statefulRegexFlagRegex.test(regex.flags)
19
+ ? new RegExp(regex).test(text)
20
+ : regex.test(text);
21
+ }
package/build/index.d.ts CHANGED
@@ -594,6 +594,7 @@ declare const plugin: {
594
594
  readonly properties: {
595
595
  readonly style: {
596
596
  readonly type: "string";
597
+ readonly pattern: "^(?:consistent|(?:-[ \\t]*){3,}|(?:\\*[ \\t]*){3,}|(?:_[ \\t]*){3,})$";
597
598
  };
598
599
  };
599
600
  readonly additionalProperties: false;
@@ -1206,6 +1207,53 @@ declare const plugin: {
1206
1207
  'root:exit'(): void;
1207
1208
  };
1208
1209
  };
1210
+ 'no-trailing-heading-punctuation': {
1211
+ readonly meta: {
1212
+ readonly type: "problem";
1213
+ readonly docs: {
1214
+ readonly description: "Disallow trailing punctuation in headings";
1215
+ readonly url: string;
1216
+ readonly recommended: false;
1217
+ readonly stylistic: true;
1218
+ };
1219
+ readonly fixable: "code";
1220
+ readonly schema: [{
1221
+ readonly type: "object";
1222
+ readonly properties: {
1223
+ readonly punctuation: {
1224
+ readonly type: "array";
1225
+ readonly items: {
1226
+ readonly type: "string";
1227
+ readonly minLength: 1;
1228
+ readonly maxLength: 1;
1229
+ };
1230
+ readonly minItems: 1;
1231
+ readonly uniqueItems: true;
1232
+ };
1233
+ };
1234
+ readonly additionalProperties: false;
1235
+ }];
1236
+ readonly defaultOptions: [{
1237
+ readonly punctuation: [".", ",", ";", ":", "!", "。", ",", ";", ":", "!"];
1238
+ }];
1239
+ readonly messages: {
1240
+ readonly noTrailingHeadingPunctuation: "Trailing punctuation `{{ punctuation }}` is not allowed in headings.";
1241
+ };
1242
+ readonly language: "markdown";
1243
+ readonly dialects: ["commonmark", "gfm"];
1244
+ };
1245
+ readonly create: (context: import("@eslint/core").RuleContext<{
1246
+ LangOptions: import("@eslint/markdown").MarkdownLanguageOptions;
1247
+ Code: import("@eslint/markdown").MarkdownSourceCode;
1248
+ RuleOptions: [{
1249
+ punctuation: string[];
1250
+ }];
1251
+ Node: import("mdast").Node;
1252
+ MessageIds: "noTrailingHeadingPunctuation";
1253
+ }>) => {
1254
+ heading(node: import("mdast").Heading): void;
1255
+ };
1256
+ };
1209
1257
  'no-url-trailing-slash': {
1210
1258
  readonly meta: {
1211
1259
  readonly type: "problem";
@@ -2,11 +2,11 @@
2
2
  * @fileoverview Rule to enforce the use of allowed or disallowed headings.
3
3
  * @author lumir(lumirlumir)
4
4
  */
5
+ import { testRegexStateless } from '../core/utils/index.js';
5
6
  import { URL_RULE_DOCS } from '../core/constants.js';
6
7
  // --------------------------------------------------------------------------------
7
8
  // Helper
8
9
  // --------------------------------------------------------------------------------
9
- const statefulRegexFlagRegex = /[gy]/u;
10
10
  const headingOptionsSchema = {
11
11
  type: 'object',
12
12
  properties: {
@@ -27,17 +27,6 @@ const headingOptionsSchema = {
27
27
  },
28
28
  additionalProperties: false,
29
29
  };
30
- /**
31
- * Tests a regex without mutating the state stored in its `lastIndex`.
32
- * @param regex Regex to test.
33
- * @param text Text to test.
34
- * @returns Whether the regex matches the text.
35
- */
36
- function testRegexStateless(regex, text) {
37
- return statefulRegexFlagRegex.test(regex.flags)
38
- ? new RegExp(regex).test(text)
39
- : regex.test(text);
40
- }
41
30
  // --------------------------------------------------------------------------------
42
31
  // Rule Definition
43
32
  // --------------------------------------------------------------------------------
@@ -3,7 +3,7 @@
3
3
  * @author lumir(lumirlumir)
4
4
  */
5
5
  import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
6
- import { getElementsByTagName } from '../core/utils/index.js';
6
+ import { getElementsByTagName, testRegexStateless } from '../core/utils/index.js';
7
7
  import { URL_RULE_DOCS } from '../core/constants.js';
8
8
  // --------------------------------------------------------------------------------
9
9
  // Rule Definition
@@ -114,7 +114,7 @@ export default {
114
114
  * Therefore, calling the `some` method on an empty array will always return `false`.
115
115
  */
116
116
  for (const { loc, url } of images) {
117
- if (!allowUrls.some(regex => regex.test(url))) {
117
+ if (!allowUrls.some(regex => testRegexStateless(regex, url))) {
118
118
  context.report({
119
119
  loc,
120
120
  messageId: 'allowImageUrl',
@@ -124,7 +124,7 @@ export default {
124
124
  },
125
125
  });
126
126
  }
127
- if (disallowUrls.some(regex => regex.test(url))) {
127
+ if (disallowUrls.some(regex => testRegexStateless(regex, url))) {
128
128
  context.report({
129
129
  loc,
130
130
  messageId: 'disallowImageUrl',
@@ -3,7 +3,7 @@
3
3
  * @author lumir(lumirlumir)
4
4
  */
5
5
  import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
6
- import { getElementsByTagName } from '../core/utils/index.js';
6
+ import { getElementsByTagName, testRegexStateless } from '../core/utils/index.js';
7
7
  import { URL_RULE_DOCS } from '../core/constants.js';
8
8
  // --------------------------------------------------------------------------------
9
9
  // Rule Definition
@@ -114,7 +114,7 @@ export default {
114
114
  * Therefore, calling the `some` method on an empty array will always return `false`.
115
115
  */
116
116
  for (const { loc, url } of links) {
117
- if (!allowUrls.some(regex => regex.test(url))) {
117
+ if (!allowUrls.some(regex => testRegexStateless(regex, url))) {
118
118
  context.report({
119
119
  loc,
120
120
  messageId: 'allowLinkUrl',
@@ -124,7 +124,7 @@ export default {
124
124
  },
125
125
  });
126
126
  }
127
- if (disallowUrls.some(regex => regex.test(url))) {
127
+ if (disallowUrls.some(regex => testRegexStateless(regex, url))) {
128
128
  context.report({
129
129
  loc,
130
130
  messageId: 'disallowLinkUrl',
@@ -20,6 +20,7 @@ declare const _default: {
20
20
  readonly properties: {
21
21
  readonly style: {
22
22
  readonly type: "string";
23
+ readonly pattern: "^(?:consistent|(?:-[ \\t]*){3,}|(?:\\*[ \\t]*){3,}|(?:_[ \\t]*){3,})$";
23
24
  };
24
25
  };
25
26
  readonly additionalProperties: false;
@@ -7,6 +7,15 @@
7
7
  // --------------------------------------------------------------------------------
8
8
  import { URL_RULE_DOCS } from '../core/constants.js';
9
9
  // --------------------------------------------------------------------------------
10
+ // Helper
11
+ // --------------------------------------------------------------------------------
12
+ /**
13
+ * Matches `consistent` or a valid CommonMark thematic break style
14
+ * (3+ identical `-`, `*`, or `_` characters, each optionally followed by spaces/tabs).
15
+ * @see https://spec.commonmark.org/0.31.2/#thematic-breaks
16
+ */
17
+ const thematicBreakStylePattern = '^(?:consistent|(?:-[ \\t]*){3,}|(?:\\*[ \\t]*){3,}|(?:_[ \\t]*){3,})$';
18
+ // --------------------------------------------------------------------------------
10
19
  // Rule Definition
11
20
  // --------------------------------------------------------------------------------
12
21
  export default {
@@ -25,6 +34,7 @@ export default {
25
34
  properties: {
26
35
  style: {
27
36
  type: 'string',
37
+ pattern: thematicBreakStylePattern,
28
38
  },
29
39
  },
30
40
  additionalProperties: false,
@@ -585,6 +585,7 @@ declare const _default: {
585
585
  readonly properties: {
586
586
  readonly style: {
587
587
  readonly type: "string";
588
+ readonly pattern: "^(?:consistent|(?:-[ \\t]*){3,}|(?:\\*[ \\t]*){3,}|(?:_[ \\t]*){3,})$";
588
589
  };
589
590
  };
590
591
  readonly additionalProperties: false;
@@ -1197,6 +1198,53 @@ declare const _default: {
1197
1198
  'root:exit'(): void;
1198
1199
  };
1199
1200
  };
1201
+ 'no-trailing-heading-punctuation': {
1202
+ readonly meta: {
1203
+ readonly type: "problem";
1204
+ readonly docs: {
1205
+ readonly description: "Disallow trailing punctuation in headings";
1206
+ readonly url: string;
1207
+ readonly recommended: false;
1208
+ readonly stylistic: true;
1209
+ };
1210
+ readonly fixable: "code";
1211
+ readonly schema: [{
1212
+ readonly type: "object";
1213
+ readonly properties: {
1214
+ readonly punctuation: {
1215
+ readonly type: "array";
1216
+ readonly items: {
1217
+ readonly type: "string";
1218
+ readonly minLength: 1;
1219
+ readonly maxLength: 1;
1220
+ };
1221
+ readonly minItems: 1;
1222
+ readonly uniqueItems: true;
1223
+ };
1224
+ };
1225
+ readonly additionalProperties: false;
1226
+ }];
1227
+ readonly defaultOptions: [{
1228
+ readonly punctuation: [".", ",", ";", ":", "!", "。", ",", ";", ":", "!"];
1229
+ }];
1230
+ readonly messages: {
1231
+ readonly noTrailingHeadingPunctuation: "Trailing punctuation `{{ punctuation }}` is not allowed in headings.";
1232
+ };
1233
+ readonly language: "markdown";
1234
+ readonly dialects: ["commonmark", "gfm"];
1235
+ };
1236
+ readonly create: (context: import("@eslint/core").RuleContext<{
1237
+ LangOptions: import("@eslint/markdown").MarkdownLanguageOptions;
1238
+ Code: import("@eslint/markdown").MarkdownSourceCode;
1239
+ RuleOptions: [{
1240
+ punctuation: string[];
1241
+ }];
1242
+ Node: import("mdast").Node;
1243
+ MessageIds: "noTrailingHeadingPunctuation";
1244
+ }>) => {
1245
+ heading(node: import("mdast").Heading): void;
1246
+ };
1247
+ };
1200
1248
  'no-url-trailing-slash': {
1201
1249
  readonly meta: {
1202
1250
  readonly type: "problem";
@@ -23,6 +23,7 @@ import noGitConflictMarker from './no-git-conflict-marker.js';
23
23
  import noIrregularDash from './no-irregular-dash.js';
24
24
  import noIrregularWhitespace from './no-irregular-whitespace.js';
25
25
  import noTab from './no-tab.js';
26
+ import noTrailingHeadingPunctuation from './no-trailing-heading-punctuation.js';
26
27
  import noUrlTrailingSlash from './no-url-trailing-slash.js';
27
28
  import requireHeadingId from './require-heading-id.js';
28
29
  import requireImageTitle from './require-image-title.js';
@@ -51,6 +52,7 @@ export default {
51
52
  'no-irregular-dash': noIrregularDash,
52
53
  'no-irregular-whitespace': noIrregularWhitespace,
53
54
  'no-tab': noTab,
55
+ 'no-trailing-heading-punctuation': noTrailingHeadingPunctuation,
54
56
  'no-url-trailing-slash': noUrlTrailingSlash,
55
57
  'require-heading-id': requireHeadingId,
56
58
  'require-image-title': requireImageTitle,
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @fileoverview Rule to disallow trailing punctuation in headings.
3
+ * @author Ga eun Lee(tooth-is-silver)
4
+ */
5
+ type RuleOptions = [{
6
+ punctuation: string[];
7
+ }];
8
+ declare const _default: {
9
+ readonly meta: {
10
+ readonly type: "problem";
11
+ readonly docs: {
12
+ readonly description: "Disallow trailing punctuation in headings";
13
+ readonly url: string;
14
+ readonly recommended: false;
15
+ readonly stylistic: true;
16
+ };
17
+ readonly fixable: "code";
18
+ readonly schema: [{
19
+ readonly type: "object";
20
+ readonly properties: {
21
+ readonly punctuation: {
22
+ readonly type: "array";
23
+ readonly items: {
24
+ readonly type: "string";
25
+ readonly minLength: 1;
26
+ readonly maxLength: 1;
27
+ };
28
+ readonly minItems: 1;
29
+ readonly uniqueItems: true;
30
+ };
31
+ };
32
+ readonly additionalProperties: false;
33
+ }];
34
+ readonly defaultOptions: [{
35
+ readonly punctuation: [".", ",", ";", ":", "!", "。", ",", ";", ":", "!"];
36
+ }];
37
+ readonly messages: {
38
+ readonly noTrailingHeadingPunctuation: "Trailing punctuation `{{ punctuation }}` is not allowed in headings.";
39
+ };
40
+ readonly language: "markdown";
41
+ readonly dialects: ["commonmark", "gfm"];
42
+ };
43
+ readonly create: (context: import("@eslint/core").RuleContext<{
44
+ LangOptions: import("@eslint/markdown").MarkdownLanguageOptions;
45
+ Code: import("@eslint/markdown").MarkdownSourceCode;
46
+ RuleOptions: RuleOptions;
47
+ Node: import("mdast").Node;
48
+ MessageIds: "noTrailingHeadingPunctuation";
49
+ }>) => {
50
+ heading(node: import("mdast").Heading): void;
51
+ };
52
+ };
53
+ export default _default;
@@ -0,0 +1,187 @@
1
+ /**
2
+ * @fileoverview Rule to disallow trailing punctuation in headings.
3
+ * @author Ga eun Lee(tooth-is-silver)
4
+ */
5
+ // --------------------------------------------------------------------------------
6
+ // Import
7
+ // --------------------------------------------------------------------------------
8
+ import { escapeStringRegexp } from '../core/utils/index.js';
9
+ import { URL_RULE_DOCS } from '../core/constants.js';
10
+ // --------------------------------------------------------------------------------
11
+ // Helper
12
+ // --------------------------------------------------------------------------------
13
+ /**
14
+ * Regular expression for identifying a GitHub emoji code at the end of a line.
15
+ * - NOTE: These patterns are based on the `markdownlint`.
16
+ * @see https://github.com/DavidAnson/markdownlint/blob/v0.41.1/helpers/helpers.cjs#L36-L38
17
+ */
18
+ const trailingGemojiRegex = /:(?:[abmovx]|[-+]1|100|1234|(?:1st|2nd|3rd)_place_medal|8ball|clock\d{1,4}|e-mail|non-potable_water|o2|t-rex|u5272|u5408|u55b6|u6307|u6708|u6709|u6e80|u7121|u7533|u7981|u7a7a|[a-z]{2,15}2?|[a-z]{1,14}(?:_[a-z\d]{1,16})+):$/;
19
+ /**
20
+ * Regular expression for identifying an HTML entity at the end of a line.
21
+ * - NOTE: These patterns are based on the `markdownlint`.
22
+ * @see https://github.com/DavidAnson/markdownlint/blob/v0.41.1/helpers/helpers.cjs#L32-L34
23
+ */
24
+ const trailingHtmlEntityRegex = /&(?:#\d+|#[xX][\da-fA-F]+|[a-zA-Z]{2,31}|blk\d{2}|emsp1[34]|frac\d{2}|sup\d|there4);$/;
25
+ // --------------------------------------------------------------------------------
26
+ // Rule Definition
27
+ // --------------------------------------------------------------------------------
28
+ export default {
29
+ meta: {
30
+ type: 'problem',
31
+ docs: {
32
+ description: 'Disallow trailing punctuation in headings',
33
+ url: URL_RULE_DOCS('no-trailing-heading-punctuation'),
34
+ recommended: false,
35
+ stylistic: true,
36
+ },
37
+ fixable: 'code',
38
+ schema: [
39
+ {
40
+ type: 'object',
41
+ properties: {
42
+ punctuation: {
43
+ type: 'array',
44
+ items: {
45
+ type: 'string',
46
+ minLength: 1,
47
+ maxLength: 1,
48
+ },
49
+ minItems: 1,
50
+ uniqueItems: true,
51
+ },
52
+ },
53
+ additionalProperties: false,
54
+ },
55
+ ],
56
+ defaultOptions: [
57
+ {
58
+ punctuation: ['.', ',', ';', ':', '!', '。', ',', ';', ':', '!'],
59
+ },
60
+ ],
61
+ messages: {
62
+ noTrailingHeadingPunctuation: 'Trailing punctuation `{{ punctuation }}` is not allowed in headings.',
63
+ },
64
+ language: 'markdown',
65
+ dialects: ['commonmark', 'gfm'],
66
+ },
67
+ create(context) {
68
+ const { sourceCode } = context;
69
+ const [{ punctuation }] = context.options;
70
+ const trailingPunctuationRegex = new RegExp(`[ \\t\\r\\n]*[${escapeStringRegexp(punctuation.join(''))}]+$`);
71
+ return {
72
+ heading(node) {
73
+ /*
74
+ * NOTE: This behavior is consistent with the `markdownlint` rule `MD026`.
75
+ *
76
+ * Instead of using deep recursive traversal to find the final `text` node,
77
+ * we simply access the node's last child directly with shallow traversal.
78
+ *
79
+ * This is because in the "Not OK" case, the `text` node is located
80
+ * in the last position while DFS(Depth First Search) traversal,
81
+ * but is located under `emphasis` or `strong` nodes.
82
+ *
83
+ * This is the situation we don't want to support.
84
+ * The trailing punctuation must be in a plain `text` node at the end
85
+ * of the heading without being wrapped by other nodes.
86
+ *
87
+ * OK:
88
+ *
89
+ * ```md
90
+ * # heading .
91
+ * ```
92
+ *
93
+ * Not OK:
94
+ *
95
+ * ```md
96
+ * # heading *.*
97
+ * ^ ^
98
+ *
99
+ * # heading **.**
100
+ * ^^ ^^
101
+ * ```
102
+ */
103
+ const lastChildNode = node.children.at(-1);
104
+ /*
105
+ * ATX headings and closed ATX headings that contain no content have no child nodes.
106
+ *
107
+ * ATX Headings:
108
+ *
109
+ * ```md
110
+ * #
111
+ *
112
+ * ##
113
+ *
114
+ * ###
115
+ * ```
116
+ *
117
+ * ATX Closed Headings:
118
+ *
119
+ * ```md
120
+ * # #
121
+ *
122
+ * ## ##
123
+ *
124
+ * ### ###
125
+ * ```
126
+ */
127
+ if (!lastChildNode || lastChildNode.type !== 'text') {
128
+ // If there is no child node or trailing `text` node, intentionally skip the heading.
129
+ return;
130
+ }
131
+ const lastChildText = sourceCode.getText(lastChildNode);
132
+ const match = trailingPunctuationRegex.exec(lastChildText);
133
+ if (!match) {
134
+ return;
135
+ }
136
+ let trailingPunctuation = match[0];
137
+ /*
138
+ * Slice from the beginning of the trailing `text` node through the first
139
+ * character matched by `trailingPunctuationRegex`, including that character.
140
+ *
141
+ * The first matched character can be the closing `:` of a gemoji or the
142
+ * closing `;` of an HTML entity. Including it allows the end-anchored regular
143
+ * expressions to recognize the construct while excluding any punctuation
144
+ * that follows it.
145
+ *
146
+ * Gemoji:
147
+ *
148
+ * ```text
149
+ * lastChildText: Heading :smile:.
150
+ * textThroughFirstTrailingPunctuation: Heading :smile:
151
+ * ```
152
+ *
153
+ * HTML entity:
154
+ *
155
+ * ```text
156
+ * lastChildText: Copyright &copy;.
157
+ * textThroughFirstTrailingPunctuation: Copyright &copy;
158
+ * ```
159
+ */
160
+ const textThroughFirstTrailingPunctuation = lastChildText.slice(0, match.index + 1);
161
+ if (trailingGemojiRegex.test(textThroughFirstTrailingPunctuation) ||
162
+ trailingHtmlEntityRegex.test(textThroughFirstTrailingPunctuation)) {
163
+ trailingPunctuation = trailingPunctuation.slice(1);
164
+ }
165
+ if (!trailingPunctuation) {
166
+ // If the match contains only a gemoji or HTML entity terminator, skip it.
167
+ return;
168
+ }
169
+ const [, endOffset] = sourceCode.getRange(lastChildNode);
170
+ const startOffset = endOffset - trailingPunctuation.length;
171
+ context.report({
172
+ loc: {
173
+ start: sourceCode.getLocFromIndex(startOffset),
174
+ end: sourceCode.getLocFromIndex(endOffset),
175
+ },
176
+ data: {
177
+ punctuation: trailingPunctuation,
178
+ },
179
+ messageId: 'noTrailingHeadingPunctuation',
180
+ fix(fixer) {
181
+ return fixer.removeRange([startOffset, endOffset]);
182
+ },
183
+ });
184
+ },
185
+ };
186
+ },
187
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-markdown",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Lint your Markdown with ESLint. Additional rules for use with `@eslint/markdown`.🛠️",
@@ -78,7 +78,7 @@
78
78
  "devDependencies": {
79
79
  "@types/mdast": "^4.0.4",
80
80
  "@types/unist": "^3.0.3",
81
- "eslint": "^9.39.4",
81
+ "eslint": "^9.39.5",
82
82
  "vitest": "^4.1.9"
83
83
  }
84
84
  }