eslint-markdown 0.1.0 → 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.
@@ -28,6 +28,7 @@ export default function all(plugin: ESLint.Plugin): {
28
28
  readonly 'md/no-git-conflict-marker': "error";
29
29
  readonly 'md/no-irregular-dash': "error";
30
30
  readonly 'md/no-irregular-whitespace': "error";
31
+ readonly 'md/no-tab': "error";
31
32
  readonly 'md/no-url-trailing-slash': "error";
32
33
  readonly 'md/require-image-title': "error";
33
34
  readonly 'md/require-link-title': "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";
@@ -13,6 +13,7 @@ declare const _default: {
13
13
  'no-git-conflict-marker': import("../core/types.js").RuleModule<import("./no-git-conflict-marker.js").RuleOptions, "noGitConflictMarker">;
14
14
  'no-irregular-dash': import("../core/types.js").RuleModule<import("./no-irregular-dash.js").RuleOptions, "noIrregularDash">;
15
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">;
16
17
  'no-url-trailing-slash': import("../core/types.js").RuleModule<[], "noUrlTrailingSlash">;
17
18
  'require-image-title': import("../core/types.js").RuleModule<import("./require-image-title.js").RuleOptions, "requireImageTitle">;
18
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",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Lint your Markdown with ESLint.🛠️",
6
6
  "exports": {
@@ -52,6 +52,7 @@ export default function all(plugin) {
52
52
  'md/no-git-conflict-marker': 'error',
53
53
  'md/no-irregular-dash': 'error',
54
54
  'md/no-irregular-whitespace': 'error',
55
+ 'md/no-tab': 'error',
55
56
  'md/no-url-trailing-slash': 'error',
56
57
  'md/require-image-title': 'error',
57
58
  'md/require-link-title': '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
  }
@@ -20,6 +20,7 @@ import noEmoji from './no-emoji.js';
20
20
  import noGitConflictMarker from './no-git-conflict-marker.js';
21
21
  import noIrregularDash from './no-irregular-dash.js';
22
22
  import noIrregularWhitespace from './no-irregular-whitespace.js';
23
+ import noTab from './no-tab.js';
23
24
  import noUrlTrailingSlash from './no-url-trailing-slash.js';
24
25
  import requireImageTitle from './require-image-title.js';
25
26
  import requireLinkTitle from './require-link-title.js';
@@ -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
+ };