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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # eslint-markdown
2
2
 
3
- [![ci](https://img.shields.io/github/actions/workflow/status/lumirlumir/npm-eslint-markdown/ci.yml?label=ci&color=6358d4&labelColor=333333&logo=github)](https://github.com/lumirlumir/npm-eslint-markdown/actions/workflows/ci.yml)
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=LJMUst9eR3&label=Codecov&color=6358d4&labelColor=333333&logo=codecov)](https://codecov.io/gh/lumirlumir/npm-eslint-markdown)
3
+ [![ci](https://img.shields.io/github/actions/workflow/status/eslint-markdown/eslint-markdown/ci.yml?label=ci&color=6358d4&labelColor=333333&logo=github)](https://github.com/eslint-markdown/eslint-markdown/actions/workflows/ci.yml)
4
+ [![test](https://img.shields.io/github/actions/workflow/status/eslint-markdown/eslint-markdown/test.yml?label=test&color=6358d4&labelColor=333333&logo=github)](https://github.com/eslint-markdown/eslint-markdown/actions/workflows/test.yml)
5
+ [![codecov](https://img.shields.io/codecov/c/gh/eslint-markdown/eslint-markdown?token=LJMUst9eR3&label=Codecov&color=6358d4&labelColor=333333&logo=codecov)](https://codecov.io/gh/eslint-markdown/eslint-markdown)
6
6
  ![Node Current](https://img.shields.io/node/v/eslint-markdown?color=6358d4&labelColor=333333&logo=node.js)
7
7
 
8
8
  [![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)
@@ -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/lumirlumir/npm-eslint-markdown)!⭐<br/>
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/lumirlumir/npm-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).
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/lumirlumir/.github/blob/main/CODE_OF_CONDUCT.md#contributor-covenant-code-of-conduct).
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/lumirlumir/npm-eslint-markdown/blob/main/CHANGELOG.md).
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/lumirlumir/.github/blob/main/SECURITY.md#security).
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/lumirlumir/npm-eslint-markdown/blob/main/LICENSE.md)
52
+ [MIT](https://github.com/eslint-markdown/eslint-markdown/blob/main/LICENSE.md)
@@ -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";
@@ -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',
@@ -29,3 +29,7 @@ export interface RuleModule<RuleOptions extends MarkdownRuleDefinitionTypeOption
29
29
  MessageIds: MessageIds;
30
30
  }>): MarkdownRuleVisitor;
31
31
  }
32
+ /**
33
+ * Represents a range of characters in the source.
34
+ */
35
+ export type SourceRange = [number, number];
@@ -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 default function escapeStringRegexp(string: string): string;
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 default function escapeStringRegexp(string) {
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 default function getElementsByTagName(html: string, tagName: string): (DefaultTreeAdapterTypes.Element | DefaultTreeAdapterTypes.Template)[];
11
+ export declare function getElementsByTagName(html: string, tagName: string): (DefaultTreeAdapterTypes.Element | DefaultTreeAdapterTypes.Template)[];
@@ -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 default function getElementsByTagName(html, tagName) {
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
- import escapeStringRegexp from './escape-string-regexp.js';
2
- import getElementsByTagName from './html.js';
3
- import isBlankLine from './is-blank-line.js';
4
- import normalizeRegexPattern from './normalize-regex-pattern.js';
5
- import SkipRanges from './skip-ranges.js';
6
- import testRegexStateless from './test-regex-stateless.js';
7
- export { escapeStringRegexp, getElementsByTagName, isBlankLine, normalizeRegexPattern, SkipRanges, testRegexStateless, };
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
- import escapeStringRegexp from './escape-string-regexp.js';
2
- import getElementsByTagName from './html.js';
3
- import isBlankLine from './is-blank-line.js';
4
- import normalizeRegexPattern from './normalize-regex-pattern.js';
5
- import SkipRanges from './skip-ranges.js';
6
- import testRegexStateless from './test-regex-stateless.js';
7
- export { escapeStringRegexp, getElementsByTagName, isBlankLine, normalizeRegexPattern, SkipRanges, testRegexStateless, };
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 default function isBlankLine(str: string, blockquoteDepth?: number): boolean;
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 default function isBlankLine(str, blockquoteDepth = -1) {
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;
@@ -8,4 +8,4 @@
8
8
  * @param pattern `RegExp` or string pattern to normalize.
9
9
  * @returns The normalized `RegExp`.
10
10
  */
11
- export default function normalizeRegexPattern(pattern: RegExp | string): RegExp;
11
+ export declare function normalizeRegexPattern(pattern: RegExp | string): RegExp;
@@ -11,6 +11,6 @@
11
11
  * @param pattern `RegExp` or string pattern to normalize.
12
12
  * @returns The normalized `RegExp`.
13
13
  */
14
- export default function normalizeRegexPattern(pattern) {
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 { AST } from 'eslint';
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 default class SkipRanges {
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 default class SkipRanges {
10
+ export class SkipRanges {
11
11
  // ------------------------------------------------------------------------------
12
12
  // Private Property
13
13
  // ------------------------------------------------------------------------------
@@ -7,4 +7,4 @@
7
7
  * @param text Text to test.
8
8
  * @returns Whether the regex matches the text.
9
9
  */
10
- export default function testRegexStateless(regex: RegExp, text: string): boolean;
10
+ export declare function testRegexStateless(regex: RegExp, text: string): boolean;
@@ -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 default function testRegexStateless(regex, text) {
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 = (typeof CODE_STYLE)[number];
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 = getCurrentCodeStyle(currentCodeFenceChar);
99
+ const currentCodeStyle = getCodeStyle(currentCodeFenceChar);
120
100
  const nodeStartLineIndex = start.line - 1;
121
101
  const nodeEndLineIndex = end.line - 1;
122
102
  if (codeStyle === null) {
@@ -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" | ("indent" | "fence-backtick" | "fence-tilde");
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";
@@ -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 irregularDashRegex = /[\u2010\u2011\u2012\u2013\u2014\u2015\u2043\u2212\u23af\u2e3a\u2e3b\u30fc\ufe58\ufe63\uff0d]/gu;
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.16.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/lumirlumir/npm-eslint-markdown.git",
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/lumirlumir/npm-eslint-markdown/issues"
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"