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 CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  [![lint](https://img.shields.io/github/actions/workflow/status/lumirlumir/npm-eslint-markdown/lint.yml?label=lint&color=6358d4&labelColor=333333&logo=github)](https://github.com/lumirlumir/npm-eslint-markdown/actions/workflows/lint.yml)
4
4
  [![test](https://img.shields.io/github/actions/workflow/status/lumirlumir/npm-eslint-markdown/test.yml?label=test&color=6358d4&labelColor=333333&logo=github)](https://github.com/lumirlumir/npm-eslint-markdown/actions/workflows/test.yml)
5
- [![codecov](https://img.shields.io/codecov/c/gh/lumirlumir/npm-eslint-markdown?token=2zUCHlMFT3&label=Codecov&color=6358d4&labelColor=333333&logo=codecov)](https://codecov.io/gh/lumirlumir/npm-eslint-markdown)
5
+ [![test-cross-platform](https://img.shields.io/github/actions/workflow/status/lumirlumir/npm-eslint-markdown/test-cross-platform.yml?label=test-cross-platform&color=6358d4&labelColor=333333&logo=github)](https://github.com/lumirlumir/npm-eslint-markdown/actions/workflows/test-cross-platform.yml)
6
+ [![codecov](https://img.shields.io/codecov/c/gh/lumirlumir/npm-eslint-markdown?token=LJMUst9eR3&label=Codecov&color=6358d4&labelColor=333333&logo=codecov)](https://codecov.io/gh/lumirlumir/npm-eslint-markdown)
7
+ ![Node Current](https://img.shields.io/node/v/eslint-markdown?color=6358d4&labelColor=333333&logo=node.js)
6
8
 
7
9
  [![npm package eslint-markdown latest version](https://img.shields.io/npm/v/eslint-markdown?label=eslint-markdown@latest&color=6358d4&labelColor=333333&logo=npm)](https://www.npmjs.com/package/eslint-markdown)
8
10
  [![npm package eslint-markdown next version](https://img.shields.io/npm/v/eslint-markdown/next?label=eslint-markdown@next&color=6358d4&labelColor=333333&logo=npm)](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).
@@ -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
- ignores: {
14
+ allow: {
15
15
  type: "array";
16
16
  items: {
17
- enum: string[];
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
- ignores: never[];
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
- ignores: string[];
52
+ allow: string[];
53
53
  override: Record<string, string>;
54
54
  }];
55
55
  export type MessageIds = "codeLangShorthand";
@@ -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.1.0-canary.12",
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.15.0"
67
+ "eslint": "^9.31.0 || ^10.0.0-beta.0"
68
68
  },
69
69
  "peerDependenciesMeta": {
70
70
  "eslint": {
@@ -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',
@@ -42,6 +42,7 @@ export default function stylistic(plugin) {
42
42
  'md/consistent-emphasis-style': 'error',
43
43
  'md/consistent-strong-style': 'error',
44
44
  'md/consistent-thematic-break-style': 'error',
45
+ 'md/no-tab': 'error',
45
46
  },
46
47
  });
47
48
  }
@@ -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
- // Typedefs
13
+ // Typedef
19
14
  // --------------------------------------------------------------------------------
20
15
 
21
16
  /**
22
17
  * @import { RuleModule } from '../core/types.js';
23
- * @typedef {[{ ignores: string[], override: Record<string, string> }]} RuleOptions
18
+ * @typedef {[{ allow: string[], override: Record<string, string> }]} RuleOptions
24
19
  * @typedef {'codeLangShorthand'} MessageIds
25
20
  */
26
21
 
27
22
  // --------------------------------------------------------------------------------
28
- // Helpers
23
+ // Helper
29
24
  // --------------------------------------------------------------------------------
30
25
 
31
- /** @satisfies {Record<string, string>} */
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
- ignores: {
130
+ allow: {
127
131
  type: 'array',
128
132
  items: {
129
- enum: Object.keys(langShorthandMap),
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
- ignores: [],
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
- const [{ ignores, override }] = context.options;
164
- const langShorthandMapMerged = Object.fromEntries(
165
- Object.entries({
166
- ...langShorthandMap,
167
- ...override, // `override` option handling.
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
- if (langShorthand === undefined) return;
187
+ // If the lang is in the allow list, skip it.
188
+ if (normalizedAllow.has(normalizedLang)) {
189
+ return;
190
+ }
175
191
 
176
- const match = context.sourceCode.getText(node).match(node.lang);
192
+ const langShorthand = mergedLangShorthandMap[normalizedLang];
177
193
 
178
- const matchIndexStart = match.index;
179
- const matchIndexEnd = matchIndexStart + match[0].length;
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
- line: node.position.start.line,
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
  },
@@ -1,17 +1,18 @@
1
1
  /* eslint sort-imports: 'error', sort-keys: 'error' */
2
2
 
3
- import allowHeading from './allow-heading.js';
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";
@@ -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
- };