eslint-markdown 0.1.0-canary.12 → 0.2.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 +8 -1
- package/build/configs/all.d.ts +1 -6
- package/build/configs/recommended.d.ts +0 -3
- package/build/configs/stylistic.d.ts +1 -0
- package/build/rules/code-lang-shorthand.d.ts +4 -4
- package/build/rules/index.d.ts +1 -5
- package/build/rules/no-control-character.d.ts +9 -0
- package/build/rules/no-irregular-dash.d.ts +9 -0
- package/build/rules/no-irregular-whitespace.d.ts +9 -0
- package/build/rules/no-tab.d.ts +56 -0
- package/package.json +3 -3
- package/src/configs/all.js +1 -6
- package/src/configs/recommended.js +0 -3
- package/src/configs/stylistic.js +1 -0
- package/src/rules/code-lang-shorthand.js +53 -41
- package/src/rules/index.js +12 -10
- package/src/rules/no-control-character.js +12 -2
- package/src/rules/no-irregular-dash.js +12 -2
- package/src/rules/no-irregular-whitespace.js +12 -2
- package/src/rules/no-tab.js +128 -0
- package/build/rules/alt-text.d.ts +0 -30
- package/src/rules/alt-text.js +0 -88
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/lumirlumir/npm-eslint-markdown/actions/workflows/lint.yml)
|
|
4
4
|
[](https://github.com/lumirlumir/npm-eslint-markdown/actions/workflows/test.yml)
|
|
5
|
-
[](https://github.com/lumirlumir/npm-eslint-markdown/actions/workflows/test-cross-platform.yml)
|
|
6
|
+
[](https://codecov.io/gh/lumirlumir/npm-eslint-markdown)
|
|
7
|
+

|
|
6
8
|
|
|
7
9
|
[](https://www.npmjs.com/package/eslint-markdown)
|
|
8
10
|
[](https://www.npmjs.com/package/eslint-markdown)
|
|
@@ -18,6 +20,11 @@ Lint your Markdown with ESLint.🛠️
|
|
|
18
20
|
|
|
19
21
|
For full documentation, see the [official documentation of the `eslint-markdown`](https://eslint-markdown.lumir.page).
|
|
20
22
|
|
|
23
|
+
- [Installation](https://eslint-markdown.lumir.page/docs/get-started/installation)
|
|
24
|
+
- [Configurations](https://eslint-markdown.lumir.page/docs/get-started/configurations)
|
|
25
|
+
- [Migration Guide](https://eslint-markdown.lumir.page/docs/get-started/migration)
|
|
26
|
+
- [Rules](https://eslint-markdown.lumir.page/docs/rules)
|
|
27
|
+
|
|
21
28
|
## Code of Conduct
|
|
22
29
|
|
|
23
30
|
See [Code of Conduct](https://github.com/lumirlumir/.github/blob/main/CODE_OF_CONDUCT.md#contributor-covenant-code-of-conduct).
|
package/build/configs/all.d.ts
CHANGED
|
@@ -14,19 +14,13 @@ export default function all(plugin: ESLint.Plugin): {
|
|
|
14
14
|
};
|
|
15
15
|
readonly language: "markdown/gfm";
|
|
16
16
|
readonly rules: {
|
|
17
|
-
readonly 'markdown/no-unused-definitions': "error";
|
|
18
|
-
readonly 'md/allow-heading': "error";
|
|
19
17
|
readonly 'md/allow-image-url': "error";
|
|
20
18
|
readonly 'md/allow-link-url': "error";
|
|
21
|
-
readonly 'md/alt-text': "error";
|
|
22
19
|
readonly 'md/code-lang-shorthand': "error";
|
|
23
20
|
readonly 'md/consistent-delete-style': "error";
|
|
24
21
|
readonly 'md/consistent-emphasis-style': "error";
|
|
25
22
|
readonly 'md/consistent-strong-style': "error";
|
|
26
23
|
readonly 'md/consistent-thematic-break-style': "error";
|
|
27
|
-
readonly 'md/en-capitalization': "error";
|
|
28
|
-
readonly 'md/heading-id': "error";
|
|
29
|
-
readonly 'md/no-bold-paragraph': "error";
|
|
30
24
|
readonly 'md/no-control-character': "error";
|
|
31
25
|
readonly 'md/no-curly-quote': "error";
|
|
32
26
|
readonly 'md/no-double-space': "error";
|
|
@@ -34,6 +28,7 @@ export default function all(plugin: ESLint.Plugin): {
|
|
|
34
28
|
readonly 'md/no-git-conflict-marker': "error";
|
|
35
29
|
readonly 'md/no-irregular-dash': "error";
|
|
36
30
|
readonly 'md/no-irregular-whitespace': "error";
|
|
31
|
+
readonly 'md/no-tab': "error";
|
|
37
32
|
readonly 'md/no-url-trailing-slash': "error";
|
|
38
33
|
readonly 'md/require-image-title': "error";
|
|
39
34
|
readonly 'md/require-link-title': "error";
|
|
@@ -14,10 +14,7 @@ export default function recommended(plugin: ESLint.Plugin): {
|
|
|
14
14
|
};
|
|
15
15
|
readonly language: "markdown/gfm";
|
|
16
16
|
readonly rules: {
|
|
17
|
-
readonly 'markdown/no-unused-definitions': "error";
|
|
18
|
-
readonly 'md/alt-text': "error";
|
|
19
17
|
readonly 'md/code-lang-shorthand': "error";
|
|
20
|
-
readonly 'md/no-bold-paragraph': "error";
|
|
21
18
|
readonly 'md/no-control-character': "error";
|
|
22
19
|
readonly 'md/no-curly-quote': "error";
|
|
23
20
|
readonly 'md/no-double-space': "error";
|
|
@@ -18,6 +18,7 @@ export default function stylistic(plugin: ESLint.Plugin): {
|
|
|
18
18
|
readonly 'md/consistent-emphasis-style': "error";
|
|
19
19
|
readonly 'md/consistent-strong-style': "error";
|
|
20
20
|
readonly 'md/consistent-thematic-break-style': "error";
|
|
21
|
+
readonly 'md/no-tab': "error";
|
|
21
22
|
};
|
|
22
23
|
};
|
|
23
24
|
import type { ESLint } from "eslint";
|
|
@@ -11,10 +11,10 @@ declare const _default: {
|
|
|
11
11
|
schema: {
|
|
12
12
|
type: "object";
|
|
13
13
|
properties: {
|
|
14
|
-
|
|
14
|
+
allow: {
|
|
15
15
|
type: "array";
|
|
16
16
|
items: {
|
|
17
|
-
|
|
17
|
+
type: "string";
|
|
18
18
|
};
|
|
19
19
|
uniqueItems: true;
|
|
20
20
|
};
|
|
@@ -28,7 +28,7 @@ declare const _default: {
|
|
|
28
28
|
additionalProperties: false;
|
|
29
29
|
}[];
|
|
30
30
|
defaultOptions: [{
|
|
31
|
-
|
|
31
|
+
allow: never[];
|
|
32
32
|
override: {};
|
|
33
33
|
}];
|
|
34
34
|
messages: {
|
|
@@ -49,7 +49,7 @@ declare const _default: {
|
|
|
49
49
|
};
|
|
50
50
|
export default _default;
|
|
51
51
|
export type RuleOptions = [{
|
|
52
|
-
|
|
52
|
+
allow: string[];
|
|
53
53
|
override: Record<string, string>;
|
|
54
54
|
}];
|
|
55
55
|
export type MessageIds = "codeLangShorthand";
|
package/build/rules/index.d.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
declare const _default: {
|
|
2
|
-
'allow-heading': import("../core/types.js").RuleModule<import("./allow-heading.js").RuleOptions, import("./allow-heading.js").MessageIds>;
|
|
3
2
|
'allow-image-url': import("../core/types.js").RuleModule<import("./allow-image-url.js").RuleOptions, import("./allow-image-url.js").MessageIds>;
|
|
4
3
|
'allow-link-url': import("../core/types.js").RuleModule<import("./allow-link-url.js").RuleOptions, import("./allow-link-url.js").MessageIds>;
|
|
5
|
-
'alt-text': import("../core/types.js").RuleModule<[], "altText">;
|
|
6
4
|
'code-lang-shorthand': import("../core/types.js").RuleModule<import("./code-lang-shorthand.js").RuleOptions, "codeLangShorthand">;
|
|
7
5
|
'consistent-delete-style': import("../core/types.js").RuleModule<import("./consistent-delete-style.js").RuleOptions, "style">;
|
|
8
6
|
'consistent-emphasis-style': import("../core/types.js").RuleModule<import("./consistent-emphasis-style.js").RuleOptions, "style">;
|
|
9
7
|
'consistent-strong-style': import("../core/types.js").RuleModule<import("./consistent-strong-style.js").RuleOptions, "style">;
|
|
10
8
|
'consistent-thematic-break-style': import("../core/types.js").RuleModule<import("./consistent-thematic-break-style.js").RuleOptions, "style">;
|
|
11
|
-
'en-capitalization': import("../core/types.js").RuleModule<import("./en-capitalization.js").RuleOptions, "enCapitalization">;
|
|
12
|
-
'heading-id': import("../core/types.js").RuleModule<import("./heading-id.js").RuleOptions, import("./heading-id.js").MessageIds>;
|
|
13
|
-
'no-bold-paragraph': import("../core/types.js").RuleModule<[], "noBoldParagraph">;
|
|
14
9
|
'no-control-character': import("../core/types.js").RuleModule<import("./no-control-character.js").RuleOptions, "noControlCharacter">;
|
|
15
10
|
'no-curly-quote': import("../core/types.js").RuleModule<import("./no-curly-quote.js").RuleOptions, "noCurlyQuote">;
|
|
16
11
|
'no-double-space': import("../core/types.js").RuleModule<import("./no-double-space.js").RuleOptions, import("./no-double-space.js").MessageIds>;
|
|
@@ -18,6 +13,7 @@ declare const _default: {
|
|
|
18
13
|
'no-git-conflict-marker': import("../core/types.js").RuleModule<import("./no-git-conflict-marker.js").RuleOptions, "noGitConflictMarker">;
|
|
19
14
|
'no-irregular-dash': import("../core/types.js").RuleModule<import("./no-irregular-dash.js").RuleOptions, "noIrregularDash">;
|
|
20
15
|
'no-irregular-whitespace': import("../core/types.js").RuleModule<import("./no-irregular-whitespace.js").RuleOptions, "noIrregularWhitespace">;
|
|
16
|
+
'no-tab': import("../core/types.js").RuleModule<import("./no-tab.js").RuleOptions, "noTab">;
|
|
21
17
|
'no-url-trailing-slash': import("../core/types.js").RuleModule<[], "noUrlTrailingSlash">;
|
|
22
18
|
'require-image-title': import("../core/types.js").RuleModule<import("./require-image-title.js").RuleOptions, "requireImageTitle">;
|
|
23
19
|
'require-link-title': import("../core/types.js").RuleModule<import("./require-link-title.js").RuleOptions, "requireLinkTitle">;
|
|
@@ -10,6 +10,13 @@ declare const _default: {
|
|
|
10
10
|
schema: {
|
|
11
11
|
type: "object";
|
|
12
12
|
properties: {
|
|
13
|
+
allow: {
|
|
14
|
+
type: "array";
|
|
15
|
+
items: {
|
|
16
|
+
type: "string";
|
|
17
|
+
};
|
|
18
|
+
uniqueItems: true;
|
|
19
|
+
};
|
|
13
20
|
skipCode: {
|
|
14
21
|
type: "boolean";
|
|
15
22
|
};
|
|
@@ -20,6 +27,7 @@ declare const _default: {
|
|
|
20
27
|
additionalProperties: false;
|
|
21
28
|
}[];
|
|
22
29
|
defaultOptions: [{
|
|
30
|
+
allow: never[];
|
|
23
31
|
skipCode: true;
|
|
24
32
|
skipInlineCode: true;
|
|
25
33
|
}];
|
|
@@ -43,6 +51,7 @@ declare const _default: {
|
|
|
43
51
|
};
|
|
44
52
|
export default _default;
|
|
45
53
|
export type RuleOptions = [{
|
|
54
|
+
allow: string[];
|
|
46
55
|
skipCode: boolean;
|
|
47
56
|
skipInlineCode: boolean;
|
|
48
57
|
}];
|
|
@@ -10,6 +10,13 @@ declare const _default: {
|
|
|
10
10
|
schema: {
|
|
11
11
|
type: "object";
|
|
12
12
|
properties: {
|
|
13
|
+
allow: {
|
|
14
|
+
type: "array";
|
|
15
|
+
items: {
|
|
16
|
+
type: "string";
|
|
17
|
+
};
|
|
18
|
+
uniqueItems: true;
|
|
19
|
+
};
|
|
13
20
|
skipCode: {
|
|
14
21
|
type: "boolean";
|
|
15
22
|
};
|
|
@@ -20,6 +27,7 @@ declare const _default: {
|
|
|
20
27
|
additionalProperties: false;
|
|
21
28
|
}[];
|
|
22
29
|
defaultOptions: [{
|
|
30
|
+
allow: never[];
|
|
23
31
|
skipCode: true;
|
|
24
32
|
skipInlineCode: true;
|
|
25
33
|
}];
|
|
@@ -43,6 +51,7 @@ declare const _default: {
|
|
|
43
51
|
};
|
|
44
52
|
export default _default;
|
|
45
53
|
export type RuleOptions = [{
|
|
54
|
+
allow: string[];
|
|
46
55
|
skipCode: boolean;
|
|
47
56
|
skipInlineCode: boolean;
|
|
48
57
|
}];
|
|
@@ -10,6 +10,13 @@ declare const _default: {
|
|
|
10
10
|
schema: {
|
|
11
11
|
type: "object";
|
|
12
12
|
properties: {
|
|
13
|
+
allow: {
|
|
14
|
+
type: "array";
|
|
15
|
+
items: {
|
|
16
|
+
type: "string";
|
|
17
|
+
};
|
|
18
|
+
uniqueItems: true;
|
|
19
|
+
};
|
|
13
20
|
skipCode: {
|
|
14
21
|
type: "boolean";
|
|
15
22
|
};
|
|
@@ -20,6 +27,7 @@ declare const _default: {
|
|
|
20
27
|
additionalProperties: false;
|
|
21
28
|
}[];
|
|
22
29
|
defaultOptions: [{
|
|
30
|
+
allow: never[];
|
|
23
31
|
skipCode: true;
|
|
24
32
|
skipInlineCode: true;
|
|
25
33
|
}];
|
|
@@ -43,6 +51,7 @@ declare const _default: {
|
|
|
43
51
|
};
|
|
44
52
|
export default _default;
|
|
45
53
|
export type RuleOptions = [{
|
|
54
|
+
allow: string[];
|
|
46
55
|
skipCode: boolean;
|
|
47
56
|
skipInlineCode: boolean;
|
|
48
57
|
}];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
meta: {
|
|
3
|
+
type: "problem";
|
|
4
|
+
docs: {
|
|
5
|
+
description: string;
|
|
6
|
+
url: string;
|
|
7
|
+
recommended: boolean;
|
|
8
|
+
stylistic: true;
|
|
9
|
+
};
|
|
10
|
+
fixable: "whitespace";
|
|
11
|
+
schema: {
|
|
12
|
+
type: "object";
|
|
13
|
+
properties: {
|
|
14
|
+
skipCode: {
|
|
15
|
+
type: "boolean";
|
|
16
|
+
};
|
|
17
|
+
skipInlineCode: {
|
|
18
|
+
type: "boolean";
|
|
19
|
+
};
|
|
20
|
+
tabWidth: {
|
|
21
|
+
type: "integer";
|
|
22
|
+
minimum: number;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
additionalProperties: false;
|
|
26
|
+
}[];
|
|
27
|
+
defaultOptions: [{
|
|
28
|
+
skipCode: true;
|
|
29
|
+
skipInlineCode: true;
|
|
30
|
+
tabWidth: number;
|
|
31
|
+
}];
|
|
32
|
+
messages: {
|
|
33
|
+
noTab: string;
|
|
34
|
+
};
|
|
35
|
+
language: string;
|
|
36
|
+
dialects: string[];
|
|
37
|
+
};
|
|
38
|
+
create(context: import("@eslint/core").RuleContext<{
|
|
39
|
+
LangOptions: import("@eslint/markdown").MarkdownLanguageOptions;
|
|
40
|
+
Code: import("@eslint/markdown").MarkdownSourceCode;
|
|
41
|
+
RuleOptions: RuleOptions;
|
|
42
|
+
Node: import("mdast").Node;
|
|
43
|
+
MessageIds: "noTab";
|
|
44
|
+
}>): {
|
|
45
|
+
code(node: import("mdast").Code): void;
|
|
46
|
+
inlineCode(node: import("mdast").InlineCode): void;
|
|
47
|
+
'root:exit'(): void;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
export default _default;
|
|
51
|
+
export type RuleOptions = [{
|
|
52
|
+
skipCode: boolean;
|
|
53
|
+
skipInlineCode: boolean;
|
|
54
|
+
tabWidth: number;
|
|
55
|
+
}];
|
|
56
|
+
export type MessageIds = "noTab";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-markdown",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Lint your Markdown with ESLint.🛠️",
|
|
6
6
|
"exports": {
|
|
@@ -58,13 +58,13 @@
|
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"prepublishOnly": "npm run build",
|
|
61
|
-
"build": "npx tsc && node ../../scripts/cp.mjs ../../LICENSE.md LICENSE.md ../../README.md README.md",
|
|
61
|
+
"build": "node ../../scripts/build-config.mjs && npx tsc && node ../../scripts/cp.mjs ../../LICENSE.md LICENSE.md ../../README.md README.md",
|
|
62
62
|
"test": "npm run test:types && npm run test:unit",
|
|
63
63
|
"test:types": "tsc -p ./tsconfig.test.json",
|
|
64
64
|
"test:unit": "node --test"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"eslint": "^9.
|
|
67
|
+
"eslint": "^9.31.0 || ^10.0.0-beta.0"
|
|
68
68
|
},
|
|
69
69
|
"peerDependenciesMeta": {
|
|
70
70
|
"eslint": {
|
package/src/configs/all.js
CHANGED
|
@@ -38,19 +38,13 @@ export default function all(plugin) {
|
|
|
38
38
|
},
|
|
39
39
|
language: 'markdown/gfm',
|
|
40
40
|
rules: {
|
|
41
|
-
'markdown/no-unused-definitions': 'error',
|
|
42
|
-
'md/allow-heading': 'error',
|
|
43
41
|
'md/allow-image-url': 'error',
|
|
44
42
|
'md/allow-link-url': 'error',
|
|
45
|
-
'md/alt-text': 'error',
|
|
46
43
|
'md/code-lang-shorthand': 'error',
|
|
47
44
|
'md/consistent-delete-style': 'error',
|
|
48
45
|
'md/consistent-emphasis-style': 'error',
|
|
49
46
|
'md/consistent-strong-style': 'error',
|
|
50
47
|
'md/consistent-thematic-break-style': 'error',
|
|
51
|
-
'md/en-capitalization': 'error',
|
|
52
|
-
'md/heading-id': 'error',
|
|
53
|
-
'md/no-bold-paragraph': 'error',
|
|
54
48
|
'md/no-control-character': 'error',
|
|
55
49
|
'md/no-curly-quote': 'error',
|
|
56
50
|
'md/no-double-space': 'error',
|
|
@@ -58,6 +52,7 @@ export default function all(plugin) {
|
|
|
58
52
|
'md/no-git-conflict-marker': 'error',
|
|
59
53
|
'md/no-irregular-dash': 'error',
|
|
60
54
|
'md/no-irregular-whitespace': 'error',
|
|
55
|
+
'md/no-tab': 'error',
|
|
61
56
|
'md/no-url-trailing-slash': 'error',
|
|
62
57
|
'md/require-image-title': 'error',
|
|
63
58
|
'md/require-link-title': 'error',
|
|
@@ -38,10 +38,7 @@ export default function recommended(plugin) {
|
|
|
38
38
|
},
|
|
39
39
|
language: 'markdown/gfm',
|
|
40
40
|
rules: {
|
|
41
|
-
'markdown/no-unused-definitions': 'error',
|
|
42
|
-
'md/alt-text': 'error',
|
|
43
41
|
'md/code-lang-shorthand': 'error',
|
|
44
|
-
'md/no-bold-paragraph': 'error',
|
|
45
42
|
'md/no-control-character': 'error',
|
|
46
43
|
'md/no-curly-quote': 'error',
|
|
47
44
|
'md/no-double-space': 'error',
|
package/src/configs/stylistic.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Rule to enforce the use of shorthand for code block language identifiers.
|
|
3
3
|
* @author 루밀LuMir(lumirlumir)
|
|
4
|
-
* @see https://shiki.style/languages#bundled-languages
|
|
5
|
-
* @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries
|
|
6
|
-
* @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
|
|
7
4
|
*/
|
|
8
5
|
|
|
9
|
-
// @ts-nocheck -- TODO
|
|
10
|
-
|
|
11
6
|
// --------------------------------------------------------------------------------
|
|
12
7
|
// Import
|
|
13
8
|
// --------------------------------------------------------------------------------
|
|
@@ -15,20 +10,24 @@
|
|
|
15
10
|
import { URL_RULE_DOCS } from '../core/constants.js';
|
|
16
11
|
|
|
17
12
|
// --------------------------------------------------------------------------------
|
|
18
|
-
//
|
|
13
|
+
// Typedef
|
|
19
14
|
// --------------------------------------------------------------------------------
|
|
20
15
|
|
|
21
16
|
/**
|
|
22
17
|
* @import { RuleModule } from '../core/types.js';
|
|
23
|
-
* @typedef {[{
|
|
18
|
+
* @typedef {[{ allow: string[], override: Record<string, string> }]} RuleOptions
|
|
24
19
|
* @typedef {'codeLangShorthand'} MessageIds
|
|
25
20
|
*/
|
|
26
21
|
|
|
27
22
|
// --------------------------------------------------------------------------------
|
|
28
|
-
//
|
|
23
|
+
// Helper
|
|
29
24
|
// --------------------------------------------------------------------------------
|
|
30
25
|
|
|
31
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Please note that the keys and values should be in lowercase.
|
|
28
|
+
* @see https://shiki.style/languages#bundled-languages
|
|
29
|
+
* @type {Record<string, string>}
|
|
30
|
+
*/
|
|
32
31
|
const langShorthandMap = Object.freeze({
|
|
33
32
|
asciidoc: 'adoc',
|
|
34
33
|
batch: 'bat',
|
|
@@ -101,6 +100,11 @@ const langShorthandMap = Object.freeze({
|
|
|
101
100
|
yaml: 'yml',
|
|
102
101
|
});
|
|
103
102
|
|
|
103
|
+
/** @param {string} str */
|
|
104
|
+
function normalize(str) {
|
|
105
|
+
return str.toLowerCase();
|
|
106
|
+
}
|
|
107
|
+
|
|
104
108
|
// --------------------------------------------------------------------------------
|
|
105
109
|
// Rule Definition
|
|
106
110
|
// --------------------------------------------------------------------------------
|
|
@@ -123,10 +127,10 @@ export default {
|
|
|
123
127
|
{
|
|
124
128
|
type: 'object',
|
|
125
129
|
properties: {
|
|
126
|
-
|
|
130
|
+
allow: {
|
|
127
131
|
type: 'array',
|
|
128
132
|
items: {
|
|
129
|
-
|
|
133
|
+
type: 'string',
|
|
130
134
|
},
|
|
131
135
|
uniqueItems: true,
|
|
132
136
|
},
|
|
@@ -143,7 +147,7 @@ export default {
|
|
|
143
147
|
|
|
144
148
|
defaultOptions: [
|
|
145
149
|
{
|
|
146
|
-
|
|
150
|
+
allow: [],
|
|
147
151
|
override: {},
|
|
148
152
|
},
|
|
149
153
|
],
|
|
@@ -158,36 +162,50 @@ export default {
|
|
|
158
162
|
},
|
|
159
163
|
|
|
160
164
|
create(context) {
|
|
165
|
+
const { sourceCode } = context;
|
|
166
|
+
const [{ allow, override }] = context.options;
|
|
167
|
+
|
|
168
|
+
const normalizedAllow = new Set(allow.map(normalize));
|
|
169
|
+
const normalizedOverride = Object.fromEntries(
|
|
170
|
+
Object.entries(override).map(([key, value]) => [normalize(key), normalize(value)]), // Normalize keys and values.
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const mergedLangShorthandMap = {
|
|
174
|
+
...langShorthandMap,
|
|
175
|
+
...normalizedOverride,
|
|
176
|
+
};
|
|
177
|
+
|
|
161
178
|
return {
|
|
162
179
|
code(node) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
.map(([key, value]) => [key.toLowerCase(), value.toLowerCase()]) // Normalize keys and values.
|
|
170
|
-
.filter(([key]) => !ignores.includes(key)), // `ignores` option handling.
|
|
171
|
-
);
|
|
172
|
-
const langShorthand = langShorthandMapMerged[node.lang?.toLowerCase()]; // Normalize lang.
|
|
180
|
+
// If it's 'Indented code block' or 'Fenced code block without lang', skip it.
|
|
181
|
+
if (node.lang === null || node.lang === undefined) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const normalizedLang = normalize(node.lang);
|
|
173
186
|
|
|
174
|
-
|
|
187
|
+
// If the lang is in the allow list, skip it.
|
|
188
|
+
if (normalizedAllow.has(normalizedLang)) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
175
191
|
|
|
176
|
-
const
|
|
192
|
+
const langShorthand = mergedLangShorthandMap[normalizedLang];
|
|
177
193
|
|
|
178
|
-
|
|
179
|
-
|
|
194
|
+
// If there is no shorthand for the lang, skip it.
|
|
195
|
+
if (langShorthand === undefined) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const [nodeStartOffset] = sourceCode.getRange(node);
|
|
200
|
+
const matchIndex = sourceCode.getText(node).indexOf(node.lang);
|
|
201
|
+
|
|
202
|
+
const startOffset = nodeStartOffset + matchIndex;
|
|
203
|
+
const endOffset = startOffset + node.lang.length;
|
|
180
204
|
|
|
181
205
|
context.report({
|
|
182
206
|
loc: {
|
|
183
|
-
start:
|
|
184
|
-
|
|
185
|
-
column: node.position.start.column + matchIndexStart,
|
|
186
|
-
},
|
|
187
|
-
end: {
|
|
188
|
-
line: node.position.start.line,
|
|
189
|
-
column: node.position.start.column + matchIndexEnd,
|
|
190
|
-
},
|
|
207
|
+
start: sourceCode.getLocFromIndex(startOffset),
|
|
208
|
+
end: sourceCode.getLocFromIndex(endOffset),
|
|
191
209
|
},
|
|
192
210
|
|
|
193
211
|
data: {
|
|
@@ -198,13 +216,7 @@ export default {
|
|
|
198
216
|
messageId: 'codeLangShorthand',
|
|
199
217
|
|
|
200
218
|
fix(fixer) {
|
|
201
|
-
return fixer.replaceTextRange(
|
|
202
|
-
[
|
|
203
|
-
node.position.start.offset + matchIndexStart,
|
|
204
|
-
node.position.start.offset + matchIndexEnd,
|
|
205
|
-
],
|
|
206
|
-
langShorthand,
|
|
207
|
-
);
|
|
219
|
+
return fixer.replaceTextRange([startOffset, endOffset], langShorthand);
|
|
208
220
|
},
|
|
209
221
|
});
|
|
210
222
|
},
|
package/src/rules/index.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
/* eslint sort-imports: 'error', sort-keys: 'error' */
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// TODO: Re-enable the commented-out rules once they stabilize.
|
|
4
|
+
|
|
5
|
+
// import allowHeading from './allow-heading.js';
|
|
4
6
|
import allowImageUrl from './allow-image-url.js';
|
|
5
7
|
import allowLinkUrl from './allow-link-url.js';
|
|
6
|
-
import altText from './alt-text.js';
|
|
7
8
|
import codeLangShorthand from './code-lang-shorthand.js';
|
|
8
9
|
import consistentDeleteStyle from './consistent-delete-style.js';
|
|
9
10
|
import consistentEmphasisStyle from './consistent-emphasis-style.js';
|
|
10
11
|
import consistentStrongStyle from './consistent-strong-style.js';
|
|
11
12
|
import consistentThematicBreakStyle from './consistent-thematic-break-style.js';
|
|
12
|
-
import enCapitalization from './en-capitalization.js';
|
|
13
|
-
import headingId from './heading-id.js';
|
|
14
|
-
import noBoldParagraph from './no-bold-paragraph.js';
|
|
13
|
+
// import enCapitalization from './en-capitalization.js';
|
|
14
|
+
// import headingId from './heading-id.js';
|
|
15
|
+
// import noBoldParagraph from './no-bold-paragraph.js';
|
|
15
16
|
import noControlCharacter from './no-control-character.js';
|
|
16
17
|
import noCurlyQuote from './no-curly-quote.js';
|
|
17
18
|
import noDoubleSpace from './no-double-space.js';
|
|
@@ -19,23 +20,23 @@ import noEmoji from './no-emoji.js';
|
|
|
19
20
|
import noGitConflictMarker from './no-git-conflict-marker.js';
|
|
20
21
|
import noIrregularDash from './no-irregular-dash.js';
|
|
21
22
|
import noIrregularWhitespace from './no-irregular-whitespace.js';
|
|
23
|
+
import noTab from './no-tab.js';
|
|
22
24
|
import noUrlTrailingSlash from './no-url-trailing-slash.js';
|
|
23
25
|
import requireImageTitle from './require-image-title.js';
|
|
24
26
|
import requireLinkTitle from './require-link-title.js';
|
|
25
27
|
|
|
26
28
|
export default {
|
|
27
|
-
'allow-heading': allowHeading,
|
|
29
|
+
// 'allow-heading': allowHeading,
|
|
28
30
|
'allow-image-url': allowImageUrl,
|
|
29
31
|
'allow-link-url': allowLinkUrl,
|
|
30
|
-
'alt-text': altText,
|
|
31
32
|
'code-lang-shorthand': codeLangShorthand,
|
|
32
33
|
'consistent-delete-style': consistentDeleteStyle,
|
|
33
34
|
'consistent-emphasis-style': consistentEmphasisStyle,
|
|
34
35
|
'consistent-strong-style': consistentStrongStyle,
|
|
35
36
|
'consistent-thematic-break-style': consistentThematicBreakStyle,
|
|
36
|
-
'en-capitalization': enCapitalization,
|
|
37
|
-
'heading-id': headingId,
|
|
38
|
-
'no-bold-paragraph': noBoldParagraph,
|
|
37
|
+
// 'en-capitalization': enCapitalization,
|
|
38
|
+
// 'heading-id': headingId,
|
|
39
|
+
// 'no-bold-paragraph': noBoldParagraph,
|
|
39
40
|
'no-control-character': noControlCharacter,
|
|
40
41
|
'no-curly-quote': noCurlyQuote,
|
|
41
42
|
'no-double-space': noDoubleSpace,
|
|
@@ -43,6 +44,7 @@ export default {
|
|
|
43
44
|
'no-git-conflict-marker': noGitConflictMarker,
|
|
44
45
|
'no-irregular-dash': noIrregularDash,
|
|
45
46
|
'no-irregular-whitespace': noIrregularWhitespace,
|
|
47
|
+
'no-tab': noTab,
|
|
46
48
|
'no-url-trailing-slash': noUrlTrailingSlash,
|
|
47
49
|
'require-image-title': requireImageTitle,
|
|
48
50
|
'require-link-title': requireLinkTitle,
|
|
@@ -16,7 +16,7 @@ import { URL_RULE_DOCS } from '../core/constants.js';
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @import { RuleModule } from '../core/types.js';
|
|
19
|
-
* @typedef {[{ skipCode: boolean, skipInlineCode: boolean }]} RuleOptions
|
|
19
|
+
* @typedef {[{ allow: string[], skipCode: boolean, skipInlineCode: boolean }]} RuleOptions
|
|
20
20
|
* @typedef {'noControlCharacter'} MessageIds
|
|
21
21
|
*/
|
|
22
22
|
|
|
@@ -47,6 +47,13 @@ export default {
|
|
|
47
47
|
{
|
|
48
48
|
type: 'object',
|
|
49
49
|
properties: {
|
|
50
|
+
allow: {
|
|
51
|
+
type: 'array',
|
|
52
|
+
items: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
},
|
|
55
|
+
uniqueItems: true,
|
|
56
|
+
},
|
|
50
57
|
skipCode: {
|
|
51
58
|
type: 'boolean',
|
|
52
59
|
},
|
|
@@ -60,6 +67,7 @@ export default {
|
|
|
60
67
|
|
|
61
68
|
defaultOptions: [
|
|
62
69
|
{
|
|
70
|
+
allow: [],
|
|
63
71
|
skipCode: true,
|
|
64
72
|
skipInlineCode: true,
|
|
65
73
|
},
|
|
@@ -76,7 +84,7 @@ export default {
|
|
|
76
84
|
|
|
77
85
|
create(context) {
|
|
78
86
|
const { sourceCode } = context;
|
|
79
|
-
const [{ skipCode, skipInlineCode }] = context.options;
|
|
87
|
+
const [{ allow, skipCode, skipInlineCode }] = context.options;
|
|
80
88
|
|
|
81
89
|
const skipRanges = new SkipRanges();
|
|
82
90
|
|
|
@@ -95,6 +103,8 @@ export default {
|
|
|
95
103
|
for (const match of matches) {
|
|
96
104
|
const controlCharacter = match[0];
|
|
97
105
|
|
|
106
|
+
if (allow.includes(controlCharacter)) continue;
|
|
107
|
+
|
|
98
108
|
const startOffset = match.index;
|
|
99
109
|
const endOffset = startOffset + controlCharacter.length;
|
|
100
110
|
|
|
@@ -16,7 +16,7 @@ import { URL_RULE_DOCS } from '../core/constants.js';
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @import { RuleModule } from '../core/types.js';
|
|
19
|
-
* @typedef {[{ skipCode: boolean, skipInlineCode: boolean }]} RuleOptions
|
|
19
|
+
* @typedef {[{ allow: string[], skipCode: boolean, skipInlineCode: boolean }]} RuleOptions
|
|
20
20
|
* @typedef {'noIrregularDash'} MessageIds
|
|
21
21
|
*/
|
|
22
22
|
|
|
@@ -47,6 +47,13 @@ export default {
|
|
|
47
47
|
{
|
|
48
48
|
type: 'object',
|
|
49
49
|
properties: {
|
|
50
|
+
allow: {
|
|
51
|
+
type: 'array',
|
|
52
|
+
items: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
},
|
|
55
|
+
uniqueItems: true,
|
|
56
|
+
},
|
|
50
57
|
skipCode: {
|
|
51
58
|
type: 'boolean',
|
|
52
59
|
},
|
|
@@ -60,6 +67,7 @@ export default {
|
|
|
60
67
|
|
|
61
68
|
defaultOptions: [
|
|
62
69
|
{
|
|
70
|
+
allow: [],
|
|
63
71
|
skipCode: true,
|
|
64
72
|
skipInlineCode: true,
|
|
65
73
|
},
|
|
@@ -76,7 +84,7 @@ export default {
|
|
|
76
84
|
|
|
77
85
|
create(context) {
|
|
78
86
|
const { sourceCode } = context;
|
|
79
|
-
const [{ skipCode, skipInlineCode }] = context.options;
|
|
87
|
+
const [{ allow, skipCode, skipInlineCode }] = context.options;
|
|
80
88
|
|
|
81
89
|
const skipRanges = new SkipRanges();
|
|
82
90
|
|
|
@@ -95,6 +103,8 @@ export default {
|
|
|
95
103
|
for (const match of matches) {
|
|
96
104
|
const irregularDash = match[0];
|
|
97
105
|
|
|
106
|
+
if (allow.includes(irregularDash)) continue;
|
|
107
|
+
|
|
98
108
|
const startOffset = match.index;
|
|
99
109
|
const endOffset = startOffset + irregularDash.length;
|
|
100
110
|
|
|
@@ -16,7 +16,7 @@ import { URL_RULE_DOCS } from '../core/constants.js';
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @import { RuleModule } from '../core/types.js';
|
|
19
|
-
* @typedef {[{ skipCode: boolean, skipInlineCode: boolean }]} RuleOptions
|
|
19
|
+
* @typedef {[{ allow: string[], skipCode: boolean, skipInlineCode: boolean }]} RuleOptions
|
|
20
20
|
* @typedef {'noIrregularWhitespace'} MessageIds
|
|
21
21
|
*/
|
|
22
22
|
|
|
@@ -47,6 +47,13 @@ export default {
|
|
|
47
47
|
{
|
|
48
48
|
type: 'object',
|
|
49
49
|
properties: {
|
|
50
|
+
allow: {
|
|
51
|
+
type: 'array',
|
|
52
|
+
items: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
},
|
|
55
|
+
uniqueItems: true,
|
|
56
|
+
},
|
|
50
57
|
skipCode: {
|
|
51
58
|
type: 'boolean',
|
|
52
59
|
},
|
|
@@ -60,6 +67,7 @@ export default {
|
|
|
60
67
|
|
|
61
68
|
defaultOptions: [
|
|
62
69
|
{
|
|
70
|
+
allow: [],
|
|
63
71
|
skipCode: true,
|
|
64
72
|
skipInlineCode: true,
|
|
65
73
|
},
|
|
@@ -77,7 +85,7 @@ export default {
|
|
|
77
85
|
|
|
78
86
|
create(context) {
|
|
79
87
|
const { sourceCode } = context;
|
|
80
|
-
const [{ skipCode, skipInlineCode }] = context.options;
|
|
88
|
+
const [{ allow, skipCode, skipInlineCode }] = context.options;
|
|
81
89
|
|
|
82
90
|
const skipRanges = new SkipRanges();
|
|
83
91
|
|
|
@@ -96,6 +104,8 @@ export default {
|
|
|
96
104
|
for (const match of matches) {
|
|
97
105
|
const irregularWhitespace = match[0];
|
|
98
106
|
|
|
107
|
+
if (allow.includes(irregularWhitespace)) continue;
|
|
108
|
+
|
|
99
109
|
const startOffset = match.index;
|
|
100
110
|
const endOffset = startOffset + irregularWhitespace.length;
|
|
101
111
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Rule to disallow tab characters.
|
|
3
|
+
* @author 루밀LuMir(lumirlumir)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// --------------------------------------------------------------------------------
|
|
7
|
+
// Import
|
|
8
|
+
// --------------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
import { SkipRanges } from '../core/ast/index.js';
|
|
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 {[{ skipCode: boolean, skipInlineCode: boolean, tabWidth: number }]} RuleOptions
|
|
20
|
+
* @typedef {'noTab'} MessageIds
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
// --------------------------------------------------------------------------------
|
|
24
|
+
// Helper
|
|
25
|
+
// --------------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
const tabRegex = /\t/gu;
|
|
28
|
+
|
|
29
|
+
// --------------------------------------------------------------------------------
|
|
30
|
+
// Rule Definition
|
|
31
|
+
// --------------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/** @type {RuleModule<RuleOptions, MessageIds>} */
|
|
34
|
+
export default {
|
|
35
|
+
meta: {
|
|
36
|
+
type: 'problem',
|
|
37
|
+
|
|
38
|
+
docs: {
|
|
39
|
+
description: 'Disallow tab characters',
|
|
40
|
+
url: URL_RULE_DOCS('no-tab'),
|
|
41
|
+
recommended: false,
|
|
42
|
+
stylistic: true,
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
fixable: 'whitespace',
|
|
46
|
+
|
|
47
|
+
schema: [
|
|
48
|
+
{
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
skipCode: {
|
|
52
|
+
type: 'boolean',
|
|
53
|
+
},
|
|
54
|
+
skipInlineCode: {
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
},
|
|
57
|
+
tabWidth: {
|
|
58
|
+
type: 'integer',
|
|
59
|
+
minimum: 1,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
additionalProperties: false,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
|
|
66
|
+
defaultOptions: [
|
|
67
|
+
{
|
|
68
|
+
skipCode: true,
|
|
69
|
+
skipInlineCode: true,
|
|
70
|
+
tabWidth: 4,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
|
|
74
|
+
messages: {
|
|
75
|
+
noTab: 'Tab character is not allowed. Please use spaces instead.',
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
language: 'markdown',
|
|
79
|
+
|
|
80
|
+
dialects: ['commonmark', 'gfm'],
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
create(context) {
|
|
84
|
+
const { sourceCode } = context;
|
|
85
|
+
const [{ skipCode, skipInlineCode, tabWidth }] = context.options;
|
|
86
|
+
|
|
87
|
+
const skipRanges = new SkipRanges();
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
code(node) {
|
|
91
|
+
if (skipCode) skipRanges.push(sourceCode.getRange(node)); // Store range information of `Code`.
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
inlineCode(node) {
|
|
95
|
+
if (skipInlineCode) skipRanges.push(sourceCode.getRange(node)); // Store range information of `InlineCode`.
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
'root:exit'() {
|
|
99
|
+
const matches = sourceCode.text.matchAll(tabRegex);
|
|
100
|
+
|
|
101
|
+
for (const match of matches) {
|
|
102
|
+
const tab = match[0];
|
|
103
|
+
|
|
104
|
+
const startOffset = match.index;
|
|
105
|
+
const endOffset = startOffset + tab.length;
|
|
106
|
+
|
|
107
|
+
if (skipRanges.includes(startOffset)) continue;
|
|
108
|
+
|
|
109
|
+
context.report({
|
|
110
|
+
loc: {
|
|
111
|
+
start: sourceCode.getLocFromIndex(startOffset),
|
|
112
|
+
end: sourceCode.getLocFromIndex(endOffset),
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
messageId: 'noTab',
|
|
116
|
+
|
|
117
|
+
fix(fixer) {
|
|
118
|
+
return fixer.replaceTextRange(
|
|
119
|
+
[startOffset, endOffset],
|
|
120
|
+
' '.repeat(tabWidth),
|
|
121
|
+
);
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
};
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
declare const _default: {
|
|
2
|
-
meta: {
|
|
3
|
-
type: "problem";
|
|
4
|
-
docs: {
|
|
5
|
-
description: string;
|
|
6
|
-
url: string;
|
|
7
|
-
recommended: boolean;
|
|
8
|
-
stylistic: false;
|
|
9
|
-
};
|
|
10
|
-
messages: {
|
|
11
|
-
altText: string;
|
|
12
|
-
};
|
|
13
|
-
language: string;
|
|
14
|
-
dialects: string[];
|
|
15
|
-
};
|
|
16
|
-
create(context: import("@eslint/core").RuleContext<{
|
|
17
|
-
LangOptions: import("@eslint/markdown").MarkdownLanguageOptions;
|
|
18
|
-
Code: import("@eslint/markdown").MarkdownSourceCode;
|
|
19
|
-
RuleOptions: [];
|
|
20
|
-
Node: import("mdast").Node;
|
|
21
|
-
MessageIds: "altText";
|
|
22
|
-
}>): {
|
|
23
|
-
image(node: import("mdast").Image): void;
|
|
24
|
-
imageReference(node: import("mdast").ImageReference): void;
|
|
25
|
-
html(node: import("mdast").Html): void;
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
export default _default;
|
|
29
|
-
export type RuleOptions = [];
|
|
30
|
-
export type MessageIds = "altText";
|
package/src/rules/alt-text.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Rule to enforce the use of alternative text for images.
|
|
3
|
-
* @author 루밀LuMir(lumirlumir)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// --------------------------------------------------------------------------------
|
|
7
|
-
// Import
|
|
8
|
-
// --------------------------------------------------------------------------------
|
|
9
|
-
|
|
10
|
-
import { getElementsByTagName } from '../core/ast/index.js';
|
|
11
|
-
import { URL_RULE_DOCS } from '../core/constants.js';
|
|
12
|
-
|
|
13
|
-
// --------------------------------------------------------------------------------
|
|
14
|
-
// Typedefs
|
|
15
|
-
// --------------------------------------------------------------------------------
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @import { RuleModule } from '../core/types.js';
|
|
19
|
-
* @typedef {[]} RuleOptions
|
|
20
|
-
* @typedef {'altText'} MessageIds
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
// --------------------------------------------------------------------------------
|
|
24
|
-
// Rule Definition
|
|
25
|
-
// --------------------------------------------------------------------------------
|
|
26
|
-
|
|
27
|
-
/** @type {RuleModule<RuleOptions, MessageIds>} */
|
|
28
|
-
export default {
|
|
29
|
-
meta: {
|
|
30
|
-
type: 'problem',
|
|
31
|
-
|
|
32
|
-
docs: {
|
|
33
|
-
description: 'Enforce the use of alternative text for images',
|
|
34
|
-
url: URL_RULE_DOCS('alt-text'),
|
|
35
|
-
recommended: true,
|
|
36
|
-
stylistic: false,
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
messages: {
|
|
40
|
-
altText: 'Images should have an alternative text (alt text).',
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
language: 'markdown',
|
|
44
|
-
|
|
45
|
-
dialects: ['commonmark', 'gfm'],
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
create(context) {
|
|
49
|
-
return {
|
|
50
|
-
image(node) {
|
|
51
|
-
if (node.alt === '') {
|
|
52
|
-
context.report({
|
|
53
|
-
node,
|
|
54
|
-
messageId: 'altText',
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
imageReference(node) {
|
|
60
|
-
if (node.alt === '') {
|
|
61
|
-
context.report({
|
|
62
|
-
node,
|
|
63
|
-
messageId: 'altText',
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
html(node) {
|
|
69
|
-
getElementsByTagName(node.value, 'img').forEach(({ attrs }) => {
|
|
70
|
-
let hasAltText = false;
|
|
71
|
-
|
|
72
|
-
attrs.forEach(({ name, value }) => {
|
|
73
|
-
if (name === 'alt' && value) {
|
|
74
|
-
hasAltText = true;
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
if (!hasAltText) {
|
|
79
|
-
context.report({
|
|
80
|
-
node,
|
|
81
|
-
messageId: 'altText',
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
},
|
|
88
|
-
};
|