eslint-markdown 0.16.0 → 0.17.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 +9 -9
- package/build/configs/all.d.ts +1 -0
- package/build/configs/all.js +1 -0
- package/build/core/types.d.ts +4 -0
- package/build/core/utils/escape-string-regexp.d.ts +2 -2
- package/build/core/utils/escape-string-regexp.js +2 -2
- package/build/core/utils/get-code-style.d.ts +13 -0
- package/build/core/utils/get-code-style.js +25 -0
- package/build/core/utils/html.d.ts +1 -1
- package/build/core/utils/html.js +1 -1
- package/build/core/utils/index.d.ts +7 -7
- package/build/core/utils/index.js +7 -7
- package/build/core/utils/is-blank-line.d.ts +1 -1
- package/build/core/utils/is-blank-line.js +1 -1
- package/build/core/utils/normalize-regex-pattern.d.ts +1 -1
- package/build/core/utils/normalize-regex-pattern.js +1 -1
- package/build/core/utils/skip-ranges.d.ts +2 -4
- package/build/core/utils/skip-ranges.js +1 -1
- package/build/core/utils/test-regex-stateless.d.ts +1 -1
- package/build/core/utils/test-regex-stateless.js +1 -1
- package/build/rules/consistent-code-style.d.ts +1 -2
- package/build/rules/consistent-code-style.js +2 -22
- package/build/rules/index.d.ts +66 -1
- package/build/rules/index.js +2 -0
- package/build/rules/no-irregular-dash.d.ts +36 -0
- package/build/rules/no-irregular-dash.js +50 -2
- package/build/rules/no-shell-dollar.d.ts +57 -0
- package/build/rules/no-shell-dollar.js +110 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# eslint-markdown
|
|
2
2
|
|
|
3
|
-
[](https://github.com/eslint-markdown/eslint-markdown/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/eslint-markdown/eslint-markdown/actions/workflows/test.yml)
|
|
5
|
+
[](https://codecov.io/gh/eslint-markdown/eslint-markdown)
|
|
6
6
|

|
|
7
7
|
|
|
8
8
|
[](https://www.npmjs.com/package/eslint-markdown)
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
> [!IMPORTANT]
|
|
13
13
|
>
|
|
14
|
-
> If you like this package, please give it a star on [GitHub](https://github.com/
|
|
14
|
+
> If you like this package, please give it a star on [GitHub](https://github.com/eslint-markdown/eslint-markdown)!⭐<br/>
|
|
15
15
|
> Your support helps us improve and maintain the project.
|
|
16
16
|
|
|
17
17
|
Lint your Markdown with ESLint. Additional rules for use with `@eslint/markdown`.🛠️
|
|
@@ -27,17 +27,17 @@ For full documentation, see the [official documentation of the `eslint-markdown`
|
|
|
27
27
|
|
|
28
28
|
## Compatibility
|
|
29
29
|
|
|
30
|
-
This [`eslint-markdown`](https://github.com/
|
|
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
31
|
|
|
32
32
|
So, we **highly recommend** using the `eslint-markdown` plugin alongside ESLint's built-in Markdown support, `@eslint/markdown`.
|
|
33
33
|
|
|
34
34
|
## Code of Conduct
|
|
35
35
|
|
|
36
|
-
See [Code of Conduct](https://github.com/
|
|
36
|
+
See [Code of Conduct](https://github.com/eslint-markdown/eslint-markdown/blob/main/CODE_OF_CONDUCT.md).
|
|
37
37
|
|
|
38
38
|
## Change Log
|
|
39
39
|
|
|
40
|
-
See [Change Log](https://github.com/
|
|
40
|
+
See [Change Log](https://github.com/eslint-markdown/eslint-markdown/blob/main/CHANGELOG.md).
|
|
41
41
|
|
|
42
42
|
## Versioning
|
|
43
43
|
|
|
@@ -45,8 +45,8 @@ See [Versioning](https://eslint-markdown.lumir.page/docs/get-started/versioning)
|
|
|
45
45
|
|
|
46
46
|
## Security
|
|
47
47
|
|
|
48
|
-
See [Security](https://github.com/
|
|
48
|
+
See [Security](https://github.com/eslint-markdown/eslint-markdown/blob/main/SECURITY.md).
|
|
49
49
|
|
|
50
50
|
## License
|
|
51
51
|
|
|
52
|
-
[MIT](https://github.com/
|
|
52
|
+
[MIT](https://github.com/eslint-markdown/eslint-markdown/blob/main/LICENSE.md)
|
package/build/configs/all.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export default function all(plugin: ESLint.Plugin): {
|
|
|
37
37
|
readonly 'md/no-git-conflict-marker': "error";
|
|
38
38
|
readonly 'md/no-irregular-dash': "error";
|
|
39
39
|
readonly 'md/no-irregular-whitespace': "error";
|
|
40
|
+
readonly 'md/no-shell-dollar': "error";
|
|
40
41
|
readonly 'md/no-tab': "error";
|
|
41
42
|
readonly 'md/no-trailing-heading-punctuation': "error";
|
|
42
43
|
readonly 'md/no-url-trailing-slash': "error";
|
package/build/configs/all.js
CHANGED
|
@@ -45,6 +45,7 @@ export default function all(plugin) {
|
|
|
45
45
|
'md/no-git-conflict-marker': 'error',
|
|
46
46
|
'md/no-irregular-dash': 'error',
|
|
47
47
|
'md/no-irregular-whitespace': 'error',
|
|
48
|
+
'md/no-shell-dollar': 'error',
|
|
48
49
|
'md/no-tab': 'error',
|
|
49
50
|
'md/no-trailing-heading-punctuation': 'error',
|
|
50
51
|
'md/no-url-trailing-slash': 'error',
|
package/build/core/types.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* @returns An escaped string.
|
|
12
12
|
* @example
|
|
13
13
|
* ```js
|
|
14
|
-
* import escapeStringRegexp from 'path/to/escape-string-regexp.js';
|
|
14
|
+
* import { escapeStringRegexp } from 'path/to/escape-string-regexp.js';
|
|
15
15
|
*
|
|
16
16
|
* const escapedString = escapeStringRegexp('How much $ for a 🦄?');
|
|
17
17
|
* //=> 'How much \\$ for a 🦄\\?'
|
|
@@ -19,4 +19,4 @@
|
|
|
19
19
|
* new RegExp(escapedString);
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
export
|
|
22
|
+
export declare function escapeStringRegexp(string: string): string;
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
* @returns An escaped string.
|
|
38
38
|
* @example
|
|
39
39
|
* ```js
|
|
40
|
-
* import escapeStringRegexp from 'path/to/escape-string-regexp.js';
|
|
40
|
+
* import { escapeStringRegexp } from 'path/to/escape-string-regexp.js';
|
|
41
41
|
*
|
|
42
42
|
* const escapedString = escapeStringRegexp('How much $ for a 🦄?');
|
|
43
43
|
* //=> 'How much \\$ for a 🦄\\?'
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
* new RegExp(escapedString);
|
|
46
46
|
* ```
|
|
47
47
|
*/
|
|
48
|
-
export
|
|
48
|
+
export function escapeStringRegexp(string) {
|
|
49
49
|
// Escape characters with special meaning either inside or outside character sets.
|
|
50
50
|
// Use a simple backslash escape when it's always valid, and a `\xnn` escape
|
|
51
51
|
// when the simpler form would be disallowed by Unicode patterns' stricter grammar.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Get the code style based on the given character.
|
|
3
|
+
* @see https://spec.commonmark.org/0.31.2/#fenced-code-blocks
|
|
4
|
+
* @see https://spec.commonmark.org/0.31.2/#indented-code-blocks
|
|
5
|
+
*/
|
|
6
|
+
export type CodeStyle = (typeof CODE_STYLE)[number];
|
|
7
|
+
export declare const CODE_STYLE: readonly ["indent", "fence-backtick", "fence-tilde"];
|
|
8
|
+
/**
|
|
9
|
+
* Get the code style based on the given character.
|
|
10
|
+
* @param char The character to determine the code style from. It must be a single character.
|
|
11
|
+
* @returns The code style.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getCodeStyle(char: string): CodeStyle;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Get the code style based on the given character.
|
|
3
|
+
* @see https://spec.commonmark.org/0.31.2/#fenced-code-blocks
|
|
4
|
+
* @see https://spec.commonmark.org/0.31.2/#indented-code-blocks
|
|
5
|
+
*/
|
|
6
|
+
// --------------------------------------------------------------------------------
|
|
7
|
+
// Export
|
|
8
|
+
// --------------------------------------------------------------------------------
|
|
9
|
+
export const CODE_STYLE = ['indent', 'fence-backtick', 'fence-tilde'];
|
|
10
|
+
/**
|
|
11
|
+
* Get the code style based on the given character.
|
|
12
|
+
* @param char The character to determine the code style from. It must be a single character.
|
|
13
|
+
* @returns The code style.
|
|
14
|
+
*/
|
|
15
|
+
export function getCodeStyle(char) {
|
|
16
|
+
if (char === '`') {
|
|
17
|
+
return 'fence-backtick';
|
|
18
|
+
}
|
|
19
|
+
else if (char === '~') {
|
|
20
|
+
return 'fence-tilde';
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return 'indent';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -8,4 +8,4 @@ import type { DefaultTreeAdapterTypes } from 'parse5';
|
|
|
8
8
|
* @param tagName The tag name to search for (case-insensitive).
|
|
9
9
|
* @returns Matched elements.
|
|
10
10
|
*/
|
|
11
|
-
export
|
|
11
|
+
export declare function getElementsByTagName(html: string, tagName: string): (DefaultTreeAdapterTypes.Element | DefaultTreeAdapterTypes.Template)[];
|
package/build/core/utils/html.js
CHANGED
|
@@ -14,7 +14,7 @@ import { parseFragment } from 'parse5';
|
|
|
14
14
|
* @param tagName The tag name to search for (case-insensitive).
|
|
15
15
|
* @returns Matched elements.
|
|
16
16
|
*/
|
|
17
|
-
export
|
|
17
|
+
export function getElementsByTagName(html, tagName) {
|
|
18
18
|
const ast = parseFragment(html, { sourceCodeLocationInfo: true });
|
|
19
19
|
const normalizedTagName = tagName.toLowerCase();
|
|
20
20
|
const nodes = [];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export
|
|
1
|
+
export * from './escape-string-regexp.js';
|
|
2
|
+
export * from './get-code-style.js';
|
|
3
|
+
export * from './html.js';
|
|
4
|
+
export * from './is-blank-line.js';
|
|
5
|
+
export * from './normalize-regex-pattern.js';
|
|
6
|
+
export * from './skip-ranges.js';
|
|
7
|
+
export * from './test-regex-stateless.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export
|
|
1
|
+
export * from './escape-string-regexp.js';
|
|
2
|
+
export * from './get-code-style.js';
|
|
3
|
+
export * from './html.js';
|
|
4
|
+
export * from './is-blank-line.js';
|
|
5
|
+
export * from './normalize-regex-pattern.js';
|
|
6
|
+
export * from './skip-ranges.js';
|
|
7
|
+
export * from './test-regex-stateless.js';
|
|
@@ -9,4 +9,4 @@
|
|
|
9
9
|
* @param blockquoteDepth The depth of blockquotes. Default is `-1`.
|
|
10
10
|
* @returns `true` if the line is blank. `false` otherwise.
|
|
11
11
|
*/
|
|
12
|
-
export
|
|
12
|
+
export declare function isBlankLine(str: string, blockquoteDepth?: number): boolean;
|
|
@@ -17,7 +17,7 @@ const blockquoteChar = '>';
|
|
|
17
17
|
* @param blockquoteDepth The depth of blockquotes. Default is `-1`.
|
|
18
18
|
* @returns `true` if the line is blank. `false` otherwise.
|
|
19
19
|
*/
|
|
20
|
-
export
|
|
20
|
+
export function isBlankLine(str, blockquoteDepth = -1) {
|
|
21
21
|
// `.length` is cached for performance.
|
|
22
22
|
const strLength = str.length;
|
|
23
23
|
let remainingBlockquotes = blockquoteDepth + 1;
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
* @param pattern `RegExp` or string pattern to normalize.
|
|
12
12
|
* @returns The normalized `RegExp`.
|
|
13
13
|
*/
|
|
14
|
-
export
|
|
14
|
+
export function normalizeRegexPattern(pattern) {
|
|
15
15
|
return typeof pattern === 'string' ? new RegExp(pattern, 'u') : pattern;
|
|
16
16
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Class to manage skip ranges.
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
5
|
-
type SourceRange = AST.Range;
|
|
4
|
+
import type { SourceRange } from '../types.js';
|
|
6
5
|
/**
|
|
7
6
|
* Class to manage skip ranges.
|
|
8
7
|
*/
|
|
9
|
-
export
|
|
8
|
+
export declare class SkipRanges {
|
|
10
9
|
#private;
|
|
11
10
|
/**
|
|
12
11
|
* Push a `range` to the set of skip ranges. (Method chaining supported.)
|
|
@@ -19,4 +18,3 @@ export default class SkipRanges {
|
|
|
19
18
|
*/
|
|
20
19
|
includes(offset: number): boolean;
|
|
21
20
|
}
|
|
22
|
-
export {};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* Class to manage skip ranges.
|
|
9
9
|
*/
|
|
10
|
-
export
|
|
10
|
+
export class SkipRanges {
|
|
11
11
|
// ------------------------------------------------------------------------------
|
|
12
12
|
// Private Property
|
|
13
13
|
// ------------------------------------------------------------------------------
|
|
@@ -14,7 +14,7 @@ const statefulRegexFlagRegex = /[gy]/u;
|
|
|
14
14
|
* @param text Text to test.
|
|
15
15
|
* @returns Whether the regex matches the text.
|
|
16
16
|
*/
|
|
17
|
-
export
|
|
17
|
+
export function testRegexStateless(regex, text) {
|
|
18
18
|
return statefulRegexFlagRegex.test(regex.flags)
|
|
19
19
|
? new RegExp(regex).test(text)
|
|
20
20
|
: regex.test(text);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @fileoverview Rule to enforce consistent code style.
|
|
3
3
|
* @author lumir(lumirlumir)
|
|
4
4
|
*/
|
|
5
|
-
type CodeStyle
|
|
5
|
+
import { type CodeStyle } from '../core/utils/index.js';
|
|
6
6
|
/**
|
|
7
7
|
* Options for the `consistent-code-style` rule.
|
|
8
8
|
*/
|
|
@@ -26,7 +26,6 @@ type RuleOptions = [
|
|
|
26
26
|
}
|
|
27
27
|
];
|
|
28
28
|
type MessageIds = 'style' | 'blankLineAbove' | 'blankLineBelow';
|
|
29
|
-
declare const CODE_STYLE: readonly ["indent", "fence-backtick", "fence-tilde"];
|
|
30
29
|
declare const _default: {
|
|
31
30
|
readonly meta: {
|
|
32
31
|
readonly type: "layout";
|
|
@@ -14,29 +14,9 @@
|
|
|
14
14
|
// --------------------------------------------------------------------------------
|
|
15
15
|
// Import
|
|
16
16
|
// --------------------------------------------------------------------------------
|
|
17
|
-
import { isBlankLine } from '../core/utils/index.js';
|
|
17
|
+
import { CODE_STYLE, getCodeStyle, isBlankLine, } from '../core/utils/index.js';
|
|
18
18
|
import { URL_RULE_DOCS } from '../core/constants.js';
|
|
19
19
|
// --------------------------------------------------------------------------------
|
|
20
|
-
// Helper
|
|
21
|
-
// --------------------------------------------------------------------------------
|
|
22
|
-
const CODE_STYLE = ['indent', 'fence-backtick', 'fence-tilde'];
|
|
23
|
-
/**
|
|
24
|
-
* Get the current code style based on the given text.
|
|
25
|
-
* @param text The text to determine the code style from.
|
|
26
|
-
* @returns The current code style.
|
|
27
|
-
*/
|
|
28
|
-
function getCurrentCodeStyle(text) {
|
|
29
|
-
if (text === '`') {
|
|
30
|
-
return 'fence-backtick';
|
|
31
|
-
}
|
|
32
|
-
else if (text === '~') {
|
|
33
|
-
return 'fence-tilde';
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
return 'indent';
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
// --------------------------------------------------------------------------------
|
|
40
20
|
// Rule Definition
|
|
41
21
|
// --------------------------------------------------------------------------------
|
|
42
22
|
export default {
|
|
@@ -116,7 +96,7 @@ export default {
|
|
|
116
96
|
const { start, end } = sourceCode.getLoc(node);
|
|
117
97
|
const [nodeStartOffset] = sourceCode.getRange(node);
|
|
118
98
|
const currentCodeFenceChar = sourceCode.text[nodeStartOffset];
|
|
119
|
-
const currentCodeStyle =
|
|
99
|
+
const currentCodeStyle = getCodeStyle(currentCodeFenceChar);
|
|
120
100
|
const nodeStartLineIndex = start.line - 1;
|
|
121
101
|
const nodeEndLineIndex = end.line - 1;
|
|
122
102
|
if (codeStyle === null) {
|
package/build/rules/index.d.ts
CHANGED
|
@@ -481,7 +481,7 @@ declare const _default: {
|
|
|
481
481
|
};
|
|
482
482
|
readonly create: (context: import("../core/types.js").RuleContext<{
|
|
483
483
|
RuleOptions: [{
|
|
484
|
-
style: "consistent" | ("
|
|
484
|
+
style: "consistent" | import("../core/utils/get-code-style.js").CodeStyle;
|
|
485
485
|
blankLineAbove: number | false;
|
|
486
486
|
blankLineBelow: number | false;
|
|
487
487
|
}];
|
|
@@ -1098,6 +1098,7 @@ declare const _default: {
|
|
|
1098
1098
|
readonly recommended: true;
|
|
1099
1099
|
readonly stylistic: false;
|
|
1100
1100
|
};
|
|
1101
|
+
readonly fixable: "code";
|
|
1101
1102
|
readonly schema: [{
|
|
1102
1103
|
readonly type: "object";
|
|
1103
1104
|
readonly properties: {
|
|
@@ -1108,6 +1109,15 @@ declare const _default: {
|
|
|
1108
1109
|
};
|
|
1109
1110
|
readonly uniqueItems: true;
|
|
1110
1111
|
};
|
|
1112
|
+
readonly override: {
|
|
1113
|
+
readonly type: "object";
|
|
1114
|
+
readonly properties: {
|
|
1115
|
+
[k: string]: {
|
|
1116
|
+
type: "string";
|
|
1117
|
+
};
|
|
1118
|
+
};
|
|
1119
|
+
readonly additionalProperties: false;
|
|
1120
|
+
};
|
|
1111
1121
|
readonly skipCode: {
|
|
1112
1122
|
readonly oneOf: [{
|
|
1113
1123
|
readonly type: "boolean";
|
|
@@ -1122,13 +1132,22 @@ declare const _default: {
|
|
|
1122
1132
|
readonly skipInlineCode: {
|
|
1123
1133
|
readonly type: "boolean";
|
|
1124
1134
|
};
|
|
1135
|
+
readonly skipMath: {
|
|
1136
|
+
readonly type: "boolean";
|
|
1137
|
+
};
|
|
1138
|
+
readonly skipInlineMath: {
|
|
1139
|
+
readonly type: "boolean";
|
|
1140
|
+
};
|
|
1125
1141
|
};
|
|
1126
1142
|
readonly additionalProperties: false;
|
|
1127
1143
|
}];
|
|
1128
1144
|
readonly defaultOptions: [{
|
|
1129
1145
|
readonly allow: [];
|
|
1146
|
+
readonly override: {};
|
|
1130
1147
|
readonly skipCode: true;
|
|
1131
1148
|
readonly skipInlineCode: true;
|
|
1149
|
+
readonly skipMath: true;
|
|
1150
|
+
readonly skipInlineMath: true;
|
|
1132
1151
|
}];
|
|
1133
1152
|
readonly messages: {
|
|
1134
1153
|
readonly noIrregularDash: "Irregular dash `{{ irregularDash }}` is not allowed.";
|
|
@@ -1139,13 +1158,18 @@ declare const _default: {
|
|
|
1139
1158
|
readonly create: (context: import("../core/types.js").RuleContext<{
|
|
1140
1159
|
RuleOptions: [{
|
|
1141
1160
|
allow: string[];
|
|
1161
|
+
override: Record<string, string>;
|
|
1142
1162
|
skipCode: boolean | string[];
|
|
1143
1163
|
skipInlineCode: boolean;
|
|
1164
|
+
skipMath: boolean;
|
|
1165
|
+
skipInlineMath: boolean;
|
|
1144
1166
|
}];
|
|
1145
1167
|
MessageIds: "noIrregularDash";
|
|
1146
1168
|
}>) => {
|
|
1147
1169
|
code(node: import("mdast").Code): void;
|
|
1148
1170
|
inlineCode(node: import("mdast").InlineCode): void;
|
|
1171
|
+
math(node: import("mdast-util-math").Math): void;
|
|
1172
|
+
inlineMath(node: import("mdast-util-math").InlineMath): void;
|
|
1149
1173
|
'root:exit'(): void;
|
|
1150
1174
|
};
|
|
1151
1175
|
};
|
|
@@ -1221,6 +1245,47 @@ declare const _default: {
|
|
|
1221
1245
|
'root:exit'(): void;
|
|
1222
1246
|
};
|
|
1223
1247
|
};
|
|
1248
|
+
'no-shell-dollar': {
|
|
1249
|
+
readonly meta: {
|
|
1250
|
+
readonly type: "problem";
|
|
1251
|
+
readonly docs: {
|
|
1252
|
+
readonly description: "Disallow dollar signs before commands without showing output";
|
|
1253
|
+
readonly url: string;
|
|
1254
|
+
readonly recommended: false;
|
|
1255
|
+
readonly stylistic: false;
|
|
1256
|
+
};
|
|
1257
|
+
readonly fixable: "code";
|
|
1258
|
+
readonly schema: [{
|
|
1259
|
+
readonly type: "object";
|
|
1260
|
+
readonly properties: {
|
|
1261
|
+
readonly skipCode: {
|
|
1262
|
+
readonly type: "array";
|
|
1263
|
+
readonly items: {
|
|
1264
|
+
readonly type: "string";
|
|
1265
|
+
};
|
|
1266
|
+
readonly uniqueItems: true;
|
|
1267
|
+
};
|
|
1268
|
+
};
|
|
1269
|
+
readonly additionalProperties: false;
|
|
1270
|
+
}];
|
|
1271
|
+
readonly defaultOptions: [{
|
|
1272
|
+
readonly skipCode: [];
|
|
1273
|
+
}];
|
|
1274
|
+
readonly messages: {
|
|
1275
|
+
readonly noShellDollar: "Dollar sign should not be used before commands without showing output.";
|
|
1276
|
+
};
|
|
1277
|
+
readonly language: "markdown";
|
|
1278
|
+
readonly dialects: ["commonmark", "gfm"];
|
|
1279
|
+
};
|
|
1280
|
+
readonly create: (context: import("../core/types.js").RuleContext<{
|
|
1281
|
+
RuleOptions: [{
|
|
1282
|
+
skipCode: string[];
|
|
1283
|
+
}];
|
|
1284
|
+
MessageIds: "noShellDollar";
|
|
1285
|
+
}>) => {
|
|
1286
|
+
code(node: import("mdast").Code): void;
|
|
1287
|
+
};
|
|
1288
|
+
};
|
|
1224
1289
|
'no-tab': {
|
|
1225
1290
|
readonly meta: {
|
|
1226
1291
|
readonly type: "problem";
|
package/build/rules/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import noEmoji from './no-emoji.js';
|
|
|
22
22
|
import noGitConflictMarker from './no-git-conflict-marker.js';
|
|
23
23
|
import noIrregularDash from './no-irregular-dash.js';
|
|
24
24
|
import noIrregularWhitespace from './no-irregular-whitespace.js';
|
|
25
|
+
import noShellDollar from './no-shell-dollar.js';
|
|
25
26
|
import noTab from './no-tab.js';
|
|
26
27
|
import noTrailingHeadingPunctuation from './no-trailing-heading-punctuation.js';
|
|
27
28
|
import noUrlTrailingSlash from './no-url-trailing-slash.js';
|
|
@@ -51,6 +52,7 @@ export default {
|
|
|
51
52
|
'no-git-conflict-marker': noGitConflictMarker,
|
|
52
53
|
'no-irregular-dash': noIrregularDash,
|
|
53
54
|
'no-irregular-whitespace': noIrregularWhitespace,
|
|
55
|
+
'no-shell-dollar': noShellDollar,
|
|
54
56
|
'no-tab': noTab,
|
|
55
57
|
'no-trailing-heading-punctuation': noTrailingHeadingPunctuation,
|
|
56
58
|
'no-url-trailing-slash': noUrlTrailingSlash,
|
|
@@ -14,6 +14,11 @@ type RuleOptions = [
|
|
|
14
14
|
* @default []
|
|
15
15
|
*/
|
|
16
16
|
allow: string[];
|
|
17
|
+
/**
|
|
18
|
+
* An object where the **key** is an irregular dash character and the **value** is the string that replaces it.
|
|
19
|
+
* @default {}
|
|
20
|
+
*/
|
|
21
|
+
override: Record<string, string>;
|
|
17
22
|
/**
|
|
18
23
|
* `true` allows irregular dashes in all code blocks, while `string[]` allows irregular dashes only in code blocks for the specified languages.
|
|
19
24
|
* @default true
|
|
@@ -24,6 +29,16 @@ type RuleOptions = [
|
|
|
24
29
|
* @default true
|
|
25
30
|
*/
|
|
26
31
|
skipInlineCode: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* `true` allows irregular dashes in all math blocks.
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
skipMath: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* `true` allows irregular dashes in all inline math.
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
skipInlineMath: boolean;
|
|
27
42
|
}
|
|
28
43
|
];
|
|
29
44
|
declare const _default: {
|
|
@@ -35,6 +50,7 @@ declare const _default: {
|
|
|
35
50
|
readonly recommended: true;
|
|
36
51
|
readonly stylistic: false;
|
|
37
52
|
};
|
|
53
|
+
readonly fixable: "code";
|
|
38
54
|
readonly schema: [{
|
|
39
55
|
readonly type: "object";
|
|
40
56
|
readonly properties: {
|
|
@@ -45,6 +61,15 @@ declare const _default: {
|
|
|
45
61
|
};
|
|
46
62
|
readonly uniqueItems: true;
|
|
47
63
|
};
|
|
64
|
+
readonly override: {
|
|
65
|
+
readonly type: "object";
|
|
66
|
+
readonly properties: {
|
|
67
|
+
[k: string]: {
|
|
68
|
+
type: "string";
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
readonly additionalProperties: false;
|
|
72
|
+
};
|
|
48
73
|
readonly skipCode: {
|
|
49
74
|
readonly oneOf: [{
|
|
50
75
|
readonly type: "boolean";
|
|
@@ -59,13 +84,22 @@ declare const _default: {
|
|
|
59
84
|
readonly skipInlineCode: {
|
|
60
85
|
readonly type: "boolean";
|
|
61
86
|
};
|
|
87
|
+
readonly skipMath: {
|
|
88
|
+
readonly type: "boolean";
|
|
89
|
+
};
|
|
90
|
+
readonly skipInlineMath: {
|
|
91
|
+
readonly type: "boolean";
|
|
92
|
+
};
|
|
62
93
|
};
|
|
63
94
|
readonly additionalProperties: false;
|
|
64
95
|
}];
|
|
65
96
|
readonly defaultOptions: [{
|
|
66
97
|
readonly allow: [];
|
|
98
|
+
readonly override: {};
|
|
67
99
|
readonly skipCode: true;
|
|
68
100
|
readonly skipInlineCode: true;
|
|
101
|
+
readonly skipMath: true;
|
|
102
|
+
readonly skipInlineMath: true;
|
|
69
103
|
}];
|
|
70
104
|
readonly messages: {
|
|
71
105
|
readonly noIrregularDash: "Irregular dash `{{ irregularDash }}` is not allowed.";
|
|
@@ -79,6 +113,8 @@ declare const _default: {
|
|
|
79
113
|
}>) => {
|
|
80
114
|
code(node: import("mdast").Code): void;
|
|
81
115
|
inlineCode(node: import("mdast").InlineCode): void;
|
|
116
|
+
math(node: import("mdast-util-math").Math): void;
|
|
117
|
+
inlineMath(node: import("mdast-util-math").InlineMath): void;
|
|
82
118
|
'root:exit'(): void;
|
|
83
119
|
};
|
|
84
120
|
};
|
|
@@ -10,7 +10,25 @@ import { URL_RULE_DOCS } from '../core/constants.js';
|
|
|
10
10
|
// --------------------------------------------------------------------------------
|
|
11
11
|
// Helper
|
|
12
12
|
// --------------------------------------------------------------------------------
|
|
13
|
-
const
|
|
13
|
+
const irregularDashes = [
|
|
14
|
+
'\u2010',
|
|
15
|
+
'\u2011',
|
|
16
|
+
'\u2012',
|
|
17
|
+
'\u2013',
|
|
18
|
+
'\u2014',
|
|
19
|
+
'\u2015',
|
|
20
|
+
'\u2043',
|
|
21
|
+
'\u2212',
|
|
22
|
+
'\u23af',
|
|
23
|
+
'\u2e3a',
|
|
24
|
+
'\u2e3b',
|
|
25
|
+
'\u30fc',
|
|
26
|
+
'\ufe58',
|
|
27
|
+
'\ufe63',
|
|
28
|
+
'\uff0d',
|
|
29
|
+
];
|
|
30
|
+
const irregularDashMap = Object.freeze(Object.fromEntries(irregularDashes.map(irregularDash => [irregularDash, '-'])));
|
|
31
|
+
const irregularDashRegex = new RegExp(`[${irregularDashes.join('')}]`, 'gu');
|
|
14
32
|
// --------------------------------------------------------------------------------
|
|
15
33
|
// Rule Definition
|
|
16
34
|
// --------------------------------------------------------------------------------
|
|
@@ -23,6 +41,7 @@ export default {
|
|
|
23
41
|
recommended: true,
|
|
24
42
|
stylistic: false,
|
|
25
43
|
},
|
|
44
|
+
fixable: 'code',
|
|
26
45
|
schema: [
|
|
27
46
|
{
|
|
28
47
|
type: 'object',
|
|
@@ -34,6 +53,11 @@ export default {
|
|
|
34
53
|
},
|
|
35
54
|
uniqueItems: true,
|
|
36
55
|
},
|
|
56
|
+
override: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: Object.fromEntries(irregularDashes.map(irregularDash => [irregularDash, { type: 'string' }])),
|
|
59
|
+
additionalProperties: false,
|
|
60
|
+
},
|
|
37
61
|
skipCode: {
|
|
38
62
|
oneOf: [
|
|
39
63
|
{
|
|
@@ -51,6 +75,12 @@ export default {
|
|
|
51
75
|
skipInlineCode: {
|
|
52
76
|
type: 'boolean',
|
|
53
77
|
},
|
|
78
|
+
skipMath: {
|
|
79
|
+
type: 'boolean',
|
|
80
|
+
},
|
|
81
|
+
skipInlineMath: {
|
|
82
|
+
type: 'boolean',
|
|
83
|
+
},
|
|
54
84
|
},
|
|
55
85
|
additionalProperties: false,
|
|
56
86
|
},
|
|
@@ -58,8 +88,11 @@ export default {
|
|
|
58
88
|
defaultOptions: [
|
|
59
89
|
{
|
|
60
90
|
allow: [],
|
|
91
|
+
override: {},
|
|
61
92
|
skipCode: true,
|
|
62
93
|
skipInlineCode: true,
|
|
94
|
+
skipMath: true,
|
|
95
|
+
skipInlineMath: true,
|
|
63
96
|
},
|
|
64
97
|
],
|
|
65
98
|
messages: {
|
|
@@ -70,8 +103,12 @@ export default {
|
|
|
70
103
|
},
|
|
71
104
|
create(context) {
|
|
72
105
|
const { sourceCode } = context;
|
|
73
|
-
const [{ allow, skipCode, skipInlineCode }] = context.options;
|
|
106
|
+
const [{ allow, override, skipCode, skipInlineCode, skipMath, skipInlineMath }] = context.options;
|
|
74
107
|
const skipRanges = new SkipRanges();
|
|
108
|
+
const mergedIrregularDashMap = {
|
|
109
|
+
...irregularDashMap,
|
|
110
|
+
...override,
|
|
111
|
+
};
|
|
75
112
|
return {
|
|
76
113
|
code(node) {
|
|
77
114
|
if (Array.isArray(skipCode) ? node.lang && skipCode.includes(node.lang) : skipCode)
|
|
@@ -81,6 +118,14 @@ export default {
|
|
|
81
118
|
if (skipInlineCode)
|
|
82
119
|
skipRanges.push(sourceCode.getRange(node)); // Store range information of `InlineCode`.
|
|
83
120
|
},
|
|
121
|
+
math(node) {
|
|
122
|
+
if (skipMath)
|
|
123
|
+
skipRanges.push(sourceCode.getRange(node)); // Store range information of `Math`.
|
|
124
|
+
},
|
|
125
|
+
inlineMath(node) {
|
|
126
|
+
if (skipInlineMath)
|
|
127
|
+
skipRanges.push(sourceCode.getRange(node)); // Store range information of `InlineMath`.
|
|
128
|
+
},
|
|
84
129
|
'root:exit'() {
|
|
85
130
|
const matches = sourceCode.text.matchAll(irregularDashRegex);
|
|
86
131
|
for (const match of matches) {
|
|
@@ -100,6 +145,9 @@ export default {
|
|
|
100
145
|
irregularDash: `U+${irregularDash.codePointAt(0)?.toString(16).toUpperCase().padStart(4, '0')}`,
|
|
101
146
|
},
|
|
102
147
|
messageId: 'noIrregularDash',
|
|
148
|
+
fix(fixer) {
|
|
149
|
+
return fixer.replaceTextRange([startOffset, endOffset], mergedIrregularDashMap[irregularDash]);
|
|
150
|
+
},
|
|
103
151
|
});
|
|
104
152
|
}
|
|
105
153
|
},
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Rule to disallow dollar signs before commands without showing output.
|
|
3
|
+
* @author Marry(uncoolclub)
|
|
4
|
+
* @see https://github.com/DavidAnson/markdownlint/blob/v0.41.1/lib/md014.mjs
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Options for the `no-shell-dollar` rule.
|
|
8
|
+
*/
|
|
9
|
+
type RuleOptions = [
|
|
10
|
+
{
|
|
11
|
+
/**
|
|
12
|
+
* An array of code block language identifiers to skip.
|
|
13
|
+
* @default []
|
|
14
|
+
*/
|
|
15
|
+
skipCode: string[];
|
|
16
|
+
}
|
|
17
|
+
];
|
|
18
|
+
declare const _default: {
|
|
19
|
+
readonly meta: {
|
|
20
|
+
readonly type: "problem";
|
|
21
|
+
readonly docs: {
|
|
22
|
+
readonly description: "Disallow dollar signs before commands without showing output";
|
|
23
|
+
readonly url: string;
|
|
24
|
+
readonly recommended: false;
|
|
25
|
+
readonly stylistic: false;
|
|
26
|
+
};
|
|
27
|
+
readonly fixable: "code";
|
|
28
|
+
readonly schema: [{
|
|
29
|
+
readonly type: "object";
|
|
30
|
+
readonly properties: {
|
|
31
|
+
readonly skipCode: {
|
|
32
|
+
readonly type: "array";
|
|
33
|
+
readonly items: {
|
|
34
|
+
readonly type: "string";
|
|
35
|
+
};
|
|
36
|
+
readonly uniqueItems: true;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
readonly additionalProperties: false;
|
|
40
|
+
}];
|
|
41
|
+
readonly defaultOptions: [{
|
|
42
|
+
readonly skipCode: [];
|
|
43
|
+
}];
|
|
44
|
+
readonly messages: {
|
|
45
|
+
readonly noShellDollar: "Dollar sign should not be used before commands without showing output.";
|
|
46
|
+
};
|
|
47
|
+
readonly language: "markdown";
|
|
48
|
+
readonly dialects: ["commonmark", "gfm"];
|
|
49
|
+
};
|
|
50
|
+
readonly create: (context: import("../core/types.js").RuleContext<{
|
|
51
|
+
RuleOptions: RuleOptions;
|
|
52
|
+
MessageIds: "noShellDollar";
|
|
53
|
+
}>) => {
|
|
54
|
+
code(node: import("mdast").Code): void;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
export default _default;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Rule to disallow dollar signs before commands without showing output.
|
|
3
|
+
* @author Marry(uncoolclub)
|
|
4
|
+
* @see https://github.com/DavidAnson/markdownlint/blob/v0.41.1/lib/md014.mjs
|
|
5
|
+
*/
|
|
6
|
+
// --------------------------------------------------------------------------------
|
|
7
|
+
// Import
|
|
8
|
+
// --------------------------------------------------------------------------------
|
|
9
|
+
import { getCodeStyle, isBlankLine } from '../core/utils/index.js';
|
|
10
|
+
import { URL_RULE_DOCS } from '../core/constants.js';
|
|
11
|
+
// --------------------------------------------------------------------------------
|
|
12
|
+
// Helper
|
|
13
|
+
// --------------------------------------------------------------------------------
|
|
14
|
+
const dollarCommandRegex = /^[ \t]*\$[ \t]+/u;
|
|
15
|
+
const promptRegex = /\$[ \t]+/u;
|
|
16
|
+
const trailingBackslashRegex = /\\+$/u;
|
|
17
|
+
const lineEndingRegex = /\r\n|[\r\n]/u;
|
|
18
|
+
// --------------------------------------------------------------------------------
|
|
19
|
+
// Rule Definition
|
|
20
|
+
// --------------------------------------------------------------------------------
|
|
21
|
+
export default {
|
|
22
|
+
meta: {
|
|
23
|
+
type: 'problem',
|
|
24
|
+
docs: {
|
|
25
|
+
description: 'Disallow dollar signs before commands without showing output',
|
|
26
|
+
url: URL_RULE_DOCS('no-shell-dollar'),
|
|
27
|
+
recommended: false,
|
|
28
|
+
stylistic: false,
|
|
29
|
+
},
|
|
30
|
+
fixable: 'code',
|
|
31
|
+
schema: [
|
|
32
|
+
{
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
skipCode: {
|
|
36
|
+
type: 'array',
|
|
37
|
+
items: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
},
|
|
40
|
+
uniqueItems: true,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
defaultOptions: [
|
|
47
|
+
{
|
|
48
|
+
skipCode: [],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
messages: {
|
|
52
|
+
noShellDollar: 'Dollar sign should not be used before commands without showing output.',
|
|
53
|
+
},
|
|
54
|
+
language: 'markdown',
|
|
55
|
+
dialects: ['commonmark', 'gfm'],
|
|
56
|
+
},
|
|
57
|
+
create(context) {
|
|
58
|
+
const { sourceCode, sourceCode: { lines }, } = context;
|
|
59
|
+
const [{ skipCode }] = context.options;
|
|
60
|
+
return {
|
|
61
|
+
code(node) {
|
|
62
|
+
if (node.lang && skipCode.includes(node.lang)) {
|
|
63
|
+
// Early return if the code block's language is in the skip list.
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const { start } = sourceCode.getLoc(node);
|
|
67
|
+
const promptRanges = [];
|
|
68
|
+
const firstCodeLine = // A fenced code block starts its content on the second line, so its opening fence is skipped.
|
|
69
|
+
getCodeStyle(lines[start.line - 1][start.column - 1]) === 'indent'
|
|
70
|
+
? start.line
|
|
71
|
+
: start.line + 1;
|
|
72
|
+
let isPreviousLineContinues = false;
|
|
73
|
+
for (const [index, codeLine] of node.value.split(lineEndingRegex).entries()) {
|
|
74
|
+
if (isBlankLine(codeLine)) {
|
|
75
|
+
isPreviousLineContinues = false;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (!isPreviousLineContinues) {
|
|
79
|
+
if (!dollarCommandRegex.test(codeLine)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// `Code#value` drops container markers and expands partial tabs, so find the prompt in the original line.
|
|
83
|
+
const line = firstCodeLine + index;
|
|
84
|
+
const match = promptRegex.exec(lines[line - 1]); // `dollarCommandRegex` match guarantees this will succeed.
|
|
85
|
+
const startOffset = sourceCode.getIndexFromLoc({
|
|
86
|
+
line,
|
|
87
|
+
column: match.index + 1,
|
|
88
|
+
});
|
|
89
|
+
const endOffset = startOffset + match[0].length;
|
|
90
|
+
promptRanges.push([startOffset, endOffset]);
|
|
91
|
+
}
|
|
92
|
+
isPreviousLineContinues =
|
|
93
|
+
(trailingBackslashRegex.exec(codeLine)?.[0].length ?? 0) % 2 === 1;
|
|
94
|
+
}
|
|
95
|
+
for (const [startOffset, endOffset] of promptRanges) {
|
|
96
|
+
context.report({
|
|
97
|
+
loc: {
|
|
98
|
+
start: sourceCode.getLocFromIndex(startOffset),
|
|
99
|
+
end: sourceCode.getLocFromIndex(endOffset),
|
|
100
|
+
},
|
|
101
|
+
messageId: 'noShellDollar',
|
|
102
|
+
fix(fixer) {
|
|
103
|
+
return fixer.removeRange([startOffset, endOffset]);
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-markdown",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Lint your Markdown with ESLint. Additional rules for use with `@eslint/markdown`.🛠️",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"homepage": "https://eslint-markdown.lumir.page",
|
|
44
44
|
"repository": {
|
|
45
45
|
"type": "github",
|
|
46
|
-
"url": "git+https://github.com/
|
|
46
|
+
"url": "git+https://github.com/eslint-markdown/eslint-markdown.git",
|
|
47
47
|
"directory": "packages/eslint-markdown"
|
|
48
48
|
},
|
|
49
49
|
"bugs": {
|
|
50
|
-
"url": "https://github.com/
|
|
50
|
+
"url": "https://github.com/eslint-markdown/eslint-markdown/issues"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": "^20.19.0 || ^22.13.0 || >=24.0.0"
|