eslint-markdown 0.18.1 → 0.19.1
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 +47 -31
- 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/core/constants.d.ts +5 -0
- package/build/core/constants.js +5 -0
- package/build/core/utils/html.d.ts +1 -1
- package/build/rules/consistent-code-style.js +32 -19
- package/build/rules/consistent-ordered-list-style.d.ts +62 -0
- package/build/rules/consistent-ordered-list-style.js +108 -0
- package/build/rules/en-capitalization.js +1 -1
- package/build/rules/index.d.ts +50 -0
- package/build/rules/index.js +2 -0
- package/build/rules/no-control-character.d.ts +20 -0
- package/build/rules/no-control-character.js +17 -1
- package/build/rules/no-trailing-heading-punctuation.js +3 -1
- package/package.json +3 -3
- package/build/rules/endline-punctuation.d.ts +0 -1
- package/build/rules/endline-punctuation.js +0 -2
- package/build/rules/glossary.d.ts +0 -1
- package/build/rules/glossary.js +0 -7
- package/build/rules/no-unmatched-pair.d.ts +0 -1
- package/build/rules/no-unmatched-pair.js +0 -2
package/README.md
CHANGED
|
@@ -1,21 +1,53 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- eslint-disable markdown/no-html -->
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-

|
|
3
|
+
<p align="center">
|
|
4
|
+
<img alt="ESLint Markdown Logo" src="https://eslint-markdown.lumir.page/logo.svg" width="150">
|
|
5
|
+
</p>
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
<h1 align="center">ESLint Markdown</h1>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://eslint-markdown.lumir.page">Documentation</a> |
|
|
11
|
+
<a href="https://eslint-markdown.lumir.page/docs/rules/">Rules</a> |
|
|
12
|
+
<a href="https://eslint-markdown.lumir.page/docs/get-started/configurations">Configurations</a> |
|
|
13
|
+
<a href="https://eslint-markdown.lumir.page/docs/get-started/migration">Migration</a> |
|
|
14
|
+
<a href="https://eslint-markdown.lumir.page/docs/packages">Packages</a> |
|
|
15
|
+
<a href="https://github.com/eslint-markdown/eslint-markdown">Repository</a> |
|
|
16
|
+
<a href="https://github.com/eslint-markdown/eslint-markdown/discussions">Discussions</a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
Community-Maintained ESLint Tools, Rules, and Configurations for High-Quality and Performant Markdown Linting.
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<a href="https://github.com/eslint-markdown/eslint-markdown/actions/workflows/ci.yml"><img alt="ci" src="https://img.shields.io/github/actions/workflow/status/eslint-markdown/eslint-markdown/ci.yml?label=ci&color=341bab&labelColor=1a1a1a&logo=github"></a>
|
|
25
|
+
<a href="https://github.com/eslint-markdown/eslint-markdown/actions/workflows/test.yml"><img alt="test" src="https://img.shields.io/github/actions/workflow/status/eslint-markdown/eslint-markdown/test.yml?label=test&color=341bab&labelColor=1a1a1a&logo=github"></a>
|
|
26
|
+
<a href="https://codecov.io/gh/eslint-markdown/eslint-markdown"><img alt="codecov" src="https://img.shields.io/codecov/c/gh/eslint-markdown/eslint-markdown?token=LJMUst9eR3&label=Codecov&color=341bab&labelColor=1a1a1a&logo=codecov"></a>
|
|
27
|
+
<img alt="Node Current" src="https://img.shields.io/node/v/eslint-markdown?color=341bab&labelColor=1a1a1a&logo=node.js">
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<!-- eslint-enable markdown/no-html -->
|
|
11
31
|
|
|
12
32
|
> [!IMPORTANT]
|
|
13
33
|
>
|
|
14
34
|
> If you like this package, please give it a star on [GitHub](https://github.com/eslint-markdown/eslint-markdown)!⭐<br/>
|
|
15
35
|
> Your support helps us improve and maintain the project.
|
|
16
36
|
|
|
37
|
+
## `eslint-markdown`
|
|
38
|
+
|
|
39
|
+
[](https://www.npmjs.com/package/eslint-markdown)
|
|
40
|
+
[](https://www.npmjs.com/package/eslint-markdown)
|
|
41
|
+
[](https://www.npmjs.com/package/eslint-markdown)
|
|
42
|
+
|
|
17
43
|
Lint your Markdown with ESLint. Additional rules for use with `@eslint/markdown`.🛠️
|
|
18
44
|
|
|
45
|
+
> [!IMPORTANT]
|
|
46
|
+
>
|
|
47
|
+
> **Compatibility**: This [`eslint-markdown`](https://github.com/eslint-markdown/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).
|
|
48
|
+
>
|
|
49
|
+
> So, we **highly recommend** using the `eslint-markdown` plugin alongside ESLint's built-in Markdown support, `@eslint/markdown`.
|
|
50
|
+
|
|
19
51
|
## Documentation
|
|
20
52
|
|
|
21
53
|
For full documentation, see the [official documentation of the `eslint-markdown`](https://eslint-markdown.lumir.page).
|
|
@@ -23,30 +55,14 @@ For full documentation, see the [official documentation of the `eslint-markdown`
|
|
|
23
55
|
- [Installation](https://eslint-markdown.lumir.page/docs/get-started/installation)
|
|
24
56
|
- [Configurations](https://eslint-markdown.lumir.page/docs/get-started/configurations)
|
|
25
57
|
- [Migration Guide](https://eslint-markdown.lumir.page/docs/get-started/migration)
|
|
58
|
+
- [Migrating from `markdownlint`](https://eslint-markdown.lumir.page/docs/get-started/migration#migrating-from-markdownlint)
|
|
59
|
+
- [Migrating from `remark-lint`](https://eslint-markdown.lumir.page/docs/get-started/migration#migrating-from-remark-lint)
|
|
60
|
+
- [Migrating from `textlint`](https://eslint-markdown.lumir.page/docs/get-started/migration#migrating-from-textlint)
|
|
26
61
|
- [Rules](https://eslint-markdown.lumir.page/docs/rules)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
This [`eslint-markdown`](https://github.com/eslint-markdown/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
|
-
|
|
34
|
-
## Code of Conduct
|
|
35
|
-
|
|
36
|
-
See [Code of Conduct](https://github.com/eslint-markdown/eslint-markdown/blob/main/CODE_OF_CONDUCT.md).
|
|
37
|
-
|
|
38
|
-
## Change Log
|
|
39
|
-
|
|
40
|
-
See [Change Log](https://github.com/eslint-markdown/eslint-markdown/blob/main/CHANGELOG.md).
|
|
41
|
-
|
|
42
|
-
## Versioning
|
|
43
|
-
|
|
44
|
-
See [Versioning](https://eslint-markdown.lumir.page/docs/get-started/versioning).
|
|
45
|
-
|
|
46
|
-
## Security
|
|
47
|
-
|
|
48
|
-
See [Security](https://github.com/eslint-markdown/eslint-markdown/blob/main/SECURITY.md).
|
|
62
|
+
- [Packages](https://eslint-markdown.lumir.page/docs/packages)
|
|
63
|
+
- [`eslint-markdown`](https://eslint-markdown.lumir.page/docs/packages/eslint-markdown)
|
|
64
|
+
- [`eslint-lumir`](https://eslint-markdown.lumir.page/docs/packages/eslint-lumir)
|
|
49
65
|
|
|
50
66
|
## License
|
|
51
67
|
|
|
52
|
-
[MIT](https://github.com/eslint-markdown/eslint-markdown/blob/main/LICENSE.md)
|
|
68
|
+
[MIT](https://github.com/eslint-markdown/eslint-markdown/blob/main/packages/eslint-markdown/LICENSE.md)
|
package/build/configs/all.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export default function all(plugin: ESLint.Plugin): {
|
|
|
25
25
|
readonly 'md/consistent-emphasis-style': "error";
|
|
26
26
|
readonly 'md/consistent-heading-style': "error";
|
|
27
27
|
readonly 'md/consistent-inline-code-style': "error";
|
|
28
|
+
readonly 'md/consistent-ordered-list-style': "error";
|
|
28
29
|
readonly 'md/consistent-strong-style': "error";
|
|
29
30
|
readonly 'md/consistent-thematic-break-style': "error";
|
|
30
31
|
readonly 'md/consistent-unordered-list-style': "error";
|
package/build/configs/all.js
CHANGED
|
@@ -33,6 +33,7 @@ export default function all(plugin) {
|
|
|
33
33
|
'md/consistent-emphasis-style': 'error',
|
|
34
34
|
'md/consistent-heading-style': 'error',
|
|
35
35
|
'md/consistent-inline-code-style': 'error',
|
|
36
|
+
'md/consistent-ordered-list-style': 'error',
|
|
36
37
|
'md/consistent-strong-style': 'error',
|
|
37
38
|
'md/consistent-thematic-break-style': 'error',
|
|
38
39
|
'md/consistent-unordered-list-style': 'error',
|
|
@@ -21,6 +21,7 @@ export default function stylistic(plugin: ESLint.Plugin): {
|
|
|
21
21
|
readonly 'md/consistent-emphasis-style': "error";
|
|
22
22
|
readonly 'md/consistent-heading-style': "error";
|
|
23
23
|
readonly 'md/consistent-inline-code-style': "error";
|
|
24
|
+
readonly 'md/consistent-ordered-list-style': "error";
|
|
24
25
|
readonly 'md/consistent-strong-style': "error";
|
|
25
26
|
readonly 'md/consistent-thematic-break-style': "error";
|
|
26
27
|
readonly 'md/consistent-unordered-list-style': "error";
|
|
@@ -29,6 +29,7 @@ export default function stylistic(plugin) {
|
|
|
29
29
|
'md/consistent-emphasis-style': 'error',
|
|
30
30
|
'md/consistent-heading-style': 'error',
|
|
31
31
|
'md/consistent-inline-code-style': 'error',
|
|
32
|
+
'md/consistent-ordered-list-style': 'error',
|
|
32
33
|
'md/consistent-strong-style': 'error',
|
|
33
34
|
'md/consistent-thematic-break-style': 'error',
|
|
34
35
|
'md/consistent-unordered-list-style': 'error',
|
|
@@ -30,6 +30,11 @@ export declare const punctuation: readonly [".", ",", ";", ":", "!", "。", ",
|
|
|
30
30
|
* @see https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-no-heading-punctuation#parameters
|
|
31
31
|
*/
|
|
32
32
|
export declare const punctuationWithQuestionMark: readonly [".", ",", ";", ":", "!", "?", "。", ",", ";", ":", "!", "?"];
|
|
33
|
+
/**
|
|
34
|
+
* Matches a single ASCII punctuation character as defined by CommonMark.
|
|
35
|
+
* @see https://spec.commonmark.org/0.31.2/#ascii-punctuation-character
|
|
36
|
+
*/
|
|
37
|
+
export declare const asciiPunctuationRegex: RegExp;
|
|
33
38
|
/**
|
|
34
39
|
* Regular expression for identifying an odd-length run of backslashes at the end of a string.
|
|
35
40
|
*/
|
package/build/core/constants.js
CHANGED
|
@@ -49,6 +49,11 @@ export const punctuationWithQuestionMark = [
|
|
|
49
49
|
// --------------------------------------------------------------------------------
|
|
50
50
|
// --------------------------------------------------------------------------------
|
|
51
51
|
// #region regex
|
|
52
|
+
/**
|
|
53
|
+
* Matches a single ASCII punctuation character as defined by CommonMark.
|
|
54
|
+
* @see https://spec.commonmark.org/0.31.2/#ascii-punctuation-character
|
|
55
|
+
*/
|
|
56
|
+
export const asciiPunctuationRegex = /^[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]$/;
|
|
52
57
|
/**
|
|
53
58
|
* Regular expression for identifying an odd-length run of backslashes at the end of a string.
|
|
54
59
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview HTML AST utilities.
|
|
3
3
|
*/
|
|
4
|
-
import type
|
|
4
|
+
import { type DefaultTreeAdapterTypes } from 'parse5';
|
|
5
5
|
/**
|
|
6
6
|
* Mimics the behavior of `getElementsByTagName` in the DOM API.
|
|
7
7
|
* @param html The HTML string to parse.
|
|
@@ -103,28 +103,30 @@ export default {
|
|
|
103
103
|
const currentCodeStyle = getCodeStyle(currentCodeFenceChar);
|
|
104
104
|
const nodeStartLineIndex = start.line - 1;
|
|
105
105
|
const nodeEndLineIndex = end.line - 1;
|
|
106
|
+
const nodeStartLineText = lines[nodeStartLineIndex];
|
|
107
|
+
const nodeEndLineText = lines[nodeEndLineIndex];
|
|
108
|
+
const leadingCodeFenceLoc = {
|
|
109
|
+
start,
|
|
110
|
+
end: {
|
|
111
|
+
line: start.line,
|
|
112
|
+
column: (() => {
|
|
113
|
+
if (currentCodeStyle === 'indent') {
|
|
114
|
+
return nodeStartLineText.length + 1;
|
|
115
|
+
}
|
|
116
|
+
let { column } = start;
|
|
117
|
+
while (nodeStartLineText[column - 1] === currentCodeFenceChar) {
|
|
118
|
+
column++;
|
|
119
|
+
}
|
|
120
|
+
return column;
|
|
121
|
+
})(),
|
|
122
|
+
},
|
|
123
|
+
};
|
|
106
124
|
if (codeStyle === null) {
|
|
107
125
|
codeStyle = currentCodeStyle;
|
|
108
126
|
}
|
|
109
127
|
if (codeStyle !== currentCodeStyle) {
|
|
110
128
|
context.report({
|
|
111
|
-
loc:
|
|
112
|
-
start,
|
|
113
|
-
end: {
|
|
114
|
-
line: start.line,
|
|
115
|
-
column: (() => {
|
|
116
|
-
const nodeStartLineText = lines[nodeStartLineIndex];
|
|
117
|
-
if (currentCodeStyle === 'indent') {
|
|
118
|
-
return nodeStartLineText.length + 1;
|
|
119
|
-
}
|
|
120
|
-
let { column } = start;
|
|
121
|
-
while (nodeStartLineText[column - 1] === currentCodeFenceChar) {
|
|
122
|
-
column++;
|
|
123
|
-
}
|
|
124
|
-
return column;
|
|
125
|
-
})(),
|
|
126
|
-
},
|
|
127
|
-
},
|
|
129
|
+
loc: leadingCodeFenceLoc,
|
|
128
130
|
messageId: 'style',
|
|
129
131
|
data: {
|
|
130
132
|
style: codeStyle,
|
|
@@ -150,7 +152,7 @@ export default {
|
|
|
150
152
|
continue;
|
|
151
153
|
}
|
|
152
154
|
context.report({
|
|
153
|
-
|
|
155
|
+
loc: leadingCodeFenceLoc,
|
|
154
156
|
messageId: 'blankLineAbove',
|
|
155
157
|
data: {
|
|
156
158
|
blankLineAbove,
|
|
@@ -179,7 +181,18 @@ export default {
|
|
|
179
181
|
continue;
|
|
180
182
|
}
|
|
181
183
|
context.report({
|
|
182
|
-
|
|
184
|
+
// Report the last line of the code block, as highlighting only the trailing
|
|
185
|
+
// code fence characters involves many edge cases that cannot be handled reliably.
|
|
186
|
+
loc: {
|
|
187
|
+
start: {
|
|
188
|
+
line: end.line,
|
|
189
|
+
column: 1,
|
|
190
|
+
},
|
|
191
|
+
end: {
|
|
192
|
+
line: end.line,
|
|
193
|
+
column: nodeEndLineText.length + 1,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
183
196
|
messageId: 'blankLineBelow',
|
|
184
197
|
data: {
|
|
185
198
|
blankLineBelow,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Rule to enforce consistent ordered list style.
|
|
3
|
+
* @author Ga eun Lee(tooth-is-silver)
|
|
4
|
+
*/
|
|
5
|
+
import type { ListItem } from 'mdast';
|
|
6
|
+
type OrderedListStyle = (typeof ORDERED_LIST_STYLE)[number];
|
|
7
|
+
/**
|
|
8
|
+
* Options for the `consistent-ordered-list-style` rule.
|
|
9
|
+
*/
|
|
10
|
+
type RuleOptions = [
|
|
11
|
+
{
|
|
12
|
+
/**
|
|
13
|
+
* When `style` is set to `'one-or-ordered'`, the rule allows either the `'one'` or `'ordered'` style based on the first two list item prefixes.
|
|
14
|
+
*
|
|
15
|
+
* When `style` is set to `'one'`, the rule requires every prefix to be `1`.
|
|
16
|
+
*
|
|
17
|
+
* When `style` is set to `'ordered'`, the rule requires prefixes to increase sequentially, starting from `1` or from `0` when the list has two or more items.
|
|
18
|
+
*
|
|
19
|
+
* When `style` is set to `'zero'`, the rule requires every prefix to be `0`.
|
|
20
|
+
* @default 'one-or-ordered'
|
|
21
|
+
*/
|
|
22
|
+
style: 'one-or-ordered' | OrderedListStyle;
|
|
23
|
+
}
|
|
24
|
+
];
|
|
25
|
+
declare const ORDERED_LIST_STYLE: readonly ["one", "ordered", "zero"];
|
|
26
|
+
declare const _default: {
|
|
27
|
+
readonly meta: {
|
|
28
|
+
readonly type: "layout";
|
|
29
|
+
readonly docs: {
|
|
30
|
+
readonly description: "Enforce consistent ordered list style";
|
|
31
|
+
readonly url: string;
|
|
32
|
+
readonly recommended: false;
|
|
33
|
+
readonly stylistic: true;
|
|
34
|
+
};
|
|
35
|
+
readonly schema: [{
|
|
36
|
+
readonly type: "object";
|
|
37
|
+
readonly properties: {
|
|
38
|
+
readonly style: {
|
|
39
|
+
readonly enum: ["one-or-ordered", "one", "ordered", "zero"];
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
readonly additionalProperties: false;
|
|
43
|
+
}];
|
|
44
|
+
readonly defaultOptions: [{
|
|
45
|
+
readonly style: "one-or-ordered";
|
|
46
|
+
}];
|
|
47
|
+
readonly messages: {
|
|
48
|
+
readonly style: "Ordered list style should be `{{ style }}`.";
|
|
49
|
+
};
|
|
50
|
+
readonly language: "markdown";
|
|
51
|
+
readonly dialects: ["commonmark", "gfm"];
|
|
52
|
+
};
|
|
53
|
+
readonly create: (context: import("../core/types.js").RuleContext<{
|
|
54
|
+
RuleOptions: RuleOptions;
|
|
55
|
+
MessageIds: "style";
|
|
56
|
+
}>) => {
|
|
57
|
+
'list[ordered=true]'(): void;
|
|
58
|
+
'list[ordered=true] > listItem'(node: ListItem): void;
|
|
59
|
+
'list[ordered=true]:exit'(): void;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
export default _default;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Rule to enforce consistent ordered list style.
|
|
3
|
+
* @author Ga eun Lee(tooth-is-silver)
|
|
4
|
+
*/
|
|
5
|
+
import { URL_RULE_DOCS } from '../core/constants.js';
|
|
6
|
+
// --------------------------------------------------------------------------------
|
|
7
|
+
// Helper
|
|
8
|
+
// --------------------------------------------------------------------------------
|
|
9
|
+
const ORDERED_LIST_STYLE = ['one', 'ordered', 'zero'];
|
|
10
|
+
/**
|
|
11
|
+
* Ordered list start numbers must be nine digits or less.
|
|
12
|
+
* @see https://spec.commonmark.org/0.31.2/#example-265
|
|
13
|
+
*/
|
|
14
|
+
const orderedListPrefixRegex = /^\d{1,9}/u;
|
|
15
|
+
// --------------------------------------------------------------------------------
|
|
16
|
+
// Rule Definition
|
|
17
|
+
// --------------------------------------------------------------------------------
|
|
18
|
+
export default {
|
|
19
|
+
meta: {
|
|
20
|
+
type: 'layout',
|
|
21
|
+
docs: {
|
|
22
|
+
description: 'Enforce consistent ordered list style',
|
|
23
|
+
url: URL_RULE_DOCS('consistent-ordered-list-style'),
|
|
24
|
+
recommended: false,
|
|
25
|
+
stylistic: true,
|
|
26
|
+
},
|
|
27
|
+
schema: [
|
|
28
|
+
{
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
style: {
|
|
32
|
+
enum: ['one-or-ordered', ...ORDERED_LIST_STYLE],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
additionalProperties: false,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
defaultOptions: [
|
|
39
|
+
{
|
|
40
|
+
style: 'one-or-ordered',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
messages: {
|
|
44
|
+
style: 'Ordered list style should be `{{ style }}`.',
|
|
45
|
+
},
|
|
46
|
+
language: 'markdown',
|
|
47
|
+
dialects: ['commonmark', 'gfm'],
|
|
48
|
+
},
|
|
49
|
+
create(context) {
|
|
50
|
+
const { sourceCode } = context;
|
|
51
|
+
const [{ style }] = context.options;
|
|
52
|
+
const orderedListPrefixesStack = [];
|
|
53
|
+
return {
|
|
54
|
+
'list[ordered=true]'() {
|
|
55
|
+
orderedListPrefixesStack.push([]);
|
|
56
|
+
},
|
|
57
|
+
'list[ordered=true] > listItem'(node) {
|
|
58
|
+
const [nodeStartOffset] = sourceCode.getRange(node);
|
|
59
|
+
// `match` is never `null` because this visitor only runs for
|
|
60
|
+
// ordered list items, whose source text starts with a numeric marker.
|
|
61
|
+
const match = orderedListPrefixRegex.exec(sourceCode.getText(node));
|
|
62
|
+
const orderedListPrefix = match[0];
|
|
63
|
+
orderedListPrefixesStack[orderedListPrefixesStack.length - 1].push({
|
|
64
|
+
startOffset: nodeStartOffset,
|
|
65
|
+
endOffset: nodeStartOffset + orderedListPrefix.length,
|
|
66
|
+
number: Number(orderedListPrefix),
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
'list[ordered=true]:exit'() {
|
|
70
|
+
// Can never be `undefined` because `list[ordered=true]` always pushes to the stack before exiting.
|
|
71
|
+
const orderedListPrefixes = orderedListPrefixesStack.pop();
|
|
72
|
+
const [firstOrderedListPrefix, secondOrderedListPrefix] = orderedListPrefixes;
|
|
73
|
+
const hasMultipleItems = orderedListPrefixes.length > 1;
|
|
74
|
+
let orderedListStyle = style;
|
|
75
|
+
if (orderedListStyle === 'one-or-ordered') {
|
|
76
|
+
orderedListStyle =
|
|
77
|
+
hasMultipleItems &&
|
|
78
|
+
(firstOrderedListPrefix.number === 0 || secondOrderedListPrefix.number !== 1)
|
|
79
|
+
? 'ordered'
|
|
80
|
+
: 'one';
|
|
81
|
+
}
|
|
82
|
+
let expectedOrderedListPrefixNumber = orderedListStyle === 'zero' ||
|
|
83
|
+
(orderedListStyle === 'ordered' &&
|
|
84
|
+
hasMultipleItems &&
|
|
85
|
+
firstOrderedListPrefix.number === 0)
|
|
86
|
+
? 0
|
|
87
|
+
: 1;
|
|
88
|
+
for (const { startOffset, endOffset, number } of orderedListPrefixes) {
|
|
89
|
+
if (number !== expectedOrderedListPrefixNumber) {
|
|
90
|
+
context.report({
|
|
91
|
+
loc: {
|
|
92
|
+
start: sourceCode.getLocFromIndex(startOffset),
|
|
93
|
+
end: sourceCode.getLocFromIndex(endOffset),
|
|
94
|
+
},
|
|
95
|
+
messageId: 'style',
|
|
96
|
+
data: {
|
|
97
|
+
style: orderedListStyle,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (orderedListStyle === 'ordered') {
|
|
102
|
+
expectedOrderedListPrefixNumber++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
};
|
|
@@ -11,7 +11,7 @@ const lowercaseRegex = /^[a-z]/u;
|
|
|
11
11
|
* Traverses the Markdown AST using a DFS pre-order approach to find the first text node.
|
|
12
12
|
* @param node The node to start searching from.
|
|
13
13
|
* @returns The first text node, if present.
|
|
14
|
-
*/ // eslint-disable-next-line
|
|
14
|
+
*/ // eslint-disable-next-line typescript/no-explicit-any -- TODO: This rule is not fully migrated to TypeScript yet.
|
|
15
15
|
function findFirstLeafTextNode(node) {
|
|
16
16
|
// Base case: if this is a text node, return it
|
|
17
17
|
if (node.type === 'text')
|
package/build/rules/index.d.ts
CHANGED
|
@@ -632,6 +632,44 @@ declare const _default: {
|
|
|
632
632
|
inlineCode(node: import("mdast").InlineCode): void;
|
|
633
633
|
};
|
|
634
634
|
};
|
|
635
|
+
'consistent-ordered-list-style': {
|
|
636
|
+
readonly meta: {
|
|
637
|
+
readonly type: "layout";
|
|
638
|
+
readonly docs: {
|
|
639
|
+
readonly description: "Enforce consistent ordered list style";
|
|
640
|
+
readonly url: string;
|
|
641
|
+
readonly recommended: false;
|
|
642
|
+
readonly stylistic: true;
|
|
643
|
+
};
|
|
644
|
+
readonly schema: [{
|
|
645
|
+
readonly type: "object";
|
|
646
|
+
readonly properties: {
|
|
647
|
+
readonly style: {
|
|
648
|
+
readonly enum: ["one-or-ordered", "one", "ordered", "zero"];
|
|
649
|
+
};
|
|
650
|
+
};
|
|
651
|
+
readonly additionalProperties: false;
|
|
652
|
+
}];
|
|
653
|
+
readonly defaultOptions: [{
|
|
654
|
+
readonly style: "one-or-ordered";
|
|
655
|
+
}];
|
|
656
|
+
readonly messages: {
|
|
657
|
+
readonly style: "Ordered list style should be `{{ style }}`.";
|
|
658
|
+
};
|
|
659
|
+
readonly language: "markdown";
|
|
660
|
+
readonly dialects: ["commonmark", "gfm"];
|
|
661
|
+
};
|
|
662
|
+
readonly create: (context: import("../core/types.js").RuleContext<{
|
|
663
|
+
RuleOptions: [{
|
|
664
|
+
style: "one-or-ordered" | ("one" | "ordered" | "zero");
|
|
665
|
+
}];
|
|
666
|
+
MessageIds: "style";
|
|
667
|
+
}>) => {
|
|
668
|
+
'list[ordered=true]'(): void;
|
|
669
|
+
'list[ordered=true] > listItem'(node: import("mdast").ListItem): void;
|
|
670
|
+
'list[ordered=true]:exit'(): void;
|
|
671
|
+
};
|
|
672
|
+
};
|
|
635
673
|
'consistent-strong-style': {
|
|
636
674
|
readonly meta: {
|
|
637
675
|
readonly type: "layout";
|
|
@@ -838,6 +876,12 @@ declare const _default: {
|
|
|
838
876
|
readonly skipInlineCode: {
|
|
839
877
|
readonly type: "boolean";
|
|
840
878
|
};
|
|
879
|
+
readonly skipMath: {
|
|
880
|
+
readonly type: "boolean";
|
|
881
|
+
};
|
|
882
|
+
readonly skipInlineMath: {
|
|
883
|
+
readonly type: "boolean";
|
|
884
|
+
};
|
|
841
885
|
};
|
|
842
886
|
readonly additionalProperties: false;
|
|
843
887
|
}];
|
|
@@ -845,6 +889,8 @@ declare const _default: {
|
|
|
845
889
|
readonly allow: [];
|
|
846
890
|
readonly skipCode: true;
|
|
847
891
|
readonly skipInlineCode: true;
|
|
892
|
+
readonly skipMath: false;
|
|
893
|
+
readonly skipInlineMath: false;
|
|
848
894
|
}];
|
|
849
895
|
readonly messages: {
|
|
850
896
|
readonly noControlCharacter: "Control character `{{ controlCharacter }}` is not allowed.";
|
|
@@ -858,11 +904,15 @@ declare const _default: {
|
|
|
858
904
|
allow: string[];
|
|
859
905
|
skipCode: boolean | string[];
|
|
860
906
|
skipInlineCode: boolean;
|
|
907
|
+
skipMath: boolean;
|
|
908
|
+
skipInlineMath: boolean;
|
|
861
909
|
}];
|
|
862
910
|
MessageIds: "noControlCharacter" | "suggestRemove";
|
|
863
911
|
}>) => {
|
|
864
912
|
code(node: import("mdast").Code): void;
|
|
865
913
|
inlineCode(node: import("mdast").InlineCode): void;
|
|
914
|
+
math(node: import("mdast-util-math").Math): void;
|
|
915
|
+
inlineMath(node: import("mdast-util-math").InlineMath): void;
|
|
866
916
|
'root:exit'(): void;
|
|
867
917
|
};
|
|
868
918
|
};
|
package/build/rules/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import consistentDeleteStyle from './consistent-delete-style.js';
|
|
|
9
9
|
import consistentEmphasisStyle from './consistent-emphasis-style.js';
|
|
10
10
|
import consistentHeadingStyle from './consistent-heading-style.js';
|
|
11
11
|
import consistentInlineCodeStyle from './consistent-inline-code-style.js';
|
|
12
|
+
import consistentOrderedListStyle from './consistent-ordered-list-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';
|
|
@@ -40,6 +41,7 @@ export default {
|
|
|
40
41
|
'consistent-emphasis-style': consistentEmphasisStyle,
|
|
41
42
|
'consistent-heading-style': consistentHeadingStyle,
|
|
42
43
|
'consistent-inline-code-style': consistentInlineCodeStyle,
|
|
44
|
+
'consistent-ordered-list-style': consistentOrderedListStyle,
|
|
43
45
|
'consistent-strong-style': consistentStrongStyle,
|
|
44
46
|
'consistent-thematic-break-style': consistentThematicBreakStyle,
|
|
45
47
|
'consistent-unordered-list-style': consistentUnorderedListStyle,
|
|
@@ -24,6 +24,16 @@ type RuleOptions = [
|
|
|
24
24
|
* @default true
|
|
25
25
|
*/
|
|
26
26
|
skipInlineCode: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* `true` allows control characters in all math blocks.
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
skipMath: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* `true` allows control characters in all inline math.
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
skipInlineMath: boolean;
|
|
27
37
|
}
|
|
28
38
|
];
|
|
29
39
|
type MessageIds = 'noControlCharacter' | 'suggestRemove';
|
|
@@ -61,6 +71,12 @@ declare const _default: {
|
|
|
61
71
|
readonly skipInlineCode: {
|
|
62
72
|
readonly type: "boolean";
|
|
63
73
|
};
|
|
74
|
+
readonly skipMath: {
|
|
75
|
+
readonly type: "boolean";
|
|
76
|
+
};
|
|
77
|
+
readonly skipInlineMath: {
|
|
78
|
+
readonly type: "boolean";
|
|
79
|
+
};
|
|
64
80
|
};
|
|
65
81
|
readonly additionalProperties: false;
|
|
66
82
|
}];
|
|
@@ -68,6 +84,8 @@ declare const _default: {
|
|
|
68
84
|
readonly allow: [];
|
|
69
85
|
readonly skipCode: true;
|
|
70
86
|
readonly skipInlineCode: true;
|
|
87
|
+
readonly skipMath: false;
|
|
88
|
+
readonly skipInlineMath: false;
|
|
71
89
|
}];
|
|
72
90
|
readonly messages: {
|
|
73
91
|
readonly noControlCharacter: "Control character `{{ controlCharacter }}` is not allowed.";
|
|
@@ -82,6 +100,8 @@ declare const _default: {
|
|
|
82
100
|
}>) => {
|
|
83
101
|
code(node: import("mdast").Code): void;
|
|
84
102
|
inlineCode(node: import("mdast").InlineCode): void;
|
|
103
|
+
math(node: import("mdast-util-math").Math): void;
|
|
104
|
+
inlineMath(node: import("mdast-util-math").InlineMath): void;
|
|
85
105
|
'root:exit'(): void;
|
|
86
106
|
};
|
|
87
107
|
};
|
|
@@ -53,6 +53,12 @@ export default {
|
|
|
53
53
|
skipInlineCode: {
|
|
54
54
|
type: 'boolean',
|
|
55
55
|
},
|
|
56
|
+
skipMath: {
|
|
57
|
+
type: 'boolean',
|
|
58
|
+
},
|
|
59
|
+
skipInlineMath: {
|
|
60
|
+
type: 'boolean',
|
|
61
|
+
},
|
|
56
62
|
},
|
|
57
63
|
additionalProperties: false,
|
|
58
64
|
},
|
|
@@ -62,6 +68,8 @@ export default {
|
|
|
62
68
|
allow: [],
|
|
63
69
|
skipCode: true,
|
|
64
70
|
skipInlineCode: true,
|
|
71
|
+
skipMath: false,
|
|
72
|
+
skipInlineMath: false,
|
|
65
73
|
},
|
|
66
74
|
],
|
|
67
75
|
messages: {
|
|
@@ -73,7 +81,7 @@ export default {
|
|
|
73
81
|
},
|
|
74
82
|
create(context) {
|
|
75
83
|
const { sourceCode } = context;
|
|
76
|
-
const [{ allow, skipCode, skipInlineCode }] = context.options;
|
|
84
|
+
const [{ allow, skipCode, skipInlineCode, skipMath, skipInlineMath }] = context.options;
|
|
77
85
|
const skipRanges = new SkipRanges();
|
|
78
86
|
return {
|
|
79
87
|
code(node) {
|
|
@@ -84,6 +92,14 @@ export default {
|
|
|
84
92
|
if (skipInlineCode)
|
|
85
93
|
skipRanges.push(sourceCode.getRange(node)); // Store range information of `InlineCode`.
|
|
86
94
|
},
|
|
95
|
+
math(node) {
|
|
96
|
+
if (skipMath)
|
|
97
|
+
skipRanges.push(sourceCode.getRange(node)); // Store range information of `Math`.
|
|
98
|
+
},
|
|
99
|
+
inlineMath(node) {
|
|
100
|
+
if (skipInlineMath)
|
|
101
|
+
skipRanges.push(sourceCode.getRange(node)); // Store range information of `InlineMath`.
|
|
102
|
+
},
|
|
87
103
|
'root:exit'() {
|
|
88
104
|
const matches = sourceCode.text.matchAll(controlCharacterRegex);
|
|
89
105
|
for (const match of matches) {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// Import
|
|
7
7
|
// --------------------------------------------------------------------------------
|
|
8
8
|
import { escapeStringRegexp } from '../core/utils/index.js';
|
|
9
|
-
import { URL_RULE_DOCS, punctuation as defaultPunctuation, escapedTrailingBackslashRegex, gemojiRegex, htmlEntityRegex, } from '../core/constants.js';
|
|
9
|
+
import { URL_RULE_DOCS, punctuation as defaultPunctuation, asciiPunctuationRegex, escapedTrailingBackslashRegex, gemojiRegex, htmlEntityRegex, } from '../core/constants.js';
|
|
10
10
|
// --------------------------------------------------------------------------------
|
|
11
11
|
// Helper
|
|
12
12
|
// --------------------------------------------------------------------------------
|
|
@@ -174,6 +174,8 @@ export default {
|
|
|
174
174
|
Number(
|
|
175
175
|
// When `leadingSpaces` is `''` (empty string), check for an escaped trailing backslash.
|
|
176
176
|
!leadingSpaces &&
|
|
177
|
+
// The preceding backslash is an escape only when the punctuation is escapable.
|
|
178
|
+
asciiPunctuationRegex.test(trailingPunctuation[0]) &&
|
|
177
179
|
escapedTrailingBackslashRegex.test(lastChildText.slice(0, -trailingPunctuation.length))),
|
|
178
180
|
endOffset,
|
|
179
181
|
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-markdown",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Lint your Markdown with ESLint. Additional rules for use with `@eslint/markdown`.🛠️",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"prepublishOnly": "npm run build",
|
|
60
|
-
"build": "tsc -b tsconfig.build.json && node ../../scripts/build-config.js && tsc -b tsconfig.build.json
|
|
60
|
+
"build": "tsc -b tsconfig.build.json && node ../../scripts/build-config.js && tsc -b tsconfig.build.json",
|
|
61
61
|
"test": "npm run test:types && npm run test:unit",
|
|
62
62
|
"test:types": "tsc -b ./tsconfig.test-d.json",
|
|
63
63
|
"test:unit": "vitest run"
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"@eslint/markdown": "^8.0.3",
|
|
80
80
|
"@types/mdast": "^4.0.4",
|
|
81
81
|
"@types/unist": "^3.0.3",
|
|
82
|
-
"eslint": "^
|
|
82
|
+
"eslint": "^10.13.0",
|
|
83
83
|
"vitest": "^4.1.9"
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/build/rules/glossary.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|