eslint-markdown 0.17.0 → 0.17.1

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.
@@ -2,6 +2,22 @@
2
2
  * @fileoverview This file declares constants used throughout the `eslint-markdown` package.
3
3
  */
4
4
  export declare const URL_RULE_DOCS: (ruleName?: string) => string;
5
+ /**
6
+ * ASCII punctuation characters.
7
+ */
8
+ export declare const asciiPunctuation: readonly [".", ",", ";", ":", "!"];
9
+ /**
10
+ * ASCII punctuation characters including the question mark.
11
+ */
12
+ export declare const asciiPunctuationWithQuestionMark: readonly [".", ",", ";", ":", "!", "?"];
13
+ /**
14
+ * Non-ASCII punctuation characters.
15
+ */
16
+ export declare const nonAsciiPunctuation: readonly ["。", ",", ";", ":", "!"];
17
+ /**
18
+ * Non-ASCII punctuation characters including the question mark.
19
+ */
20
+ export declare const nonAsciiPunctuationWithQuestionMark: readonly ["。", ",", ";", ":", "!", "?"];
5
21
  /**
6
22
  * This pattern is based on the punctuation list used by `markdownlint` and `remark-lint`.
7
23
  * @see https://github.com/DavidAnson/markdownlint/blob/v0.41.1/helpers/helpers.cjs#L41
@@ -13,7 +29,11 @@ export declare const punctuation: readonly [".", ",", ";", ":", "!", "。", ",
13
29
  * @see https://github.com/DavidAnson/markdownlint/blob/v0.41.1/helpers/helpers.cjs#L41
14
30
  * @see https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-no-heading-punctuation#parameters
15
31
  */
16
- export declare const punctuationWithQuestionMark: readonly [".", ",", ";", ":", "!", "。", ",", ";", ":", "!", "?", "?"];
32
+ export declare const punctuationWithQuestionMark: readonly [".", ",", ";", ":", "!", "?", "。", ",", ";", ":", "!", "?"];
33
+ /**
34
+ * Regular expression for identifying an odd-length run of backslashes at the end of a string.
35
+ */
36
+ export declare const escapedTrailingBackslashRegex: RegExp;
17
37
  /**
18
38
  * Regular expression for identifying a GitHub emoji code.
19
39
  * - NOTE: These patterns are based on the `markdownlint`.
@@ -11,33 +11,48 @@ import pkg from '../../package.json' with { type: 'json' };
11
11
  export const URL_RULE_DOCS = (ruleName = '') => `${pkg.homepage}/docs/rules/${ruleName}`;
12
12
  // --------------------------------------------------------------------------------
13
13
  // #region array
14
+ /**
15
+ * ASCII punctuation characters.
16
+ */
17
+ export const asciiPunctuation = ['.', ',', ';', ':', '!'];
18
+ /**
19
+ * ASCII punctuation characters including the question mark.
20
+ */
21
+ export const asciiPunctuationWithQuestionMark = [...asciiPunctuation, '?'];
22
+ /**
23
+ * Non-ASCII punctuation characters.
24
+ */
25
+ export const nonAsciiPunctuation = ['。', ',', ';', ':', '!'];
26
+ /**
27
+ * Non-ASCII punctuation characters including the question mark.
28
+ */
29
+ export const nonAsciiPunctuationWithQuestionMark = [
30
+ ...nonAsciiPunctuation,
31
+ '?',
32
+ ];
14
33
  /**
15
34
  * This pattern is based on the punctuation list used by `markdownlint` and `remark-lint`.
16
35
  * @see https://github.com/DavidAnson/markdownlint/blob/v0.41.1/helpers/helpers.cjs#L41
17
36
  * @see https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-no-heading-punctuation#parameters
18
37
  */
19
- export const punctuation = [
20
- '.',
21
- ',',
22
- ';',
23
- ':',
24
- '!',
25
- '。',
26
- ',',
27
- ';',
28
- ':',
29
- '!',
30
- ];
38
+ export const punctuation = [...asciiPunctuation, ...nonAsciiPunctuation];
31
39
  /**
32
40
  * This pattern is based on the punctuation list used by `markdownlint` and `remark-lint`.
33
41
  * @see https://github.com/DavidAnson/markdownlint/blob/v0.41.1/helpers/helpers.cjs#L41
34
42
  * @see https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-no-heading-punctuation#parameters
35
43
  */
36
- export const punctuationWithQuestionMark = [...punctuation, '?', '?'];
44
+ export const punctuationWithQuestionMark = [
45
+ ...asciiPunctuationWithQuestionMark,
46
+ ...nonAsciiPunctuationWithQuestionMark,
47
+ ];
37
48
  // #endregion array
38
49
  // --------------------------------------------------------------------------------
39
50
  // --------------------------------------------------------------------------------
40
51
  // #region regex
52
+ /**
53
+ * Regular expression for identifying an odd-length run of backslashes at the end of a string.
54
+ */
55
+ export const escapedTrailingBackslashRegex = /(?<=(?<!\\)(?:\\{2})*)\\$/u;
41
56
  /**
42
57
  * Regular expression for identifying a GitHub emoji code.
43
58
  * - NOTE: These patterns are based on the `markdownlint`.
@@ -932,7 +932,7 @@ declare const _default: {
932
932
  readonly type: "string";
933
933
  readonly minLength: 2;
934
934
  readonly maxLength: 2;
935
- readonly pattern: "^[!,.:;?]{2}$";
935
+ readonly pattern: `^[${string}]{2}$`;
936
936
  };
937
937
  readonly uniqueItems: true;
938
938
  };
@@ -37,7 +37,7 @@ declare const _default: {
37
37
  readonly type: "string";
38
38
  readonly minLength: 2;
39
39
  readonly maxLength: 2;
40
- readonly pattern: "^[!,.:;?]{2}$";
40
+ readonly pattern: `^[${string}]{2}$`;
41
41
  };
42
42
  readonly uniqueItems: true;
43
43
  };
@@ -5,15 +5,17 @@
5
5
  // --------------------------------------------------------------------------------
6
6
  // Import
7
7
  // --------------------------------------------------------------------------------
8
- import { URL_RULE_DOCS } from '../core/constants.js';
8
+ import { escapeStringRegexp } from '../core/utils/index.js';
9
+ import { URL_RULE_DOCS, asciiPunctuationWithQuestionMark } from '../core/constants.js';
9
10
  // --------------------------------------------------------------------------------
10
11
  // Helper
11
12
  // --------------------------------------------------------------------------------
13
+ const escapedAsciiPunctuationWithQuestionMark = escapeStringRegexp(asciiPunctuationWithQuestionMark.join(''));
12
14
  /**
13
15
  * This pattern is based on the punctuation list used by `remark-lint`.
14
16
  * @see https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-no-heading-punctuation#parameters
15
17
  */
16
- const doublePunctuationRegex = /(?:^|(?<=[^!,.:;?]))[!,.:;?]{2}(?:$|(?=[^!,.:;?]))/g;
18
+ const doublePunctuationRegex = new RegExp(`(?:^|(?<=[^${escapedAsciiPunctuationWithQuestionMark}]))[${escapedAsciiPunctuationWithQuestionMark}]{2}(?:$|(?=[^${escapedAsciiPunctuationWithQuestionMark}]))`, 'g');
17
19
  // --------------------------------------------------------------------------------
18
20
  // Rule Definition
19
21
  // --------------------------------------------------------------------------------
@@ -38,7 +40,7 @@ export default {
38
40
  type: 'string',
39
41
  minLength: 2,
40
42
  maxLength: 2,
41
- pattern: '^[!,.:;?]{2}$',
43
+ pattern: `^[${escapedAsciiPunctuationWithQuestionMark}]{2}$`,
42
44
  },
43
45
  uniqueItems: true,
44
46
  },
@@ -7,13 +7,12 @@
7
7
  // Import
8
8
  // --------------------------------------------------------------------------------
9
9
  import { getCodeStyle, isBlankLine } from '../core/utils/index.js';
10
- import { URL_RULE_DOCS } from '../core/constants.js';
10
+ import { URL_RULE_DOCS, escapedTrailingBackslashRegex } from '../core/constants.js';
11
11
  // --------------------------------------------------------------------------------
12
12
  // Helper
13
13
  // --------------------------------------------------------------------------------
14
14
  const dollarCommandRegex = /^[ \t]*\$[ \t]+/u;
15
15
  const promptRegex = /\$[ \t]+/u;
16
- const trailingBackslashRegex = /\\+$/u;
17
16
  const lineEndingRegex = /\r\n|[\r\n]/u;
18
17
  // --------------------------------------------------------------------------------
19
18
  // Rule Definition
@@ -89,8 +88,7 @@ export default {
89
88
  const endOffset = startOffset + match[0].length;
90
89
  promptRanges.push([startOffset, endOffset]);
91
90
  }
92
- isPreviousLineContinues =
93
- (trailingBackslashRegex.exec(codeLine)?.[0].length ?? 0) % 2 === 1;
91
+ isPreviousLineContinues = escapedTrailingBackslashRegex.test(codeLine);
94
92
  }
95
93
  for (const [startOffset, endOffset] of promptRanges) {
96
94
  context.report({
@@ -6,7 +6,7 @@
6
6
  // Import
7
7
  // --------------------------------------------------------------------------------
8
8
  import { escapeStringRegexp } from '../core/utils/index.js';
9
- import { URL_RULE_DOCS, punctuation as defaultPunctuation, gemojiRegex, } from '../core/constants.js';
9
+ import { URL_RULE_DOCS, punctuation as defaultPunctuation, escapedTrailingBackslashRegex, gemojiRegex, } from '../core/constants.js';
10
10
  // --------------------------------------------------------------------------------
11
11
  // Helper
12
12
  // --------------------------------------------------------------------------------
@@ -65,7 +65,7 @@ export default {
65
65
  create(context) {
66
66
  const { sourceCode } = context;
67
67
  const [{ punctuation }] = context.options;
68
- const trailingPunctuationRegex = new RegExp(`[ \\t\\r\\n]*[${escapeStringRegexp(punctuation.join(''))}]+$`);
68
+ const trailingPunctuationRegex = new RegExp(`(?<leadingSpaces>[ \\t\\r\\n]*)[${escapeStringRegexp(punctuation.join(''))}]+$`);
69
69
  return {
70
70
  heading(node) {
71
71
  /*
@@ -128,9 +128,10 @@ export default {
128
128
  }
129
129
  const lastChildText = sourceCode.getText(lastChildNode);
130
130
  const match = trailingPunctuationRegex.exec(lastChildText);
131
- if (!match) {
131
+ if (!match || !match.groups) {
132
132
  return;
133
133
  }
134
+ const { leadingSpaces } = match.groups;
134
135
  let trailingPunctuation = match[0];
135
136
  /*
136
137
  * Slice from the beginning of the trailing `text` node through the first
@@ -176,7 +177,14 @@ export default {
176
177
  },
177
178
  messageId: 'noTrailingHeadingPunctuation',
178
179
  fix(fixer) {
179
- return fixer.removeRange([startOffset, endOffset]);
180
+ return fixer.removeRange([
181
+ startOffset -
182
+ Number(
183
+ // When `leadingSpaces` is `''` (empty string), check for an escaped trailing backslash.
184
+ !leadingSpaces &&
185
+ escapedTrailingBackslashRegex.test(lastChildText.slice(0, -trailingPunctuation.length))),
186
+ endOffset,
187
+ ]);
180
188
  },
181
189
  });
182
190
  },
@@ -64,14 +64,28 @@ export default {
64
64
  const [nodeStartOffset] = sourceCode.getRange(node);
65
65
  const html = sourceCode.getText(node);
66
66
  for (const { attrs, sourceCodeLocation } of getElementsByTagName(html, 'img')) {
67
- let hasTitle = false;
67
+ if (!sourceCodeLocation ||
68
+ (!sourceCodeLocation.attrs?.src &&
69
+ !sourceCodeLocation.attrs?.srcset &&
70
+ !sourceCodeLocation.attrs?.alt)) {
71
+ continue;
72
+ }
73
+ let needsTitle = true;
68
74
  for (const { name, value } of attrs) {
69
75
  if (name === 'title' && value) {
70
- hasTitle = true;
76
+ needsTitle = false;
77
+ break;
78
+ }
79
+ if (name === 'alt' && !value) {
80
+ needsTitle = false;
81
+ break;
82
+ }
83
+ if (name === 'aria-hidden' && value.toLowerCase() === 'true') {
84
+ needsTitle = false;
71
85
  break;
72
86
  }
73
87
  }
74
- if (!hasTitle && sourceCodeLocation) {
88
+ if (needsTitle) {
75
89
  report({
76
90
  start: sourceCode.getLocFromIndex(nodeStartOffset + sourceCodeLocation.startOffset),
77
91
  end: sourceCode.getLocFromIndex(nodeStartOffset + sourceCodeLocation.endOffset),
@@ -67,14 +67,21 @@ export default {
67
67
  const [nodeStartOffset] = sourceCode.getRange(node);
68
68
  const html = sourceCode.getText(node);
69
69
  for (const { attrs, sourceCodeLocation } of getElementsByTagName(html, 'a')) {
70
- let hasTitle = false;
70
+ if (!sourceCodeLocation?.startTag || !sourceCodeLocation.attrs?.href) {
71
+ continue;
72
+ }
73
+ let needsTitle = true;
71
74
  for (const { name, value } of attrs) {
72
75
  if (name === 'title' && value) {
73
- hasTitle = true;
76
+ needsTitle = false;
77
+ break;
78
+ }
79
+ if (name === 'aria-hidden' && value.toLowerCase() === 'true') {
80
+ needsTitle = false;
74
81
  break;
75
82
  }
76
83
  }
77
- if (!hasTitle && sourceCodeLocation?.startTag) {
84
+ if (needsTitle) {
78
85
  report({
79
86
  start: sourceCode.getLocFromIndex(nodeStartOffset + sourceCodeLocation.startTag.startOffset),
80
87
  end: sourceCode.getLocFromIndex(nodeStartOffset + sourceCodeLocation.startTag.endOffset),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-markdown",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Lint your Markdown with ESLint. Additional rules for use with `@eslint/markdown`.🛠️",