eslint-markdown 0.5.0 → 0.6.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
@@ -25,6 +25,12 @@ For full documentation, see the [official documentation of the `eslint-markdown`
25
25
  - [Migration Guide](https://eslint-markdown.lumir.page/docs/get-started/migration)
26
26
  - [Rules](https://eslint-markdown.lumir.page/docs/rules)
27
27
 
28
+ ## Compatibility
29
+
30
+ This [`eslint-markdown`](https://github.com/lumirlumir/npm-eslint-markdown#readme) plugin does not include any rules that overlap with ESLint's built-in Markdown rules provided by [`@eslint/markdown`](https://github.com/eslint/markdown#readme).
31
+
32
+ So, we **highly recommend** using the `eslint-markdown` plugin alongside ESLint's built-in Markdown support, `@eslint/markdown`.
33
+
28
34
  ## Code of Conduct
29
35
 
30
36
  See [Code of Conduct](https://github.com/lumirlumir/.github/blob/main/CODE_OF_CONDUCT.md#contributor-covenant-code-of-conduct).
@@ -20,6 +20,7 @@ export default function all(plugin: ESLint.Plugin): {
20
20
  readonly 'md/consistent-code-style': "error";
21
21
  readonly 'md/consistent-delete-style': "error";
22
22
  readonly 'md/consistent-emphasis-style': "error";
23
+ readonly 'md/consistent-inline-code-style': "error";
23
24
  readonly 'md/consistent-strong-style': "error";
24
25
  readonly 'md/consistent-thematic-break-style': "error";
25
26
  readonly 'md/consistent-unordered-list-style': "error";
@@ -17,6 +17,7 @@ export default function stylistic(plugin: ESLint.Plugin): {
17
17
  readonly 'md/consistent-code-style': "error";
18
18
  readonly 'md/consistent-delete-style': "error";
19
19
  readonly 'md/consistent-emphasis-style': "error";
20
+ readonly 'md/consistent-inline-code-style': "error";
20
21
  readonly 'md/consistent-strong-style': "error";
21
22
  readonly 'md/consistent-thematic-break-style': "error";
22
23
  readonly 'md/consistent-unordered-list-style': "error";
@@ -1,3 +1,4 @@
1
1
  import getElementsByTagName from './html.js';
2
+ import isBlankLine from './is-blank-line.js';
2
3
  import SkipRanges from './skip-ranges.js';
3
- export { getElementsByTagName, SkipRanges };
4
+ export { getElementsByTagName, isBlankLine, SkipRanges };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Check if a line is blank.
3
+ * @param {string} str Line string.
4
+ * @returns {boolean} `true` if the line is blank. `false` otherwise.
5
+ */
6
+ export default function isBlankLine(str: string): boolean;
@@ -1,4 +1,4 @@
1
- /** @satisfies {string} */
1
+ /** @type {'eslint-markdown'} */
2
2
  export const PKG_NAME: "eslint-markdown";
3
3
  /** @satisfies {string} */
4
4
  export const PKG_VERSION: string;
@@ -8,6 +8,6 @@ export default function ruleTester(ruleName: string, rule: RuleModule<RuleOption
8
8
  export type RuleOptions = MarkdownRuleDefinitionTypeOptions["RuleOptions"];
9
9
  export type MessageIds = MarkdownRuleDefinitionTypeOptions["MessageIds"];
10
10
  export type Tests = Parameters<RuleTester["run"]>[2];
11
- import type { RuleModule } from '../types.js';
11
+ import type { RuleModule } from './types.js';
12
12
  import type { MarkdownRuleDefinitionTypeOptions } from '@eslint/markdown';
13
13
  import { RuleTester } from 'eslint';
@@ -13,14 +13,40 @@ declare const _default: {
13
13
  style: {
14
14
  enum: string[];
15
15
  };
16
+ blankLineAbove: {
17
+ oneOf: ({
18
+ enum: false[];
19
+ type?: never;
20
+ minimum?: never;
21
+ } | {
22
+ type: "integer";
23
+ minimum: number;
24
+ enum?: never;
25
+ })[];
26
+ };
27
+ blankLineBelow: {
28
+ oneOf: ({
29
+ enum: false[];
30
+ type?: never;
31
+ minimum?: never;
32
+ } | {
33
+ type: "integer";
34
+ minimum: number;
35
+ enum?: never;
36
+ })[];
37
+ };
16
38
  };
17
39
  additionalProperties: false;
18
40
  }[];
19
41
  defaultOptions: [{
20
42
  style: "consistent";
43
+ blankLineAbove: false;
44
+ blankLineBelow: false;
21
45
  }];
22
46
  messages: {
23
47
  style: string;
48
+ blankLineAbove: string;
49
+ blankLineBelow: string;
24
50
  };
25
51
  language: string;
26
52
  dialects: string[];
@@ -30,7 +56,7 @@ declare const _default: {
30
56
  Code: import("@eslint/markdown").MarkdownSourceCode;
31
57
  RuleOptions: RuleOptions;
32
58
  Node: import("mdast").Node;
33
- MessageIds: "style";
59
+ MessageIds: MessageIds;
34
60
  }>): {
35
61
  code(node: import("mdast").Code): void;
36
62
  };
@@ -39,5 +65,7 @@ export default _default;
39
65
  export type CodeStyle = "indent" | "fence-backtick" | "fence-tilde";
40
66
  export type RuleOptions = [{
41
67
  style: "consistent" | CodeStyle;
68
+ blankLineAbove: number | false;
69
+ blankLineBelow: number | false;
42
70
  }];
43
- export type MessageIds = "style";
71
+ export type MessageIds = "style" | "blankLineAbove" | "blankLineBelow";
@@ -0,0 +1,29 @@
1
+ declare const _default: {
2
+ meta: {
3
+ type: "layout";
4
+ docs: {
5
+ description: string;
6
+ url: string;
7
+ recommended: boolean;
8
+ stylistic: true;
9
+ };
10
+ fixable: "whitespace";
11
+ messages: {
12
+ style: string;
13
+ };
14
+ language: string;
15
+ dialects: string[];
16
+ };
17
+ create(context: import("@eslint/core").RuleContext<{
18
+ LangOptions: import("@eslint/markdown").MarkdownLanguageOptions;
19
+ Code: import("@eslint/markdown").MarkdownSourceCode;
20
+ RuleOptions: [];
21
+ Node: import("mdast").Node;
22
+ MessageIds: "style";
23
+ }>): {
24
+ inlineCode(node: import("mdast").InlineCode): void;
25
+ };
26
+ };
27
+ export default _default;
28
+ export type RuleOptions = [];
29
+ export type MessageIds = "style";
@@ -2,9 +2,10 @@ declare const _default: {
2
2
  'allow-image-url': import("../core/types.js").RuleModule<import("./allow-image-url.js").RuleOptions, import("./allow-image-url.js").MessageIds>;
3
3
  'allow-link-url': import("../core/types.js").RuleModule<import("./allow-link-url.js").RuleOptions, import("./allow-link-url.js").MessageIds>;
4
4
  'code-lang-shorthand': import("../core/types.js").RuleModule<import("./code-lang-shorthand.js").RuleOptions, "codeLangShorthand">;
5
- 'consistent-code-style': import("../core/types.js").RuleModule<import("./consistent-code-style.js").RuleOptions, "style">;
5
+ 'consistent-code-style': import("../core/types.js").RuleModule<import("./consistent-code-style.js").RuleOptions, import("./consistent-code-style.js").MessageIds>;
6
6
  'consistent-delete-style': import("../core/types.js").RuleModule<import("./consistent-delete-style.js").RuleOptions, "style">;
7
7
  'consistent-emphasis-style': import("../core/types.js").RuleModule<import("./consistent-emphasis-style.js").RuleOptions, "style">;
8
+ 'consistent-inline-code-style': import("../core/types.js").RuleModule<[], "style">;
8
9
  'consistent-strong-style': import("../core/types.js").RuleModule<import("./consistent-strong-style.js").RuleOptions, "style">;
9
10
  'consistent-thematic-break-style': import("../core/types.js").RuleModule<import("./consistent-thematic-break-style.js").RuleOptions, "style">;
10
11
  'consistent-unordered-list-style': import("../core/types.js").RuleModule<import("./consistent-unordered-list-style.js").RuleOptions, "style">;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-markdown",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "Lint your Markdown with ESLint.🛠️",
6
6
  "exports": {
@@ -40,6 +40,7 @@
40
40
  "gfm"
41
41
  ],
42
42
  "author": "루밀LuMir <rpfos@naver.com> (https://github.com/lumirlumir)",
43
+ "funding": "https://github.com/sponsors/lumirlumir",
43
44
  "license": "MIT",
44
45
  "homepage": "https://eslint-markdown.lumir.page",
45
46
  "repository": {
@@ -78,6 +79,6 @@
78
79
  },
79
80
  "devDependencies": {
80
81
  "@types/mdast": "^4.0.4",
81
- "eslint": "^9.39.2"
82
+ "eslint": "^9.39.4"
82
83
  }
83
84
  }
@@ -44,6 +44,7 @@ export default function all(plugin) {
44
44
  'md/consistent-code-style': 'error',
45
45
  'md/consistent-delete-style': 'error',
46
46
  'md/consistent-emphasis-style': 'error',
47
+ 'md/consistent-inline-code-style': 'error',
47
48
  'md/consistent-strong-style': 'error',
48
49
  'md/consistent-thematic-break-style': 'error',
49
50
  'md/consistent-unordered-list-style': 'error',
@@ -41,6 +41,7 @@ export default function stylistic(plugin) {
41
41
  'md/consistent-code-style': 'error',
42
42
  'md/consistent-delete-style': 'error',
43
43
  'md/consistent-emphasis-style': 'error',
44
+ 'md/consistent-inline-code-style': 'error',
44
45
  'md/consistent-strong-style': 'error',
45
46
  'md/consistent-thematic-break-style': 'error',
46
47
  'md/consistent-unordered-list-style': 'error',
@@ -9,7 +9,7 @@
9
9
  import { parseFragment } from 'parse5';
10
10
 
11
11
  // --------------------------------------------------------------------------------
12
- // Typedefs
12
+ // Typedef
13
13
  // --------------------------------------------------------------------------------
14
14
 
15
15
  /**
@@ -1,4 +1,5 @@
1
1
  import getElementsByTagName from './html.js';
2
+ import isBlankLine from './is-blank-line.js';
2
3
  import SkipRanges from './skip-ranges.js';
3
4
 
4
- export { getElementsByTagName, SkipRanges };
5
+ export { getElementsByTagName, isBlankLine, SkipRanges };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @fileoverview Check if a line is blank.
3
+ * @see https://spec.commonmark.org/0.31.2/#blank-line
4
+ */
5
+
6
+ // --------------------------------------------------------------------------------
7
+ // Helper
8
+ // --------------------------------------------------------------------------------
9
+
10
+ const whitespaceChars = new Set([' ', '\t']);
11
+
12
+ // --------------------------------------------------------------------------------
13
+ // Export
14
+ // --------------------------------------------------------------------------------
15
+
16
+ /**
17
+ * Check if a line is blank.
18
+ * @param {string} str Line string.
19
+ * @returns {boolean} `true` if the line is blank. `false` otherwise.
20
+ */
21
+ export default function isBlankLine(str) {
22
+ // `.length` is cached for performance.
23
+ const strLength = str.length;
24
+
25
+ for (let i = 0; i < strLength; i++) {
26
+ if (!whitespaceChars.has(str[i])) {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ return true;
32
+ }
@@ -6,22 +6,15 @@
6
6
  // Import
7
7
  // --------------------------------------------------------------------------------
8
8
 
9
- import { createRequire } from 'node:module';
10
-
11
- // --------------------------------------------------------------------------------
12
- // Declaration
13
- // --------------------------------------------------------------------------------
14
-
15
- /** @type {{ homepage: string, name: 'eslint-markdown', version: string }} */
16
- const { homepage, name, version } = createRequire(import.meta.url)('../../package.json');
9
+ import pkg from '../../package.json' with { type: 'json' };
17
10
 
18
11
  // --------------------------------------------------------------------------------
19
12
  // Export
20
13
  // --------------------------------------------------------------------------------
21
14
 
15
+ /** @type {'eslint-markdown'} */
16
+ export const PKG_NAME = /** @type {'eslint-markdown'} */ (pkg.name);
22
17
  /** @satisfies {string} */
23
- export const PKG_NAME = name;
24
- /** @satisfies {string} */
25
- export const PKG_VERSION = version;
18
+ export const PKG_VERSION = pkg.version;
26
19
  /** Get the URL for the rule documentation based on the rule name. @param {string} [ruleName] */
27
- export const URL_RULE_DOCS = (ruleName = '') => `${homepage}/docs/rules/${ruleName}`;
20
+ export const URL_RULE_DOCS = (ruleName = '') => `${pkg.homepage}/docs/rules/${ruleName}`;
@@ -17,7 +17,7 @@ import markdown from '@eslint/markdown';
17
17
 
18
18
  /**
19
19
  * @import { MarkdownRuleDefinitionTypeOptions } from '@eslint/markdown';
20
- * @import { RuleModule } from '../types.js';
20
+ * @import { RuleModule } from './types.js';
21
21
  * @typedef {MarkdownRuleDefinitionTypeOptions['RuleOptions']} RuleOptions
22
22
  * @typedef {MarkdownRuleDefinitionTypeOptions['MessageIds']} MessageIds
23
23
  * @typedef {Parameters<RuleTester['run']>[2]} Tests
@@ -17,6 +17,7 @@
17
17
  // Import
18
18
  // --------------------------------------------------------------------------------
19
19
 
20
+ import { isBlankLine } from '../core/ast/index.js';
20
21
  import { URL_RULE_DOCS } from '../core/constants.js';
21
22
 
22
23
  // --------------------------------------------------------------------------------
@@ -26,8 +27,8 @@ import { URL_RULE_DOCS } from '../core/constants.js';
26
27
  /**
27
28
  * @import { RuleModule } from '../core/types.js';
28
29
  * @typedef {'indent' | 'fence-backtick' | 'fence-tilde'} CodeStyle
29
- * @typedef {[{ style: 'consistent' | CodeStyle }]} RuleOptions
30
- * @typedef {'style'} MessageIds
30
+ * @typedef {[{ style: 'consistent' | CodeStyle, blankLineAbove: number | false, blankLineBelow: number | false }]} RuleOptions
31
+ * @typedef {'style' | 'blankLineAbove' | 'blankLineBelow'} MessageIds
31
32
  */
32
33
 
33
34
  // --------------------------------------------------------------------------------
@@ -76,6 +77,28 @@ export default {
76
77
  style: {
77
78
  enum: ['consistent', 'indent', 'fence-backtick', 'fence-tilde'],
78
79
  },
80
+ blankLineAbove: {
81
+ oneOf: [
82
+ {
83
+ enum: [false],
84
+ },
85
+ {
86
+ type: 'integer',
87
+ minimum: 1,
88
+ },
89
+ ],
90
+ },
91
+ blankLineBelow: {
92
+ oneOf: [
93
+ {
94
+ enum: [false],
95
+ },
96
+ {
97
+ type: 'integer',
98
+ minimum: 1,
99
+ },
100
+ ],
101
+ },
79
102
  },
80
103
  additionalProperties: false,
81
104
  },
@@ -84,11 +107,17 @@ export default {
84
107
  defaultOptions: [
85
108
  {
86
109
  style: 'consistent',
110
+ blankLineAbove: false,
111
+ blankLineBelow: false,
87
112
  },
88
113
  ],
89
114
 
90
115
  messages: {
91
116
  style: 'Code style should be `{{ style }}`.',
117
+ blankLineAbove:
118
+ 'Code should be surrounded by {{ blankLineAbove }} blank line(s) above.',
119
+ blankLineBelow:
120
+ 'Code should be surrounded by {{ blankLineBelow }} blank line(s) below.',
92
121
  },
93
122
 
94
123
  language: 'markdown',
@@ -98,13 +127,18 @@ export default {
98
127
 
99
128
  create(context) {
100
129
  const { sourceCode } = context;
101
- const [{ style }] = context.options;
130
+ const { lines } = sourceCode;
131
+ const [{ style, blankLineAbove, blankLineBelow }] = context.options;
102
132
 
103
133
  /** @type {CodeStyle | null} */
104
134
  let codeStyle = style === 'consistent' ? null : style;
105
135
 
106
136
  return {
107
137
  code(node) {
138
+ // ------------------------------------------------------------------------
139
+ // 1. Check code style consistency.
140
+ // ------------------------------------------------------------------------
141
+
108
142
  const [nodeStartOffset] = sourceCode.getRange(node);
109
143
  const currentCodeStyle = getCurrentCodeStyle(sourceCode.text[nodeStartOffset]);
110
144
 
@@ -123,6 +157,92 @@ export default {
123
157
  },
124
158
  });
125
159
  }
160
+
161
+ // ------------------------------------------------------------------------
162
+ // 2. Check blank lines above the code block.
163
+ // ------------------------------------------------------------------------
164
+
165
+ // `markdownlint` doesn't check blank lines above indented code blocks, so we skip this check for the `indent` style.
166
+ if (blankLineAbove !== false && currentCodeStyle !== 'indent') {
167
+ const {
168
+ start: { line: nodeStartLine },
169
+ } = sourceCode.getLoc(node);
170
+ const nodeStartLineIndex = nodeStartLine - 1;
171
+
172
+ for (
173
+ let i = nodeStartLineIndex - 1; // Start checking from the line above the code block.
174
+ i >= nodeStartLineIndex - blankLineAbove; // Check up to the specified number of blank lines.
175
+ i-- // Move upwards through the lines.
176
+ ) {
177
+ const line = lines[i];
178
+
179
+ // If the line is `undefined`, it means we've reached the beginning of the file.
180
+ if (line === undefined) {
181
+ break;
182
+ }
183
+
184
+ // If the line is blank, continue checking the next line. If it's not blank, report the issue.
185
+ if (isBlankLine(line)) {
186
+ continue;
187
+ }
188
+
189
+ context.report({
190
+ node,
191
+
192
+ messageId: 'blankLineAbove',
193
+
194
+ data: {
195
+ blankLineAbove,
196
+ },
197
+ });
198
+
199
+ // No need to check further once we've found a non-blank line.
200
+ break;
201
+ }
202
+ }
203
+
204
+ // ------------------------------------------------------------------------
205
+ // 3. Check blank lines below the code block.
206
+ // ------------------------------------------------------------------------
207
+
208
+ // `markdownlint` doesn't check blank lines below indented code blocks, so we skip this check for the `indent` style.
209
+ if (blankLineBelow !== false && currentCodeStyle !== 'indent') {
210
+ const {
211
+ end: { line: nodeEndLine },
212
+ } = sourceCode.getLoc(node);
213
+ const nodeEndLineIndex = nodeEndLine - 1;
214
+
215
+ for (
216
+ let i = nodeEndLineIndex + 1; // Start checking from the line below the code block.
217
+ i <= nodeEndLineIndex + blankLineBelow; // Check up to the specified number of blank lines.
218
+ i++ // Move downwards through the lines.
219
+ ) {
220
+ const line = lines[i];
221
+
222
+ // If the line is `undefined`, it means we've reached the end of the file.
223
+ if (line === undefined) {
224
+ break;
225
+ }
226
+
227
+ // If the line is blank, continue checking the next line. If it's not blank, report the issue.
228
+ if (isBlankLine(line)) {
229
+ continue;
230
+ }
231
+
232
+ context.report({
233
+ node,
234
+
235
+ messageId: 'blankLineBelow',
236
+
237
+ data: {
238
+ blankLineBelow,
239
+ },
240
+ });
241
+
242
+ // No need to check further once we've found a non-blank line.
243
+ break;
244
+ }
245
+ }
126
246
  },
127
247
  };
128
248
  },
@@ -0,0 +1,152 @@
1
+ /**
2
+ * @fileoverview Rule to enforce consistent inline code style.
3
+ * @author 루밀LuMir(lumirlumir)
4
+ * @see https://github.com/DavidAnson/markdownlint/blob/v0.40.0/lib/md038.mjs
5
+ */
6
+
7
+ // --------------------------------------------------------------------------------
8
+ // Import
9
+ // --------------------------------------------------------------------------------
10
+
11
+ import { URL_RULE_DOCS } from '../core/constants.js';
12
+
13
+ // --------------------------------------------------------------------------------
14
+ // Typedef
15
+ // --------------------------------------------------------------------------------
16
+
17
+ /**
18
+ * @import { RuleModule } from '../core/types.js';
19
+ * @typedef {[]} RuleOptions
20
+ * @typedef {'style'} MessageIds
21
+ */
22
+
23
+ // --------------------------------------------------------------------------------
24
+ // Helper
25
+ // --------------------------------------------------------------------------------
26
+
27
+ // `\s` in regular expressions matches whitespace characters beyond `\r`, `\n`, ` `, and `\t`,
28
+ // so we explicitly use `[\r\n \t]` to match those characters to avoid unexpected matches.
29
+ const leadingInlineCodeRegex =
30
+ /^(?<leadingBackticks>`*)(?<leadingSpaces>[\r\n \t]+)(?<firstChar>[^\r\n \t])/;
31
+ const trailingInlineCodeRegex =
32
+ /(?<lastChar>[^\r\n \t])(?<trailingSpaces>[\r\n \t]+)(?<trailingBackticks>`*)$/;
33
+
34
+ // --------------------------------------------------------------------------------
35
+ // Rule Definition
36
+ // --------------------------------------------------------------------------------
37
+
38
+ /** @type {RuleModule<RuleOptions, MessageIds>} */
39
+ export default {
40
+ meta: {
41
+ type: 'layout',
42
+
43
+ docs: {
44
+ description: 'Enforce consistent inline code style',
45
+ url: URL_RULE_DOCS('consistent-inline-code-style'),
46
+ recommended: false,
47
+ stylistic: true,
48
+ },
49
+
50
+ fixable: 'whitespace',
51
+
52
+ messages: {
53
+ style: 'Inline code should not have extra spaces or tabs next to backticks.',
54
+ },
55
+
56
+ language: 'markdown',
57
+
58
+ dialects: ['commonmark', 'gfm'],
59
+ },
60
+
61
+ create(context) {
62
+ const { sourceCode } = context;
63
+
64
+ /**
65
+ * @param {number} startOffset
66
+ * @param {number} endOffset
67
+ */
68
+ function reportStyle(startOffset, endOffset) {
69
+ context.report({
70
+ loc: {
71
+ start: sourceCode.getLocFromIndex(startOffset),
72
+ end: sourceCode.getLocFromIndex(endOffset),
73
+ },
74
+
75
+ messageId: 'style',
76
+
77
+ fix(fixer) {
78
+ return fixer.removeRange([startOffset, endOffset]);
79
+ },
80
+ });
81
+ }
82
+
83
+ return {
84
+ inlineCode(node) {
85
+ // ------------------------------------------------------------------------
86
+ // 1. Extract the text and offsets of the inline code node.
87
+ // ------------------------------------------------------------------------
88
+
89
+ const text = sourceCode.getText(node);
90
+ const [nodeStartOffset, nodeEndOffset] = sourceCode.getRange(node);
91
+
92
+ // ------------------------------------------------------------------------
93
+ // 2. Extract the leading spaces and backticks of the inline code node.
94
+ // ------------------------------------------------------------------------
95
+
96
+ const { leadingBackticks = '', leadingSpaces: leadingSpacesText = '' } =
97
+ text.match(leadingInlineCodeRegex)?.groups ?? {};
98
+ const { leadingSpaces: leadingSpacesValue = '', firstChar = '' } =
99
+ node.value.match(leadingInlineCodeRegex)?.groups ?? {};
100
+
101
+ const startBacktick = firstChar === '`';
102
+ const startPaddingLength = /** @type {0 | 1} */ (
103
+ // `startPaddingLength` is always `0` or `1` because the parser consumes at most one padding space on each side.
104
+ leadingSpacesText.length - leadingSpacesValue.length
105
+ );
106
+ const startBacktickSpaceAdjustment = startBacktick && !startPaddingLength ? 1 : 0;
107
+ const startSpaces = leadingSpacesValue.length > startBacktickSpaceAdjustment;
108
+
109
+ // ------------------------------------------------------------------------
110
+ // 3. Extract the trailing spaces and backticks of the inline code node.
111
+ // ------------------------------------------------------------------------
112
+
113
+ const { trailingSpaces: trailingSpacesText = '', trailingBackticks = '' } =
114
+ text.match(trailingInlineCodeRegex)?.groups ?? {};
115
+ const { lastChar = '', trailingSpaces: trailingSpacesValue = '' } =
116
+ node.value.match(trailingInlineCodeRegex)?.groups ?? {};
117
+
118
+ const endBacktick = lastChar === '`';
119
+ const endPaddingLength = /** @type {0 | 1} */ (
120
+ // `endPaddingLength` is always `0` or `1` because the parser consumes at most one padding space on each side.
121
+ trailingSpacesText.length - trailingSpacesValue.length
122
+ );
123
+ const endBacktickSpaceAdjustment = endBacktick && !endPaddingLength ? 1 : 0;
124
+ const endSpaces = trailingSpacesValue.length > endBacktickSpaceAdjustment;
125
+
126
+ // ------------------------------------------------------------------------
127
+ // 4. Report if there are extra spaces or tabs next to backticks.
128
+ // ------------------------------------------------------------------------
129
+
130
+ const removePadding = startSpaces && endSpaces && !startBacktick && !endBacktick;
131
+
132
+ if (startSpaces) {
133
+ const baseOffset = nodeStartOffset + leadingBackticks.length;
134
+
135
+ reportStyle(
136
+ baseOffset + (removePadding ? 0 : startPaddingLength),
137
+ baseOffset + leadingSpacesText.length - startBacktickSpaceAdjustment,
138
+ );
139
+ }
140
+
141
+ if (endSpaces) {
142
+ const baseOffset = nodeEndOffset - trailingBackticks.length;
143
+
144
+ reportStyle(
145
+ baseOffset - trailingSpacesText.length + endBacktickSpaceAdjustment,
146
+ baseOffset - (removePadding ? 0 : endPaddingLength),
147
+ );
148
+ }
149
+ },
150
+ };
151
+ },
152
+ };
@@ -9,6 +9,7 @@ import codeLangShorthand from './code-lang-shorthand.js';
9
9
  import consistentCodeStyle from './consistent-code-style.js';
10
10
  import consistentDeleteStyle from './consistent-delete-style.js';
11
11
  import consistentEmphasisStyle from './consistent-emphasis-style.js';
12
+ import consistentInlineCodeStyle from './consistent-inline-code-style.js';
12
13
  import consistentStrongStyle from './consistent-strong-style.js';
13
14
  import consistentThematicBreakStyle from './consistent-thematic-break-style.js';
14
15
  import consistentUnorderedListStyle from './consistent-unordered-list-style.js';
@@ -35,6 +36,7 @@ export default {
35
36
  'consistent-code-style': consistentCodeStyle,
36
37
  'consistent-delete-style': consistentDeleteStyle,
37
38
  'consistent-emphasis-style': consistentEmphasisStyle,
39
+ 'consistent-inline-code-style': consistentInlineCodeStyle,
38
40
  'consistent-strong-style': consistentStrongStyle,
39
41
  'consistent-thematic-break-style': consistentThematicBreakStyle,
40
42
  'consistent-unordered-list-style': consistentUnorderedListStyle,
@@ -1,6 +0,0 @@
1
- /**
2
- * Get the file name of the module.
3
- * @param {string} importMetaUrl The absolute `file:` URL of the module.
4
- * @returns {string} The file name of the module.
5
- */
6
- export default function getFileName(importMetaUrl: string): string;
@@ -1,3 +0,0 @@
1
- import getFileName from './get-file-name.js';
2
- import ruleTester from './rule-tester.js';
3
- export { getFileName, ruleTester };
@@ -1,23 +0,0 @@
1
- /**
2
- * @fileoverview Get the file name of the module.
3
- */
4
-
5
- // --------------------------------------------------------------------------------
6
- // Import
7
- // --------------------------------------------------------------------------------
8
-
9
- import { fileURLToPath } from 'node:url';
10
- import { parse } from 'node:path';
11
-
12
- // --------------------------------------------------------------------------------
13
- // Export
14
- // --------------------------------------------------------------------------------
15
-
16
- /**
17
- * Get the file name of the module.
18
- * @param {string} importMetaUrl The absolute `file:` URL of the module.
19
- * @returns {string} The file name of the module.
20
- */
21
- export default function getFileName(importMetaUrl) {
22
- return parse(fileURLToPath(importMetaUrl)).name.replace(/\.test$/, '');
23
- }
@@ -1,6 +0,0 @@
1
- /* eslint sort-imports: 'error', sort-keys: 'error' */
2
-
3
- import getFileName from './get-file-name.js';
4
- import ruleTester from './rule-tester.js';
5
-
6
- export { getFileName, ruleTester };