markdownlint 0.31.1 → 0.32.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/CONTRIBUTING.md +2 -2
  3. package/README.md +19 -28
  4. package/demo/markdownlint-browser.js +521 -352
  5. package/doc/Rules.md +146 -127
  6. package/doc/md001.md +2 -2
  7. package/doc/md003.md +2 -2
  8. package/doc/md007.md +3 -4
  9. package/doc/md013.md +0 -3
  10. package/doc/md018.md +1 -1
  11. package/doc/md019.md +1 -1
  12. package/doc/md020.md +1 -1
  13. package/doc/md021.md +1 -1
  14. package/doc/md022.md +2 -2
  15. package/doc/md023.md +9 -2
  16. package/doc/md024.md +2 -2
  17. package/doc/md025.md +1 -1
  18. package/doc/md026.md +1 -1
  19. package/doc/md036.md +2 -2
  20. package/doc/md041.md +1 -1
  21. package/doc/md043.md +2 -5
  22. package/doc/md045.md +6 -0
  23. package/doc/md049.md +1 -1
  24. package/doc/md050.md +1 -1
  25. package/doc/md053.md +3 -3
  26. package/doc/md054.md +100 -0
  27. package/helpers/helpers.js +27 -7
  28. package/helpers/micromark.cjs +62 -15
  29. package/helpers/package.json +2 -1
  30. package/helpers/shared.js +3 -0
  31. package/lib/configuration.d.ts +1152 -0
  32. package/lib/constants.js +7 -7
  33. package/lib/markdownlint.d.ts +2 -4
  34. package/lib/markdownlint.js +9 -8
  35. package/lib/md001.js +2 -2
  36. package/lib/md003.js +2 -2
  37. package/lib/md005.js +21 -17
  38. package/lib/md007.js +62 -25
  39. package/lib/md013.js +1 -4
  40. package/lib/md018.js +1 -1
  41. package/lib/md019.js +1 -1
  42. package/lib/md020.js +1 -1
  43. package/lib/md021.js +1 -1
  44. package/lib/md022.js +4 -4
  45. package/lib/md023.js +2 -2
  46. package/lib/md024.js +2 -2
  47. package/lib/md025.js +1 -1
  48. package/lib/md026.js +1 -1
  49. package/lib/md030.js +33 -22
  50. package/lib/md032.js +1 -4
  51. package/lib/md033.js +1 -3
  52. package/lib/md034.js +31 -25
  53. package/lib/md036.js +2 -2
  54. package/lib/md037.js +2 -2
  55. package/lib/md038.js +7 -5
  56. package/lib/md041.js +1 -1
  57. package/lib/md043.js +3 -3
  58. package/lib/md045.js +33 -3
  59. package/lib/md049-md050.js +2 -2
  60. package/lib/md051.js +8 -6
  61. package/lib/md053.js +1 -1
  62. package/lib/md054.js +125 -0
  63. package/lib/rules.js +5 -3
  64. package/package.json +18 -16
  65. package/schema/.markdownlint.jsonc +63 -60
  66. package/schema/.markdownlint.yaml +62 -59
  67. package/schema/markdownlint-config-schema.json +840 -186
  68. package/style/relaxed.json +0 -1
  69. package/doc/md002.md +0 -37
  70. package/doc/md006.md +0 -46
  71. package/lib/md002.js +0 -22
  72. package/lib/md006.js +0 -34
package/doc/md053.md CHANGED
@@ -11,9 +11,9 @@ Parameters:
11
11
  Fixable: Some violations can be fixed by tooling
12
12
 
13
13
  Links and images in Markdown can provide the link destination or image source
14
- at the time of use or can define it elsewhere and use a label for reference.
15
- The reference format is convenient for keeping paragraph text clutter-free
16
- and makes it easy to reuse the same URL in multiple places.
14
+ at the time of use or can use a label to reference a definition elsewhere in
15
+ the document. The latter reference format is convenient for keeping paragraph
16
+ text clutter-free and makes it easy to reuse the same URL in multiple places.
17
17
 
18
18
  Because link and image reference definitions are located separately from
19
19
  where they are used, there are two scenarios where a definition can be
package/doc/md054.md ADDED
@@ -0,0 +1,100 @@
1
+ # `MD054` - Link and image style
2
+
3
+ Tags: `images`, `links`
4
+
5
+ Aliases: `link-image-style`
6
+
7
+ Parameters:
8
+
9
+ - `autolink`: Allow autolinks (`boolean`, default `true`)
10
+ - `collapsed`: Allow collapsed reference links and images (`boolean`, default
11
+ `true`)
12
+ - `full`: Allow full reference links and images (`boolean`, default `true`)
13
+ - `inline`: Allow inline links and images (`boolean`, default `true`)
14
+ - `shortcut`: Allow shortcut reference links and images (`boolean`, default
15
+ `true`)
16
+ - `url_inline`: Allow URLs as inline links (`boolean`, default `true`)
17
+
18
+ Fixable: Some violations can be fixed by tooling
19
+
20
+ Links and images in Markdown can provide the link destination or image source at
21
+ the time of use or can use a label to reference a definition elsewhere in the
22
+ document. The three reference formats are convenient for keeping paragraph text
23
+ clutter-free and make it easy to reuse the same URL in multiple places.
24
+
25
+ By default, this rule allows all link/image styles.
26
+
27
+ Setting the `autolink` parameter to `false` disables autolinks:
28
+
29
+ ```markdown
30
+ <https://example.com>
31
+ ```
32
+
33
+ Setting the `inline` parameter to `false` disables inline links and images:
34
+
35
+ ```markdown
36
+ [link](https://example.com)
37
+
38
+ ![image](https://example.com)
39
+ ```
40
+
41
+ Setting the `full` parameter to `false` disables full reference links and
42
+ images:
43
+
44
+ ```markdown
45
+ [link][url]
46
+
47
+ ![image][url]
48
+
49
+ [url]: https://example.com
50
+ ```
51
+
52
+ Setting the `collapsed` parameter to `false` disables collapsed reference links
53
+ and images:
54
+
55
+ ```markdown
56
+ [url][]
57
+
58
+ ![url][]
59
+
60
+ [url]: https://example.com
61
+ ```
62
+
63
+ Setting the `shortcut` parameter to `false` disables shortcut reference links
64
+ and images:
65
+
66
+ ```markdown
67
+ [url]
68
+
69
+ ![url]
70
+
71
+ [url]: https://example.com
72
+ ```
73
+
74
+ To fix violations of this rule, change the link or image to use an allowed
75
+ style. This rule can automatically fix violations when a link or image can be
76
+ converted to the `inline` style (preferred) or a link can be converted to the
77
+ `autolink` style (which does not support images and must be an absolute URL).
78
+ This rule does *not* fix scenarios that require converting a link or image to
79
+ the `full`, `collapsed`, or `shortcut` reference styles because that involves
80
+ naming the reference and determining where to insert it in the document.
81
+
82
+ Setting the `url_inline` parameter to `false` prevents the use of inline links
83
+ with the same absolute URL text/destination and no title because such links can
84
+ be converted to autolinks:
85
+
86
+ ```markdown
87
+ [https://example.com](https://example.com)
88
+ ```
89
+
90
+ To fix `url_inline` violations, use the simpler autolink syntax instead:
91
+
92
+ ```markdown
93
+ <https://example.com>
94
+ ```
95
+
96
+ Rationale: Consistent formatting makes it easier to understand a document.
97
+ Autolinks are concise, but appear as URLs which can be long and confusing.
98
+ Inline links and images can include descriptive text, but take up more space in
99
+ Markdown form. Reference links and images can be easier to read and manipulate
100
+ in Markdown form, but require a separate link reference definition.
@@ -4,17 +4,17 @@
4
4
 
5
5
  const micromark = require("./micromark.cjs");
6
6
 
7
- const { newLineRe } = require("./shared.js");
7
+ const { newLineRe, nextLinesRe } = require("./shared.js");
8
+
8
9
  module.exports.newLineRe = newLineRe;
10
+ module.exports.nextLinesRe = nextLinesRe;
9
11
 
10
12
  // Regular expression for matching common front matter (YAML and TOML)
11
13
  module.exports.frontMatterRe =
12
- // eslint-disable-next-line max-len
13
- /((^---\s*$[\s\S]*?^---\s*)|(^\+\+\+\s*$[\s\S]*?^(\+\+\+|\.\.\.)\s*)|(^\{\s*$[\s\S]*?^\}\s*))(\r\n|\r|\n|$)/m;
14
+ /((^---\s*$[\s\S]+?^---\s*)|(^\+\+\+\s*$[\s\S]+?^(\+\+\+|\.\.\.)\s*)|(^\{\s*$[\s\S]+?^\}\s*))(\r\n|\r|\n|$)/m;
14
15
 
15
16
  // Regular expression for matching the start of inline disable/enable comments
16
17
  const inlineCommentStartRe =
17
- // eslint-disable-next-line max-len
18
18
  /(<!--\s*markdownlint-(disable|enable|capture|restore|disable-file|enable-file|disable-line|disable-next-line|configure-file))(?:\s|-->)/gi;
19
19
  module.exports.inlineCommentStartRe = inlineCommentStartRe;
20
20
 
@@ -32,12 +32,10 @@ module.exports.linkReferenceDefinitionRe = linkReferenceDefinitionRe;
32
32
 
33
33
  // Regular expression for identifying an HTML entity at the end of a line
34
34
  module.exports.endOfLineHtmlEntityRe =
35
- // eslint-disable-next-line max-len
36
35
  /&(?:#\d+|#[xX][\da-fA-F]+|[a-zA-Z]{2,31}|blk\d{2}|emsp1[34]|frac\d{2}|sup\d|there4);$/;
37
36
 
38
37
  // Regular expression for identifying a GitHub emoji code at the end of a line
39
38
  module.exports.endOfLineGemojiCodeRe =
40
- // eslint-disable-next-line max-len
41
39
  /:(?:[abmovx]|[-+]1|100|1234|(?:1st|2nd|3rd)_place_medal|8ball|clock\d{1,4}|e-mail|non-potable_water|o2|t-rex|u5272|u5408|u55b6|u6307|u6708|u6709|u6e80|u7121|u7533|u7981|u7a7a|[a-z]{2,15}2?|[a-z]{1,14}(?:_[a-z\d]{1,16})+):$/;
42
40
 
43
41
  // All punctuation characters (normal and full-width)
@@ -124,6 +122,16 @@ function cloneIfUrl(url) {
124
122
  }
125
123
  module.exports.cloneIfUrl = cloneIfUrl;
126
124
 
125
+ /**
126
+ * Gets a Regular Expression for matching the specified HTML attribute.
127
+ *
128
+ * @param {string} name HTML attribute name.
129
+ * @returns {RegExp} Regular Expression for matching.
130
+ */
131
+ module.exports.getHtmlAttributeRe = function getHtmlAttributeRe(name) {
132
+ return new RegExp(`\\s${name}\\s*=\\s*['"]?([^'"\\s>]*)`, "iu");
133
+ };
134
+
127
135
  /**
128
136
  * Returns true iff the input line is blank (contains nothing, whitespace, or
129
137
  * comments (unclosed start/end comments allowed)).
@@ -736,7 +744,19 @@ function getReferenceLinkImageData(params) {
736
744
  if (definitions.has(reference)) {
737
745
  duplicateDefinitions.push([ reference, token.startLine - 1 ]);
738
746
  } else {
739
- definitions.set(reference, token.startLine - 1);
747
+ let destinationString = null;
748
+ const parent =
749
+ micromark.getTokenParentOfType(token, [ "definition" ]);
750
+ if (parent) {
751
+ destinationString = micromark.getTokenTextByType(
752
+ micromark.filterByPredicate(parent.children),
753
+ "definitionDestinationString"
754
+ );
755
+ }
756
+ definitions.set(
757
+ reference,
758
+ [ token.startLine - 1, destinationString ]
759
+ );
740
760
  }
741
761
  }
742
762
  break;
@@ -23,6 +23,7 @@ const flatTokensSymbol = Symbol("flat-tokens");
23
23
  * @property {number} endColumn End column (1-based).
24
24
  * @property {string} text Token text.
25
25
  * @property {Token[]} children Child tokens.
26
+ * @property {Token | null} parent Parent token.
26
27
  */
27
28
 
28
29
  /**
@@ -42,8 +43,13 @@ function isHtmlFlowComment(token) {
42
43
  return (
43
44
  !comment.startsWith(">") &&
44
45
  !comment.startsWith("->") &&
45
- !comment.endsWith("-") &&
46
- !comment.includes("--")
46
+ !comment.endsWith("-")
47
+ // The following condition from the CommonMark specification is commented
48
+ // to avoid parsing HTML comments that include "--" because that is NOT a
49
+ // condition of the HTML specification.
50
+ // https://spec.commonmark.org/0.30/#raw-html
51
+ // https://html.spec.whatwg.org/multipage/syntax.html#comments
52
+ // && !comment.includes("--")
47
53
  );
48
54
  }
49
55
  return false;
@@ -92,13 +98,15 @@ function getMicromarkEvents(
92
98
  * @param {Object} micromarkOptions Options for micromark.
93
99
  * @param {boolean} referencesDefined Treat references as defined.
94
100
  * @param {number} lineDelta Offset to apply to start/end line.
101
+ * @param {Token} [ancestor] Parent of top-most tokens.
95
102
  * @returns {Token[]} Micromark tokens (frozen).
96
103
  */
97
104
  function micromarkParseWithOffset(
98
105
  markdown,
99
106
  micromarkOptions,
100
107
  referencesDefined,
101
- lineDelta
108
+ lineDelta,
109
+ ancestor
102
110
  ) {
103
111
  // Use micromark to parse document into Events
104
112
  const events = getMicromarkEvents(
@@ -108,10 +116,19 @@ function micromarkParseWithOffset(
108
116
  // Create Token objects
109
117
  const document = [];
110
118
  let flatTokens = [];
111
- let current = {
112
- "children": document
119
+ /** @type {Token} */
120
+ const root = {
121
+ "type": "ROOT",
122
+ "startLine": -1,
123
+ "startColumn": -1,
124
+ "endLine": -1,
125
+ "endColumn": -1,
126
+ "text": "ROOT",
127
+ "children": document,
128
+ "parent": null
113
129
  };
114
- const history = [ current ];
130
+ const history = [ root ];
131
+ let current = root;
115
132
  let reparseOptions = null;
116
133
  let lines = null;
117
134
  let skipHtmlFlowChildren = false;
@@ -131,11 +148,11 @@ function micromarkParseWithOffset(
131
148
  "endLine": endLine + lineDelta,
132
149
  endColumn,
133
150
  text,
134
- "children": []
151
+ "children": [],
152
+ "parent": ((previous === root) ? (ancestor || null) : previous)
135
153
  };
136
154
  previous.children.push(current);
137
155
  flatTokens.push(current);
138
- // @ts-ignore
139
156
  if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) {
140
157
  skipHtmlFlowChildren = true;
141
158
  if (!reparseOptions || !lines) {
@@ -158,7 +175,8 @@ function micromarkParseWithOffset(
158
175
  reparseMarkdown,
159
176
  reparseOptions,
160
177
  referencesDefined,
161
- current.startLine - 1
178
+ current.startLine - 1,
179
+ current
162
180
  );
163
181
  current.children = tokens;
164
182
  // Avoid stack overflow of Array.push(...spread)
@@ -221,11 +239,12 @@ function micromarkParse(
221
239
  * Filter a list of Micromark tokens by predicate.
222
240
  *
223
241
  * @param {Token[]} tokens Micromark tokens.
224
- * @param {AllowedPredicate} allowed Allowed token predicate.
242
+ * @param {AllowedPredicate} [allowed] Allowed token predicate.
225
243
  * @param {TransformPredicate} [transformChildren] Transform predicate.
226
244
  * @returns {Token[]} Filtered tokens.
227
245
  */
228
246
  function filterByPredicate(tokens, allowed, transformChildren) {
247
+ allowed = allowed || (() => true);
229
248
  const result = [];
230
249
  const queue = [
231
250
  {
@@ -263,11 +282,11 @@ function filterByPredicate(tokens, allowed, transformChildren) {
263
282
  * Filter a list of Micromark tokens by type.
264
283
  *
265
284
  * @param {Token[]} tokens Micromark tokens.
266
- * @param {string[]} allowed Types to allow.
285
+ * @param {string[]} types Types to allow.
267
286
  * @returns {Token[]} Filtered tokens.
268
287
  */
269
- function filterByTypes(tokens, allowed) {
270
- const predicate = (token) => allowed.includes(token.type);
288
+ function filterByTypes(tokens, types) {
289
+ const predicate = (token) => types.includes(token.type);
271
290
  const flatTokens = tokens[flatTokensSymbol];
272
291
  if (flatTokens) {
273
292
  return flatTokens.filter(predicate);
@@ -319,7 +338,23 @@ function getHtmlTagInfo(token) {
319
338
  }
320
339
 
321
340
  /**
322
- * Get the text of a single token from a list of Micromark tokens by type.
341
+ * Gets the nearest parent of the specified type for a Micromark token.
342
+ *
343
+ * @param {Token} token Micromark token.
344
+ * @param {string[]} types Types to allow.
345
+ * @returns {Token | null} Parent token.
346
+ */
347
+ function getTokenParentOfType(token, types) {
348
+ /** @type {Token | null} */
349
+ let current = token;
350
+ while ((current = current.parent) && !types.includes(current.type)) {
351
+ // Empty
352
+ }
353
+ return current;
354
+ }
355
+
356
+ /**
357
+ * Get the text of the first match from a list of Micromark tokens by type.
323
358
  *
324
359
  * @param {Token[]} tokens Micromark tokens.
325
360
  * @param {string} type Types to match.
@@ -327,7 +362,17 @@ function getHtmlTagInfo(token) {
327
362
  */
328
363
  function getTokenTextByType(tokens, type) {
329
364
  const filtered = tokens.filter((token) => token.type === type);
330
- return (filtered.length === 1) ? filtered[0].text : null;
365
+ return (filtered.length > 0) ? filtered[0].text : null;
366
+ }
367
+
368
+ /**
369
+ * Determines if a Micromark token has an htmlFlow-type parent.
370
+ *
371
+ * @param {Token} token Micromark token.
372
+ * @returns {boolean} True iff the token has an htmlFlow-type parent.
373
+ */
374
+ function inHtmlFlow(token) {
375
+ return getTokenParentOfType(token, [ "htmlFlow" ]) !== null;
331
376
  }
332
377
 
333
378
  /**
@@ -373,7 +418,9 @@ module.exports = {
373
418
  getHeadingLevel,
374
419
  getHtmlTagInfo,
375
420
  getMicromarkEvents,
421
+ getTokenParentOfType,
376
422
  getTokenTextByType,
423
+ inHtmlFlow,
377
424
  matchAndGetTokensByType,
378
425
  tokenIfType
379
426
  };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdownlint-rule-helpers",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "A collection of markdownlint helper functions for custom rules",
5
5
  "main": "./helpers.js",
6
6
  "exports": "./helpers.js",
@@ -12,6 +12,7 @@
12
12
  "url": "git+https://github.com/DavidAnson/markdownlint.git"
13
13
  },
14
14
  "bugs": "https://github.com/DavidAnson/markdownlint/issues",
15
+ "funding": "https://github.com/sponsors/DavidAnson",
15
16
  "engines": {
16
17
  "node": ">=16"
17
18
  },
package/helpers/shared.js CHANGED
@@ -5,3 +5,6 @@
5
5
  // Regular expression for matching common newline characters
6
6
  // See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
7
7
  module.exports.newLineRe = /\r\n?|\n/g;
8
+
9
+ // Regular expression for matching next lines
10
+ module.exports.nextLinesRe = /[\r\n][\s\S]*$/;