markdownlint 0.33.0 → 0.35.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/CONTRIBUTING.md +4 -3
  3. package/README.md +25 -10
  4. package/demo/markdownlint-browser.js +3286 -2119
  5. package/doc/CustomRules.md +25 -13
  6. package/doc/Rules.md +106 -7
  7. package/doc/md013.md +8 -7
  8. package/doc/md034.md +5 -0
  9. package/doc/md044.md +10 -0
  10. package/doc/md049.md +3 -0
  11. package/doc/md050.md +3 -0
  12. package/doc/md051.md +26 -0
  13. package/doc/md058.md +48 -0
  14. package/helpers/README.md +3 -27
  15. package/helpers/helpers.js +60 -292
  16. package/helpers/micromark.cjs +153 -36
  17. package/helpers/package.json +2 -2
  18. package/lib/cache.js +62 -13
  19. package/lib/configuration-strict.d.ts +1185 -0
  20. package/lib/configuration.d.ts +3 -1173
  21. package/lib/constants.js +3 -2
  22. package/lib/markdownlint.d.ts +143 -61
  23. package/lib/markdownlint.js +151 -54
  24. package/lib/md001.js +17 -8
  25. package/lib/md003.js +16 -8
  26. package/lib/md004.js +57 -47
  27. package/lib/md005.js +7 -8
  28. package/lib/md007.js +13 -12
  29. package/lib/md009.js +52 -43
  30. package/lib/md010.js +50 -32
  31. package/lib/md011.js +18 -7
  32. package/lib/md012.js +19 -8
  33. package/lib/md013.js +46 -37
  34. package/lib/md014.js +37 -36
  35. package/lib/md018.js +20 -8
  36. package/lib/md019-md021.js +80 -0
  37. package/lib/md020.js +15 -6
  38. package/lib/md022.js +10 -12
  39. package/lib/md023.js +24 -22
  40. package/lib/md024.js +14 -8
  41. package/lib/md025.js +17 -9
  42. package/lib/md026.js +5 -6
  43. package/lib/md027.js +24 -43
  44. package/lib/md028.js +26 -14
  45. package/lib/md029.js +42 -33
  46. package/lib/md030.js +7 -8
  47. package/lib/md031.js +45 -22
  48. package/lib/md032.js +18 -30
  49. package/lib/md033.js +6 -4
  50. package/lib/md034.js +30 -9
  51. package/lib/md035.js +5 -3
  52. package/lib/md036.js +34 -39
  53. package/lib/md037.js +3 -0
  54. package/lib/md038.js +6 -5
  55. package/lib/md039.js +68 -52
  56. package/lib/md040.js +21 -21
  57. package/lib/md041.js +21 -26
  58. package/lib/md042.js +50 -36
  59. package/lib/md043.js +22 -13
  60. package/lib/md044.js +5 -2
  61. package/lib/md045.js +6 -4
  62. package/lib/md046.js +9 -8
  63. package/lib/md047.js +5 -2
  64. package/lib/md048.js +22 -13
  65. package/lib/md049-md050.js +19 -5
  66. package/lib/md051.js +18 -20
  67. package/lib/md052.js +5 -2
  68. package/lib/md053.js +5 -2
  69. package/lib/md054.js +12 -12
  70. package/lib/md055.js +10 -3
  71. package/lib/md056.js +18 -6
  72. package/lib/md058.js +43 -0
  73. package/lib/rules.js +11 -4
  74. package/package.json +22 -25
  75. package/schema/.markdownlint.jsonc +54 -51
  76. package/schema/.markdownlint.yaml +53 -50
  77. package/schema/ValidatingConfiguration.md +6 -8
  78. package/schema/markdownlint-config-schema-strict.json +1819 -0
  79. package/schema/markdownlint-config-schema.json +115 -105
  80. package/style/prettier.json +1 -0
  81. package/lib/md019.js +0 -39
  82. package/lib/md021.js +0 -62
@@ -4,27 +4,29 @@
4
4
 
5
5
  // @ts-ignore
6
6
  const {
7
- directive, gfmAutolinkLiteral, gfmFootnote, gfmTable, math, parse,
8
- postprocess, preprocess
7
+ directive, gfmAutolinkLiteral, gfmFootnote, gfmTable, math,
8
+ parse, postprocess, preprocess
9
9
  // @ts-ignore
10
10
  } = require("markdownlint-micromark");
11
11
  const { newLineRe } = require("./shared.js");
12
12
 
13
13
  const flatTokensSymbol = Symbol("flat-tokens");
14
+ const reparseSymbol = Symbol("reparse");
15
+
16
+ /** @typedef {import("markdownlint-micromark").Event} Event */
17
+ /** @typedef {import("markdownlint-micromark").ParseOptions} ParseOptions */
18
+ /** @typedef {import("markdownlint-micromark").TokenType} TokenType */
19
+ /** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */
14
20
 
15
21
  /**
16
- * Markdown token.
22
+ * Determines if a Micromark token is within an htmlFlow.
17
23
  *
18
- * @typedef {Object} Token
19
- * @property {string} type Token type.
20
- * @property {number} startLine Start line (1-based).
21
- * @property {number} startColumn Start column (1-based).
22
- * @property {number} endLine End line (1-based).
23
- * @property {number} endColumn End column (1-based).
24
- * @property {string} text Token text.
25
- * @property {Token[]} children Child tokens.
26
- * @property {Token | null} parent Parent token.
24
+ * @param {Token} token Micromark token.
25
+ * @returns {boolean} True iff the token is within an htmlFlow.
27
26
  */
27
+ function inHtmlFlow(token) {
28
+ return token[reparseSymbol];
29
+ }
28
30
 
29
31
  /**
30
32
  * Returns whether a token is an htmlFlow type containing an HTML comment.
@@ -59,9 +61,9 @@ function isHtmlFlowComment(token) {
59
61
  * Parses a Markdown document and returns Micromark events.
60
62
  *
61
63
  * @param {string} markdown Markdown document.
62
- * @param {Object} [micromarkOptions] Options for micromark.
64
+ * @param {ParseOptions} [micromarkOptions] Options for micromark.
63
65
  * @param {boolean} [referencesDefined] Treat references as defined.
64
- * @returns {Object[]} Micromark events.
66
+ * @returns {Event[]} Micromark events.
65
67
  */
66
68
  function getMicromarkEvents(
67
69
  markdown,
@@ -96,7 +98,7 @@ function getMicromarkEvents(
96
98
  * Parses a Markdown document and returns (frozen) tokens.
97
99
  *
98
100
  * @param {string} markdown Markdown document.
99
- * @param {Object} micromarkOptions Options for micromark.
101
+ * @param {ParseOptions} micromarkOptions Options for micromark.
100
102
  * @param {boolean} referencesDefined Treat references as defined.
101
103
  * @param {number} lineDelta Offset to apply to start/end line.
102
104
  * @param {Token} [ancestor] Parent of top-most tokens.
@@ -119,7 +121,7 @@ function micromarkParseWithOffset(
119
121
  let flatTokens = [];
120
122
  /** @type {Token} */
121
123
  const root = {
122
- "type": "ROOT",
124
+ "type": "data",
123
125
  "startLine": -1,
124
126
  "startColumn": -1,
125
127
  "endLine": -1,
@@ -130,6 +132,8 @@ function micromarkParseWithOffset(
130
132
  };
131
133
  const history = [ root ];
132
134
  let current = root;
135
+ // eslint-disable-next-line jsdoc/valid-types
136
+ /** @type ParseOptions | null */
133
137
  let reparseOptions = null;
134
138
  let lines = null;
135
139
  let skipHtmlFlowChildren = false;
@@ -152,6 +156,9 @@ function micromarkParseWithOffset(
152
156
  "children": [],
153
157
  "parent": ((previous === root) ? (ancestor || null) : previous)
154
158
  };
159
+ if (ancestor) {
160
+ Object.defineProperty(current, reparseSymbol, { "value": true });
161
+ }
155
162
  previous.children.push(current);
156
163
  flatTokens.push(current);
157
164
  if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) {
@@ -207,7 +214,7 @@ function micromarkParseWithOffset(
207
214
  * Parses a Markdown document and returns (frozen) tokens.
208
215
  *
209
216
  * @param {string} markdown Markdown document.
210
- * @param {Object} [micromarkOptions] Options for micromark.
217
+ * @param {ParseOptions} [micromarkOptions] Options for micromark.
211
218
  * @param {boolean} [referencesDefined] Treat references as defined.
212
219
  * @returns {Token[]} Micromark tokens (frozen).
213
220
  */
@@ -224,6 +231,20 @@ function micromarkParse(
224
231
  );
225
232
  }
226
233
 
234
+ /**
235
+ * Adds a range of numbers to a set.
236
+ *
237
+ * @param {Set<number>} set Set of numbers.
238
+ * @param {number} start Starting number.
239
+ * @param {number} end Ending number.
240
+ * @returns {void}
241
+ */
242
+ function addRangeToSet(set, start, end) {
243
+ for (let i = start; i <= end; i++) {
244
+ set.add(i);
245
+ }
246
+ }
247
+
227
248
  /**
228
249
  * @callback AllowedPredicate
229
250
  * @param {Token} token Micromark token.
@@ -283,11 +304,13 @@ function filterByPredicate(tokens, allowed, transformChildren) {
283
304
  * Filter a list of Micromark tokens by type.
284
305
  *
285
306
  * @param {Token[]} tokens Micromark tokens.
286
- * @param {string[]} types Types to allow.
307
+ * @param {TokenType[]} types Types to allow.
308
+ * @param {boolean} [htmlFlow] Whether to include htmlFlow content.
287
309
  * @returns {Token[]} Filtered tokens.
288
310
  */
289
- function filterByTypes(tokens, types) {
290
- const predicate = (token) => types.includes(token.type);
311
+ function filterByTypes(tokens, types, htmlFlow) {
312
+ const predicate = (token) =>
313
+ (htmlFlow || !inHtmlFlow(token)) && types.includes(token.type);
291
314
  const flatTokens = tokens[flatTokensSymbol];
292
315
  if (flatTokens) {
293
316
  return flatTokens.filter(predicate);
@@ -295,6 +318,46 @@ function filterByTypes(tokens, types) {
295
318
  return filterByPredicate(tokens, predicate);
296
319
  }
297
320
 
321
+ /**
322
+ * Gets a list of nested Micromark token descendants by type path.
323
+ *
324
+ * @param {Token|Token[]} parent Micromark token parent or parents.
325
+ * @param {TokenType[]} typePath Micromark token type path.
326
+ * @returns {Token[]} Micromark token descendants.
327
+ */
328
+ function getDescendantsByType(parent, typePath) {
329
+ let tokens = Array.isArray(parent) ? parent : [ parent ];
330
+ for (const type of typePath) {
331
+ tokens = tokens.flatMap((t) => t.children).filter((t) => t.type === type);
332
+ }
333
+ return tokens;
334
+ }
335
+
336
+ // eslint-disable-next-line jsdoc/valid-types
337
+ /** @typedef {readonly string[]} ReadonlyStringArray */
338
+
339
+ /**
340
+ * Gets the line/column/length exclusions for a Micromark token.
341
+ *
342
+ * @param {ReadonlyStringArray} lines File/string lines.
343
+ * @param {Token} token Micromark token.
344
+ * @returns {number[][]} Exclusions (line number, start column, length).
345
+ */
346
+ function getExclusionsForToken(lines, token) {
347
+ const exclusions = [];
348
+ const { endColumn, endLine, startColumn, startLine } = token;
349
+ for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++) {
350
+ const start = (lineNumber === startLine) ? startColumn : 1;
351
+ const end = (lineNumber === endLine) ? endColumn : lines[lineNumber - 1].length;
352
+ exclusions.push([
353
+ lineNumber,
354
+ start,
355
+ end - start + 1
356
+ ]);
357
+ }
358
+ return exclusions;
359
+ }
360
+
298
361
  /**
299
362
  * Gets the heading level of a Micromark heading tokan.
300
363
  *
@@ -316,11 +379,53 @@ function getHeadingLevel(heading) {
316
379
  return level;
317
380
  }
318
381
 
382
+ /**
383
+ * Gets the heading style of a Micromark heading tokan.
384
+ *
385
+ * @param {Token} heading Micromark heading token.
386
+ * @returns {"atx" | "atx_closed" | "setext"} Heading style.
387
+ */
388
+ function getHeadingStyle(heading) {
389
+ if (heading.type === "setextHeading") {
390
+ return "setext";
391
+ }
392
+ const atxHeadingSequenceLength = filterByTypes(
393
+ heading.children,
394
+ [ "atxHeadingSequence" ]
395
+ ).length;
396
+ if (atxHeadingSequenceLength === 1) {
397
+ return "atx";
398
+ }
399
+ return "atx_closed";
400
+ }
401
+
402
+ /**
403
+ * Gets the heading text of a Micromark heading token.
404
+ *
405
+ * @param {Token} heading Micromark heading token.
406
+ * @returns {string} Heading text.
407
+ */
408
+ function getHeadingText(heading) {
409
+ const headingTexts = filterByTypes(
410
+ heading.children,
411
+ [ "atxHeadingText", "setextHeadingText" ]
412
+ );
413
+ return headingTexts[0]?.text.replace(/[\r\n]+/g, " ") || "";
414
+ }
415
+
416
+ /**
417
+ * HTML tag information.
418
+ *
419
+ * @typedef {Object} HtmlTagInfo
420
+ * @property {boolean} close True iff close tag.
421
+ * @property {string} name Tag name.
422
+ */
423
+
319
424
  /**
320
425
  * Gets information about the tag in an HTML token.
321
426
  *
322
427
  * @param {Token} token Micromark token.
323
- * @returns {Object | null} HTML tag information.
428
+ * @returns {HtmlTagInfo | null} HTML tag information.
324
429
  */
325
430
  function getHtmlTagInfo(token) {
326
431
  const htmlTagNameRe = /^<([^!>][^/\s>]*)/;
@@ -342,7 +447,7 @@ function getHtmlTagInfo(token) {
342
447
  * Gets the nearest parent of the specified type for a Micromark token.
343
448
  *
344
449
  * @param {Token} token Micromark token.
345
- * @param {string[]} types Types to allow.
450
+ * @param {TokenType[]} types Types to allow.
346
451
  * @returns {Token | null} Parent token.
347
452
  */
348
453
  function getTokenParentOfType(token, types) {
@@ -358,7 +463,7 @@ function getTokenParentOfType(token, types) {
358
463
  * Get the text of the first match from a list of Micromark tokens by type.
359
464
  *
360
465
  * @param {Token[]} tokens Micromark tokens.
361
- * @param {string} type Types to match.
466
+ * @param {TokenType} type Type to match.
362
467
  * @returns {string | null} Text of token.
363
468
  */
364
469
  function getTokenTextByType(tokens, type) {
@@ -366,22 +471,12 @@ function getTokenTextByType(tokens, type) {
366
471
  return (filtered.length > 0) ? filtered[0].text : null;
367
472
  }
368
473
 
369
- /**
370
- * Determines if a Micromark token has an htmlFlow-type parent.
371
- *
372
- * @param {Token} token Micromark token.
373
- * @returns {boolean} True iff the token has an htmlFlow-type parent.
374
- */
375
- function inHtmlFlow(token) {
376
- return getTokenParentOfType(token, [ "htmlFlow" ]) !== null;
377
- }
378
-
379
474
  /**
380
475
  * Determines a list of Micromark tokens matches and returns a subset.
381
476
  *
382
477
  * @param {Token[]} tokens Micromark tokens.
383
- * @param {string[]} matchTypes Types to match.
384
- * @param {string[]} [resultTypes] Types to return.
478
+ * @param {TokenType[]} matchTypes Types to match.
479
+ * @param {TokenType[]} [resultTypes] Types to return.
385
480
  * @returns {Token[] | null} Matching tokens.
386
481
  */
387
482
  function matchAndGetTokensByType(tokens, matchTypes, resultTypes) {
@@ -405,23 +500,45 @@ function matchAndGetTokensByType(tokens, matchTypes, resultTypes) {
405
500
  * Returns the specified token iff it is of the desired type.
406
501
  *
407
502
  * @param {Token} token Micromark token candidate.
408
- * @param {string} type Desired type.
503
+ * @param {TokenType} type Desired type.
409
504
  * @returns {Token | null} Token instance.
410
505
  */
411
506
  function tokenIfType(token, type) {
412
507
  return (token && (token.type === type)) ? token : null;
413
508
  }
414
509
 
510
+ /**
511
+ * Set containing token types that do not contain content.
512
+ *
513
+ * @type {Set<TokenType>}
514
+ */
515
+ const nonContentTokens = new Set([
516
+ "blockQuoteMarker",
517
+ "blockQuotePrefix",
518
+ "blockQuotePrefixWhitespace",
519
+ "lineEnding",
520
+ "lineEndingBlank",
521
+ "linePrefix",
522
+ "listItemIndent"
523
+ ]);
524
+
415
525
  module.exports = {
416
526
  "parse": micromarkParse,
527
+ addRangeToSet,
417
528
  filterByPredicate,
418
529
  filterByTypes,
530
+ getDescendantsByType,
531
+ getExclusionsForToken,
419
532
  getHeadingLevel,
533
+ getHeadingStyle,
534
+ getHeadingText,
420
535
  getHtmlTagInfo,
421
536
  getMicromarkEvents,
422
537
  getTokenParentOfType,
423
538
  getTokenTextByType,
424
539
  inHtmlFlow,
540
+ isHtmlFlowComment,
425
541
  matchAndGetTokensByType,
542
+ nonContentTokens,
426
543
  tokenIfType
427
544
  };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdownlint-rule-helpers",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "A collection of markdownlint helper functions for custom rules",
5
5
  "main": "./helpers.js",
6
6
  "exports": "./helpers.js",
@@ -14,7 +14,7 @@
14
14
  "bugs": "https://github.com/DavidAnson/markdownlint/issues",
15
15
  "funding": "https://github.com/sponsors/DavidAnson",
16
16
  "engines": {
17
- "node": ">=16"
17
+ "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
20
  "markdownlint-micromark": "0.1.2"
package/lib/cache.js CHANGED
@@ -2,20 +2,69 @@
2
2
 
3
3
  "use strict";
4
4
 
5
+ const helpers = require("../helpers");
6
+ const { filterByTypes } = require("../helpers/micromark.cjs");
7
+
8
+ /** @type {Map<string, object>} */
5
9
  const map = new Map();
10
+ // eslint-disable-next-line no-undef-init
11
+ let params = undefined;
12
+
13
+ /**
14
+ * Initializes (resets) the cache.
15
+ *
16
+ * @param {import("./markdownlint").RuleParams} [p] Rule parameters object.
17
+ * @returns {void}
18
+ */
19
+ function initialize(p) {
20
+ map.clear();
21
+ params = p;
22
+ }
6
23
 
7
- module.exports.set = (keyValuePairs) => {
8
- for (const [ key, value ] of Object.entries(keyValuePairs)) {
9
- map.set(key, value);
24
+ /**
25
+ * Gets a cached object value - computes it and caches it.
26
+ *
27
+ * @param {string} name Cache object name.
28
+ * @param {Function} getValue Getter for object value.
29
+ * @returns {Object} Object value.
30
+ */
31
+ function getCached(name, getValue) {
32
+ if (map.has(name)) {
33
+ return map.get(name);
10
34
  }
35
+ const value = getValue();
36
+ map.set(name, value);
37
+ return value;
38
+ }
39
+
40
+ /**
41
+ * Filters a list of Micromark tokens by type and caches the result.
42
+ *
43
+ * @param {import("./markdownlint").MicromarkTokenType[]} types Types to allow.
44
+ * @param {boolean} [htmlFlow] Whether to include htmlFlow content.
45
+ * @returns {import("./markdownlint").MicromarkToken[]} Filtered tokens.
46
+ */
47
+ function filterByTypesCached(types, htmlFlow) {
48
+ return getCached(
49
+ types.join("|"),
50
+ () => filterByTypes(params.parsers.micromark.tokens, types, htmlFlow)
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Gets a reference link and image data object.
56
+ *
57
+ * @returns {Object} Reference link and image data object.
58
+ */
59
+ function getReferenceLinkImageData() {
60
+ return getCached(
61
+ getReferenceLinkImageData.name,
62
+ () => helpers.getReferenceLinkImageData(params.parsers.micromark.tokens)
63
+ );
64
+ }
65
+
66
+ module.exports = {
67
+ initialize,
68
+ filterByTypesCached,
69
+ getReferenceLinkImageData
11
70
  };
12
- module.exports.clear = () => map.clear();
13
-
14
- module.exports.codeBlockAndSpanRanges =
15
- () => map.get("codeBlockAndSpanRanges");
16
- module.exports.flattenedLists =
17
- () => map.get("flattenedLists");
18
- module.exports.lineMetadata =
19
- () => map.get("lineMetadata");
20
- module.exports.referenceLinkImageData =
21
- () => map.get("referenceLinkImageData");