eslint-markdown 0.15.0 → 0.16.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/build/configs/all.d.ts +1 -0
- package/build/configs/all.js +1 -0
- package/build/configs/stylistic.d.ts +1 -0
- package/build/configs/stylistic.js +1 -0
- package/build/rules/consistent-heading-style.d.ts +55 -0
- package/build/rules/consistent-heading-style.js +290 -0
- package/build/rules/index.d.ts +56 -1
- package/build/rules/index.js +2 -0
- package/build/rules/no-git-conflict-marker.d.ts +1 -1
- package/build/rules/no-irregular-whitespace.d.ts +20 -0
- package/build/rules/no-irregular-whitespace.js +17 -1
- package/build/rules/no-url-trailing-slash.js +2 -2
- package/package.json +1 -1
package/build/configs/all.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export default function all(plugin: ESLint.Plugin): {
|
|
|
23
23
|
readonly 'md/consistent-code-style': "error";
|
|
24
24
|
readonly 'md/consistent-delete-style': "error";
|
|
25
25
|
readonly 'md/consistent-emphasis-style': "error";
|
|
26
|
+
readonly 'md/consistent-heading-style': "error";
|
|
26
27
|
readonly 'md/consistent-inline-code-style': "error";
|
|
27
28
|
readonly 'md/consistent-strong-style': "error";
|
|
28
29
|
readonly 'md/consistent-thematic-break-style': "error";
|
package/build/configs/all.js
CHANGED
|
@@ -31,6 +31,7 @@ export default function all(plugin) {
|
|
|
31
31
|
'md/consistent-code-style': 'error',
|
|
32
32
|
'md/consistent-delete-style': 'error',
|
|
33
33
|
'md/consistent-emphasis-style': 'error',
|
|
34
|
+
'md/consistent-heading-style': 'error',
|
|
34
35
|
'md/consistent-inline-code-style': 'error',
|
|
35
36
|
'md/consistent-strong-style': 'error',
|
|
36
37
|
'md/consistent-thematic-break-style': 'error',
|
|
@@ -19,6 +19,7 @@ export default function stylistic(plugin: ESLint.Plugin): {
|
|
|
19
19
|
readonly 'md/consistent-code-style': "error";
|
|
20
20
|
readonly 'md/consistent-delete-style': "error";
|
|
21
21
|
readonly 'md/consistent-emphasis-style': "error";
|
|
22
|
+
readonly 'md/consistent-heading-style': "error";
|
|
22
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";
|
|
@@ -27,6 +27,7 @@ export default function stylistic(plugin) {
|
|
|
27
27
|
'md/consistent-code-style': 'error',
|
|
28
28
|
'md/consistent-delete-style': 'error',
|
|
29
29
|
'md/consistent-emphasis-style': 'error',
|
|
30
|
+
'md/consistent-heading-style': 'error',
|
|
30
31
|
'md/consistent-inline-code-style': 'error',
|
|
31
32
|
'md/consistent-strong-style': 'error',
|
|
32
33
|
'md/consistent-thematic-break-style': 'error',
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Rule to enforce consistent heading style.
|
|
3
|
+
* @author Ga eun Lee(tooth-is-silver)
|
|
4
|
+
* @author lumir(lumirlumir)
|
|
5
|
+
* @see https://github.com/DavidAnson/markdownlint/blob/v0.41.1/lib/md003.mjs
|
|
6
|
+
*/
|
|
7
|
+
import type { Heading } from 'mdast';
|
|
8
|
+
type HeadingStyle = (typeof HEADING_STYLE)[number];
|
|
9
|
+
type RuleOptions = [{
|
|
10
|
+
style: HeadingStyle;
|
|
11
|
+
}];
|
|
12
|
+
type MessageIds = 'style' | 'suggestAtxToSetext' | 'suggestAtxClosedToSetext';
|
|
13
|
+
declare const HEADING_STYLE: readonly ["consistent", "atx", "atx-closed", "setext", "setext-with-atx", "setext-with-atx-closed"];
|
|
14
|
+
declare const _default: {
|
|
15
|
+
readonly meta: {
|
|
16
|
+
readonly type: "layout";
|
|
17
|
+
readonly docs: {
|
|
18
|
+
readonly description: "Enforce consistent heading style";
|
|
19
|
+
readonly url: string;
|
|
20
|
+
readonly recommended: false;
|
|
21
|
+
readonly stylistic: true;
|
|
22
|
+
};
|
|
23
|
+
readonly fixable: "code";
|
|
24
|
+
readonly hasSuggestions: true;
|
|
25
|
+
readonly schema: [{
|
|
26
|
+
readonly type: "object";
|
|
27
|
+
readonly properties: {
|
|
28
|
+
readonly style: {
|
|
29
|
+
readonly enum: readonly ["consistent", "atx", "atx-closed", "setext", "setext-with-atx", "setext-with-atx-closed"];
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
readonly additionalProperties: false;
|
|
33
|
+
}];
|
|
34
|
+
readonly defaultOptions: [{
|
|
35
|
+
readonly style: "consistent";
|
|
36
|
+
}];
|
|
37
|
+
readonly messages: {
|
|
38
|
+
readonly style: "Heading style should be `{{ style }}`.";
|
|
39
|
+
readonly suggestAtxToSetext: "Replace ATX heading with a Setext heading.";
|
|
40
|
+
readonly suggestAtxClosedToSetext: "Replace ATX closed heading with a Setext heading.";
|
|
41
|
+
};
|
|
42
|
+
readonly language: "markdown";
|
|
43
|
+
readonly dialects: ["commonmark", "gfm"];
|
|
44
|
+
};
|
|
45
|
+
readonly create: (context: import("../core/types.js").RuleContext<{
|
|
46
|
+
RuleOptions: RuleOptions;
|
|
47
|
+
MessageIds: MessageIds;
|
|
48
|
+
}>) => {
|
|
49
|
+
heading(node: Heading): void;
|
|
50
|
+
"heading[depth<=2]"(): void;
|
|
51
|
+
"heading[depth>2]"(): void;
|
|
52
|
+
'heading:exit'(node: Heading): void;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
export default _default;
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Rule to enforce consistent heading style.
|
|
3
|
+
* @author Ga eun Lee(tooth-is-silver)
|
|
4
|
+
* @author lumir(lumirlumir)
|
|
5
|
+
* @see https://github.com/DavidAnson/markdownlint/blob/v0.41.1/lib/md003.mjs
|
|
6
|
+
*/
|
|
7
|
+
import { URL_RULE_DOCS } from '../core/constants.js';
|
|
8
|
+
// --------------------------------------------------------------------------------
|
|
9
|
+
// Helper
|
|
10
|
+
// --------------------------------------------------------------------------------
|
|
11
|
+
const SETEXT_MAX_DEPTH = 2;
|
|
12
|
+
const HEADING_STYLE = [
|
|
13
|
+
'consistent',
|
|
14
|
+
'atx',
|
|
15
|
+
'atx-closed',
|
|
16
|
+
'setext',
|
|
17
|
+
'setext-with-atx',
|
|
18
|
+
'setext-with-atx-closed',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Matches the closing sequence of a closed ATX heading.
|
|
22
|
+
* @see https://spec.commonmark.org/0.31.2/#atx-headings
|
|
23
|
+
*/
|
|
24
|
+
const trailingAtxHeadingHashRegex = /[ \t]#+[ \t]*$/;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the setext marker for the given heading depth.
|
|
27
|
+
* @param depth The depth of the heading (`1` or `2`).
|
|
28
|
+
* @returns The setext marker for the given heading depth.
|
|
29
|
+
*/
|
|
30
|
+
function getSetextMarker(depth) {
|
|
31
|
+
return depth === 1 ? '=' : '-';
|
|
32
|
+
}
|
|
33
|
+
// --------------------------------------------------------------------------------
|
|
34
|
+
// Rule Definition
|
|
35
|
+
// --------------------------------------------------------------------------------
|
|
36
|
+
export default {
|
|
37
|
+
meta: {
|
|
38
|
+
type: 'layout',
|
|
39
|
+
docs: {
|
|
40
|
+
description: 'Enforce consistent heading style',
|
|
41
|
+
url: URL_RULE_DOCS('consistent-heading-style'),
|
|
42
|
+
recommended: false,
|
|
43
|
+
stylistic: true,
|
|
44
|
+
},
|
|
45
|
+
fixable: 'code',
|
|
46
|
+
hasSuggestions: true,
|
|
47
|
+
schema: [
|
|
48
|
+
{
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
style: {
|
|
52
|
+
enum: HEADING_STYLE,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
additionalProperties: false,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
defaultOptions: [
|
|
59
|
+
{
|
|
60
|
+
style: 'consistent',
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
messages: {
|
|
64
|
+
style: 'Heading style should be `{{ style }}`.',
|
|
65
|
+
suggestAtxToSetext: 'Replace ATX heading with a Setext heading.',
|
|
66
|
+
suggestAtxClosedToSetext: 'Replace ATX closed heading with a Setext heading.',
|
|
67
|
+
},
|
|
68
|
+
language: 'markdown',
|
|
69
|
+
dialects: ['commonmark', 'gfm'],
|
|
70
|
+
},
|
|
71
|
+
create(context) {
|
|
72
|
+
const { sourceCode } = context;
|
|
73
|
+
const [{ style }] = context.options;
|
|
74
|
+
let headingStyle = style === 'consistent' ? null : style;
|
|
75
|
+
let currentHeadingStyle = null;
|
|
76
|
+
let expectedHeadingStyle = null;
|
|
77
|
+
function reportStyle(node, fix = null, ...suggest) {
|
|
78
|
+
context.report({
|
|
79
|
+
node,
|
|
80
|
+
messageId: 'style',
|
|
81
|
+
data: {
|
|
82
|
+
style: expectedHeadingStyle,
|
|
83
|
+
},
|
|
84
|
+
fix,
|
|
85
|
+
suggest,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
// The `heading` selector is more general, so it is visited before the other `heading[xxx]` selectors.
|
|
90
|
+
heading(node) {
|
|
91
|
+
const { start, end } = sourceCode.getLoc(node);
|
|
92
|
+
if (start.line !== end.line /* Multiline Heading */) {
|
|
93
|
+
currentHeadingStyle = 'setext';
|
|
94
|
+
}
|
|
95
|
+
else if (trailingAtxHeadingHashRegex.test(sourceCode.getText(node))) {
|
|
96
|
+
currentHeadingStyle = 'atx-closed';
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
currentHeadingStyle = 'atx';
|
|
100
|
+
}
|
|
101
|
+
if (headingStyle === null) {
|
|
102
|
+
headingStyle = currentHeadingStyle;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
[`heading[depth<=${SETEXT_MAX_DEPTH}]`]() {
|
|
106
|
+
if (headingStyle === 'setext-with-atx' ||
|
|
107
|
+
headingStyle === 'setext-with-atx-closed') {
|
|
108
|
+
expectedHeadingStyle = 'setext';
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
expectedHeadingStyle = headingStyle;
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
[`heading[depth>${SETEXT_MAX_DEPTH}]`]() {
|
|
115
|
+
if (headingStyle === 'setext-with-atx') {
|
|
116
|
+
expectedHeadingStyle = 'atx';
|
|
117
|
+
}
|
|
118
|
+
else if (headingStyle === 'setext-with-atx-closed') {
|
|
119
|
+
expectedHeadingStyle = 'atx-closed';
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
expectedHeadingStyle = headingStyle;
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
'heading:exit'(node) {
|
|
126
|
+
if (currentHeadingStyle === expectedHeadingStyle) {
|
|
127
|
+
// Early return if the current heading style matches the expected heading style.
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
/*
|
|
131
|
+
* Possible fix combinations include:
|
|
132
|
+
*
|
|
133
|
+
* - autofix: 🔧
|
|
134
|
+
* - suggestion: 💡
|
|
135
|
+
* - no fix: ❌
|
|
136
|
+
*
|
|
137
|
+
* 1. Converting `atx` to `atx-closed`.
|
|
138
|
+
* 1-1. If `atx` is empty, it can be converted to `atx-closed`. (🔧)
|
|
139
|
+
* 1-2. If `atx` is not empty, it can be converted to `atx-closed`. (🔧)
|
|
140
|
+
* 2. Converting `atx` to `setext`.
|
|
141
|
+
* 2-1. If `atx` is empty, it cannot be converted to `setext`. (❌)
|
|
142
|
+
* 2-2. If `atx` is not empty:
|
|
143
|
+
* 2-2-1. If its depth is 1 or 2, conversion can be offered as a suggestion. (💡)
|
|
144
|
+
* Some edge cases are unsafe: `# > Heading`, `# - Heading`, and `# 1. Heading`.
|
|
145
|
+
* 2-2-2. If its depth is greater than 2, it cannot be converted to `setext`. (❌)
|
|
146
|
+
* 3. Converting `atx-closed` to `atx`.
|
|
147
|
+
* 3-1. If `atx-closed` is empty, it can be converted to `atx`. (🔧)
|
|
148
|
+
* 3-2. If `atx-closed` is not empty, it can be converted to `atx`. (🔧)
|
|
149
|
+
* 4. Converting `atx-closed` to `setext`.
|
|
150
|
+
* 4-1. If `atx-closed` is empty, it cannot be converted to `setext`. (❌)
|
|
151
|
+
* 4-2. If `atx-closed` is not empty:
|
|
152
|
+
* 4-2-1. If its depth is 1 or 2, conversion can be offered as a suggestion. (💡)
|
|
153
|
+
* Some edge cases are unsafe: `# > Heading #`, `# - Heading #`, and `# 1. Heading #`.
|
|
154
|
+
* 4-2-2. If its depth is greater than 2, it cannot be converted to `setext`. (❌)
|
|
155
|
+
* 5. Converting `setext` to `atx`.
|
|
156
|
+
* 5-1. Setext headings cannot be empty (https://spec.commonmark.org/0.31.2/#example-97)
|
|
157
|
+
* 5-2. If `setext` is not empty:
|
|
158
|
+
* 5-2-1. If it is single-line, it can be converted to `atx`. (🔧)
|
|
159
|
+
* 5-2-2. If it is multiline, it cannot be converted to `atx`. (❌)
|
|
160
|
+
* 6. Converting `setext` to `atx-closed`.
|
|
161
|
+
* 6-1. Setext headings cannot be empty (https://spec.commonmark.org/0.31.2/#example-97)
|
|
162
|
+
* 6-2. If `setext` is not empty:
|
|
163
|
+
* 6-2-1. If it is single-line, it can be converted to `atx-closed`. (🔧)
|
|
164
|
+
* 6-2-2. If it is multiline, it cannot be converted to `atx-closed`. (❌)
|
|
165
|
+
*/
|
|
166
|
+
const [nodeStartOffset, nodeEndOffset] = sourceCode.getRange(node);
|
|
167
|
+
// The final style checks are exhaustive after matching styles return early.
|
|
168
|
+
if (currentHeadingStyle === 'atx') {
|
|
169
|
+
if (expectedHeadingStyle === 'atx-closed') {
|
|
170
|
+
if (node.children.length === 0) {
|
|
171
|
+
reportStyle(node, function* fix(fixer) {
|
|
172
|
+
if (nodeStartOffset + node.depth === nodeEndOffset) {
|
|
173
|
+
yield fixer.insertTextAfter(node, ' ');
|
|
174
|
+
}
|
|
175
|
+
yield fixer.insertTextAfter(node, '#'.repeat(node.depth));
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
reportStyle(node, function* fix(fixer) {
|
|
180
|
+
const [, lastChildNodeEndOffset] = sourceCode.getRange(node.children[node.children.length - 1]);
|
|
181
|
+
if (lastChildNodeEndOffset === nodeEndOffset) {
|
|
182
|
+
yield fixer.insertTextAfter(node, ' ');
|
|
183
|
+
}
|
|
184
|
+
yield fixer.insertTextAfter(node, '#'.repeat(node.depth));
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/* v8 ignore start */
|
|
188
|
+
}
|
|
189
|
+
else if (expectedHeadingStyle === 'setext') {
|
|
190
|
+
/* v8 ignore stop */
|
|
191
|
+
if (node.children.length === 0) {
|
|
192
|
+
// Empty ATX headings cannot be converted to Setext headings,
|
|
193
|
+
// so report the mismatch without a fix.
|
|
194
|
+
reportStyle(node);
|
|
195
|
+
}
|
|
196
|
+
else if (node.depth <= SETEXT_MAX_DEPTH) {
|
|
197
|
+
reportStyle(node, null, {
|
|
198
|
+
messageId: 'suggestAtxToSetext',
|
|
199
|
+
*fix(fixer) {
|
|
200
|
+
const [firstChildNodeStartOffset] = sourceCode.getRange(node.children[0]);
|
|
201
|
+
const [, lastChildNodeEndOffset] = sourceCode.getRange(node.children[node.children.length - 1]);
|
|
202
|
+
yield fixer.removeRange([nodeStartOffset, firstChildNodeStartOffset]);
|
|
203
|
+
yield fixer.replaceTextRange([lastChildNodeEndOffset, nodeEndOffset], `\n${getSetextMarker(node.depth).repeat(lastChildNodeEndOffset - firstChildNodeStartOffset)}`);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
reportStyle(node);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else if (currentHeadingStyle === 'atx-closed') {
|
|
213
|
+
if (expectedHeadingStyle === 'atx') {
|
|
214
|
+
if (node.children.length === 0) {
|
|
215
|
+
reportStyle(node, fixer => fixer.removeRange([nodeStartOffset + node.depth, nodeEndOffset]));
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
reportStyle(node, fixer => {
|
|
219
|
+
const [, lastChildNodeEndOffset] = sourceCode.getRange(node.children[node.children.length - 1]);
|
|
220
|
+
return fixer.removeRange([lastChildNodeEndOffset, nodeEndOffset]);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/* v8 ignore start */
|
|
224
|
+
}
|
|
225
|
+
else if (expectedHeadingStyle === 'setext') {
|
|
226
|
+
/* v8 ignore stop */
|
|
227
|
+
if (node.children.length === 0) {
|
|
228
|
+
// Empty ATX Closed headings cannot be converted to Setext headings,
|
|
229
|
+
// so report the mismatch without a fix.
|
|
230
|
+
reportStyle(node);
|
|
231
|
+
}
|
|
232
|
+
else if (node.depth <= SETEXT_MAX_DEPTH) {
|
|
233
|
+
reportStyle(node, null, {
|
|
234
|
+
messageId: 'suggestAtxClosedToSetext',
|
|
235
|
+
*fix(fixer) {
|
|
236
|
+
const [firstChildNodeStartOffset] = sourceCode.getRange(node.children[0]);
|
|
237
|
+
const [, lastChildNodeEndOffset] = sourceCode.getRange(node.children[node.children.length - 1]);
|
|
238
|
+
yield fixer.removeRange([nodeStartOffset, firstChildNodeStartOffset]);
|
|
239
|
+
yield fixer.replaceTextRange([lastChildNodeEndOffset, nodeEndOffset], `\n${getSetextMarker(node.depth).repeat(lastChildNodeEndOffset - firstChildNodeStartOffset)}`);
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
reportStyle(node);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/* v8 ignore start */
|
|
248
|
+
}
|
|
249
|
+
else if (currentHeadingStyle === 'setext') {
|
|
250
|
+
/* v8 ignore stop */
|
|
251
|
+
const firstChildNode = node.children[0];
|
|
252
|
+
const lastChildNode = node.children[node.children.length - 1];
|
|
253
|
+
const { start } = sourceCode.getLoc(firstChildNode);
|
|
254
|
+
const { end } = sourceCode.getLoc(lastChildNode);
|
|
255
|
+
if (expectedHeadingStyle === 'atx') {
|
|
256
|
+
if (start.line === end.line /* Singleline Heading */) {
|
|
257
|
+
reportStyle(node, function* fix(fixer) {
|
|
258
|
+
const [lastChildNodeStartOffset, lastChildNodeEndOffset] = sourceCode.getRange(lastChildNode);
|
|
259
|
+
// Prevent trailing hashes from becoming an ATX closing sequence.
|
|
260
|
+
const match = trailingAtxHeadingHashRegex.exec(sourceCode.getText(lastChildNode));
|
|
261
|
+
if (match) {
|
|
262
|
+
yield fixer.insertTextBeforeRange([lastChildNodeStartOffset + match.index + 1, lastChildNodeEndOffset], '\\');
|
|
263
|
+
}
|
|
264
|
+
yield fixer.insertTextBefore(firstChildNode, `${'#'.repeat(node.depth)} `);
|
|
265
|
+
yield fixer.removeRange([lastChildNodeEndOffset, nodeEndOffset]);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
else /* Multiline Heading */ {
|
|
269
|
+
reportStyle(node);
|
|
270
|
+
}
|
|
271
|
+
/* v8 ignore start */
|
|
272
|
+
}
|
|
273
|
+
else if (expectedHeadingStyle === 'atx-closed') {
|
|
274
|
+
/* v8 ignore stop */
|
|
275
|
+
if (start.line === end.line /* Singleline Heading */) {
|
|
276
|
+
reportStyle(node, function* fix(fixer) {
|
|
277
|
+
const [, lastChildNodeEndOffset] = sourceCode.getRange(lastChildNode);
|
|
278
|
+
yield fixer.insertTextBefore(firstChildNode, `${'#'.repeat(node.depth)} `);
|
|
279
|
+
yield fixer.replaceTextRange([lastChildNodeEndOffset, nodeEndOffset], ` ${'#'.repeat(node.depth)}`);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
else /* Multiline Heading */ {
|
|
283
|
+
reportStyle(node);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
},
|
|
290
|
+
};
|
package/build/rules/index.d.ts
CHANGED
|
@@ -566,6 +566,49 @@ declare const _default: {
|
|
|
566
566
|
emphasis(node: import("mdast").Emphasis): void;
|
|
567
567
|
};
|
|
568
568
|
};
|
|
569
|
+
'consistent-heading-style': {
|
|
570
|
+
readonly meta: {
|
|
571
|
+
readonly type: "layout";
|
|
572
|
+
readonly docs: {
|
|
573
|
+
readonly description: "Enforce consistent heading style";
|
|
574
|
+
readonly url: string;
|
|
575
|
+
readonly recommended: false;
|
|
576
|
+
readonly stylistic: true;
|
|
577
|
+
};
|
|
578
|
+
readonly fixable: "code";
|
|
579
|
+
readonly hasSuggestions: true;
|
|
580
|
+
readonly schema: [{
|
|
581
|
+
readonly type: "object";
|
|
582
|
+
readonly properties: {
|
|
583
|
+
readonly style: {
|
|
584
|
+
readonly enum: readonly ["consistent", "atx", "atx-closed", "setext", "setext-with-atx", "setext-with-atx-closed"];
|
|
585
|
+
};
|
|
586
|
+
};
|
|
587
|
+
readonly additionalProperties: false;
|
|
588
|
+
}];
|
|
589
|
+
readonly defaultOptions: [{
|
|
590
|
+
readonly style: "consistent";
|
|
591
|
+
}];
|
|
592
|
+
readonly messages: {
|
|
593
|
+
readonly style: "Heading style should be `{{ style }}`.";
|
|
594
|
+
readonly suggestAtxToSetext: "Replace ATX heading with a Setext heading.";
|
|
595
|
+
readonly suggestAtxClosedToSetext: "Replace ATX closed heading with a Setext heading.";
|
|
596
|
+
};
|
|
597
|
+
readonly language: "markdown";
|
|
598
|
+
readonly dialects: ["commonmark", "gfm"];
|
|
599
|
+
};
|
|
600
|
+
readonly create: (context: import("../core/types.js").RuleContext<{
|
|
601
|
+
RuleOptions: [{
|
|
602
|
+
style: "consistent" | "atx" | "atx-closed" | "setext" | "setext-with-atx" | "setext-with-atx-closed";
|
|
603
|
+
}];
|
|
604
|
+
MessageIds: "style" | "suggestAtxToSetext" | "suggestAtxClosedToSetext";
|
|
605
|
+
}>) => {
|
|
606
|
+
heading(node: import("mdast").Heading): void;
|
|
607
|
+
"heading[depth<=2]"(): void;
|
|
608
|
+
"heading[depth>2]"(): void;
|
|
609
|
+
'heading:exit'(node: import("mdast").Heading): void;
|
|
610
|
+
};
|
|
611
|
+
};
|
|
569
612
|
'consistent-inline-code-style': {
|
|
570
613
|
readonly meta: {
|
|
571
614
|
readonly type: "layout";
|
|
@@ -694,7 +737,7 @@ declare const _default: {
|
|
|
694
737
|
};
|
|
695
738
|
readonly create: (context: import("../core/types.js").RuleContext<{
|
|
696
739
|
RuleOptions: [{
|
|
697
|
-
style: "consistent" | "sublist" | ("*" | "
|
|
740
|
+
style: "consistent" | "sublist" | ("*" | "-" | "+");
|
|
698
741
|
}];
|
|
699
742
|
MessageIds: "style";
|
|
700
743
|
}>) => {
|
|
@@ -1139,6 +1182,12 @@ declare const _default: {
|
|
|
1139
1182
|
readonly skipInlineCode: {
|
|
1140
1183
|
readonly type: "boolean";
|
|
1141
1184
|
};
|
|
1185
|
+
readonly skipMath: {
|
|
1186
|
+
readonly type: "boolean";
|
|
1187
|
+
};
|
|
1188
|
+
readonly skipInlineMath: {
|
|
1189
|
+
readonly type: "boolean";
|
|
1190
|
+
};
|
|
1142
1191
|
};
|
|
1143
1192
|
readonly additionalProperties: false;
|
|
1144
1193
|
}];
|
|
@@ -1146,6 +1195,8 @@ declare const _default: {
|
|
|
1146
1195
|
readonly allow: [];
|
|
1147
1196
|
readonly skipCode: true;
|
|
1148
1197
|
readonly skipInlineCode: true;
|
|
1198
|
+
readonly skipMath: true;
|
|
1199
|
+
readonly skipInlineMath: true;
|
|
1149
1200
|
}];
|
|
1150
1201
|
readonly messages: {
|
|
1151
1202
|
readonly noIrregularWhitespace: "Irregular whitespace `{{ irregularWhitespace }}` is not allowed.";
|
|
@@ -1158,11 +1209,15 @@ declare const _default: {
|
|
|
1158
1209
|
allow: string[];
|
|
1159
1210
|
skipCode: boolean | string[];
|
|
1160
1211
|
skipInlineCode: boolean;
|
|
1212
|
+
skipMath: boolean;
|
|
1213
|
+
skipInlineMath: boolean;
|
|
1161
1214
|
}];
|
|
1162
1215
|
MessageIds: "noIrregularWhitespace";
|
|
1163
1216
|
}>) => {
|
|
1164
1217
|
code(node: import("mdast").Code): void;
|
|
1165
1218
|
inlineCode(node: import("mdast").InlineCode): void;
|
|
1219
|
+
math(node: import("mdast-util-math").Math): void;
|
|
1220
|
+
inlineMath(node: import("mdast-util-math").InlineMath): void;
|
|
1166
1221
|
'root:exit'(): void;
|
|
1167
1222
|
};
|
|
1168
1223
|
};
|
package/build/rules/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import codeLangShorthand from './code-lang-shorthand.js';
|
|
|
7
7
|
import consistentCodeStyle from './consistent-code-style.js';
|
|
8
8
|
import consistentDeleteStyle from './consistent-delete-style.js';
|
|
9
9
|
import consistentEmphasisStyle from './consistent-emphasis-style.js';
|
|
10
|
+
import consistentHeadingStyle from './consistent-heading-style.js';
|
|
10
11
|
import consistentInlineCodeStyle from './consistent-inline-code-style.js';
|
|
11
12
|
import consistentStrongStyle from './consistent-strong-style.js';
|
|
12
13
|
import consistentThematicBreakStyle from './consistent-thematic-break-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-heading-style': consistentHeadingStyle,
|
|
38
40
|
'consistent-inline-code-style': consistentInlineCodeStyle,
|
|
39
41
|
'consistent-strong-style': consistentStrongStyle,
|
|
40
42
|
'consistent-thematic-break-style': consistentThematicBreakStyle,
|
|
@@ -24,6 +24,16 @@ type RuleOptions = [
|
|
|
24
24
|
* @default true
|
|
25
25
|
*/
|
|
26
26
|
skipInlineCode: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* `true` allows irregular whitespaces in all math blocks.
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
skipMath: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* `true` allows irregular whitespaces in all inline math.
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
skipInlineMath: boolean;
|
|
27
37
|
}
|
|
28
38
|
];
|
|
29
39
|
declare const _default: {
|
|
@@ -59,6 +69,12 @@ declare const _default: {
|
|
|
59
69
|
readonly skipInlineCode: {
|
|
60
70
|
readonly type: "boolean";
|
|
61
71
|
};
|
|
72
|
+
readonly skipMath: {
|
|
73
|
+
readonly type: "boolean";
|
|
74
|
+
};
|
|
75
|
+
readonly skipInlineMath: {
|
|
76
|
+
readonly type: "boolean";
|
|
77
|
+
};
|
|
62
78
|
};
|
|
63
79
|
readonly additionalProperties: false;
|
|
64
80
|
}];
|
|
@@ -66,6 +82,8 @@ declare const _default: {
|
|
|
66
82
|
readonly allow: [];
|
|
67
83
|
readonly skipCode: true;
|
|
68
84
|
readonly skipInlineCode: true;
|
|
85
|
+
readonly skipMath: true;
|
|
86
|
+
readonly skipInlineMath: true;
|
|
69
87
|
}];
|
|
70
88
|
readonly messages: {
|
|
71
89
|
readonly noIrregularWhitespace: "Irregular whitespace `{{ irregularWhitespace }}` is not allowed.";
|
|
@@ -79,6 +97,8 @@ declare const _default: {
|
|
|
79
97
|
}>) => {
|
|
80
98
|
code(node: import("mdast").Code): void;
|
|
81
99
|
inlineCode(node: import("mdast").InlineCode): void;
|
|
100
|
+
math(node: import("mdast-util-math").Math): void;
|
|
101
|
+
inlineMath(node: import("mdast-util-math").InlineMath): void;
|
|
82
102
|
'root:exit'(): void;
|
|
83
103
|
};
|
|
84
104
|
};
|
|
@@ -51,6 +51,12 @@ export default {
|
|
|
51
51
|
skipInlineCode: {
|
|
52
52
|
type: 'boolean',
|
|
53
53
|
},
|
|
54
|
+
skipMath: {
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
},
|
|
57
|
+
skipInlineMath: {
|
|
58
|
+
type: 'boolean',
|
|
59
|
+
},
|
|
54
60
|
},
|
|
55
61
|
additionalProperties: false,
|
|
56
62
|
},
|
|
@@ -60,6 +66,8 @@ export default {
|
|
|
60
66
|
allow: [],
|
|
61
67
|
skipCode: true,
|
|
62
68
|
skipInlineCode: true,
|
|
69
|
+
skipMath: true,
|
|
70
|
+
skipInlineMath: true,
|
|
63
71
|
},
|
|
64
72
|
],
|
|
65
73
|
messages: {
|
|
@@ -70,7 +78,7 @@ export default {
|
|
|
70
78
|
},
|
|
71
79
|
create(context) {
|
|
72
80
|
const { sourceCode } = context;
|
|
73
|
-
const [{ allow, skipCode, skipInlineCode }] = context.options;
|
|
81
|
+
const [{ allow, skipCode, skipInlineCode, skipMath, skipInlineMath }] = context.options;
|
|
74
82
|
const skipRanges = new SkipRanges();
|
|
75
83
|
return {
|
|
76
84
|
code(node) {
|
|
@@ -81,6 +89,14 @@ export default {
|
|
|
81
89
|
if (skipInlineCode)
|
|
82
90
|
skipRanges.push(sourceCode.getRange(node)); // Store range information of `InlineCode`.
|
|
83
91
|
},
|
|
92
|
+
math(node) {
|
|
93
|
+
if (skipMath)
|
|
94
|
+
skipRanges.push(sourceCode.getRange(node)); // Store range information of `Math`.
|
|
95
|
+
},
|
|
96
|
+
inlineMath(node) {
|
|
97
|
+
if (skipInlineMath)
|
|
98
|
+
skipRanges.push(sourceCode.getRange(node)); // Store range information of `InlineMath`.
|
|
99
|
+
},
|
|
84
100
|
'root:exit'() {
|
|
85
101
|
const matches = sourceCode.text.matchAll(irregularWhitespaceRegex);
|
|
86
102
|
for (const match of matches) {
|
|
@@ -38,7 +38,7 @@ function hasTrailingSlash(url) {
|
|
|
38
38
|
* -------------------------------------------------^
|
|
39
39
|
*/
|
|
40
40
|
if (hash) {
|
|
41
|
-
urlWithoutSearchAndHash = urlWithoutSearchAndHash.slice(0, url.indexOf(
|
|
41
|
+
urlWithoutSearchAndHash = urlWithoutSearchAndHash.slice(0, url.indexOf('#'));
|
|
42
42
|
}
|
|
43
43
|
else if (urlWithoutSearchAndHash.endsWith('#')) {
|
|
44
44
|
urlWithoutSearchAndHash = urlWithoutSearchAndHash.slice(0, -1);
|
|
@@ -56,7 +56,7 @@ function hasTrailingSlash(url) {
|
|
|
56
56
|
* ------------------------------------^
|
|
57
57
|
*/
|
|
58
58
|
if (search) {
|
|
59
|
-
urlWithoutSearchAndHash = urlWithoutSearchAndHash.slice(0, url.indexOf(
|
|
59
|
+
urlWithoutSearchAndHash = urlWithoutSearchAndHash.slice(0, url.indexOf('?'));
|
|
60
60
|
}
|
|
61
61
|
else if (urlWithoutSearchAndHash.endsWith('?')) {
|
|
62
62
|
urlWithoutSearchAndHash = urlWithoutSearchAndHash.slice(0, -1);
|