markdownlint 0.40.0 → 0.41.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 (50) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +13 -25
  4. package/doc/CustomRules.md +1 -1
  5. package/doc/Rules.md +80 -26
  6. package/doc/md001.md +1 -1
  7. package/doc/md011.md +1 -1
  8. package/doc/md022.md +15 -0
  9. package/doc/md025.md +1 -1
  10. package/doc/md026.md +1 -1
  11. package/doc/md028.md +13 -9
  12. package/doc/md029.md +11 -1
  13. package/doc/md034.md +1 -1
  14. package/doc/md035.md +12 -5
  15. package/doc/md041.md +2 -2
  16. package/doc/md042.md +6 -0
  17. package/doc/md045.md +1 -1
  18. package/doc/md060.md +15 -3
  19. package/helpers/README.md +4 -4
  20. package/helpers/helpers.cjs +55 -49
  21. package/helpers/micromark-helpers.cjs +5 -5
  22. package/helpers/package.json +2 -2
  23. package/helpers/shared.cjs +2 -2
  24. package/lib/cache.mjs +1 -0
  25. package/lib/configuration-strict.d.ts +116 -108
  26. package/lib/constants.mjs +2 -2
  27. package/lib/exports.d.mts +1 -0
  28. package/lib/exports.mjs +1 -0
  29. package/lib/markdownit.cjs +8 -7
  30. package/lib/markdownlint.d.mts +56 -41
  31. package/lib/markdownlint.mjs +40 -105
  32. package/lib/md004.mjs +1 -0
  33. package/lib/md005.mjs +1 -1
  34. package/lib/md019-md021.mjs +1 -0
  35. package/lib/md022.mjs +15 -11
  36. package/lib/md024.mjs +1 -1
  37. package/lib/md029.mjs +37 -25
  38. package/lib/md032.mjs +5 -4
  39. package/lib/md036.mjs +1 -0
  40. package/lib/md037.mjs +1 -2
  41. package/lib/md051.mjs +3 -1
  42. package/lib/md060.mjs +47 -8
  43. package/lib/micromark-parse.mjs +21 -5
  44. package/lib/resolve-module.cjs +4 -3
  45. package/lib/resolve-module.d.cts +1 -1
  46. package/package.json +37 -38
  47. package/schema/.markdownlint.jsonc +55 -53
  48. package/schema/.markdownlint.yaml +77 -75
  49. package/schema/markdownlint-config-schema-strict.json +120 -110
  50. package/schema/markdownlint-config-schema.json +120 -110
package/doc/md060.md CHANGED
@@ -10,6 +10,8 @@ Parameters:
10
10
  - `style`: Table column style (`string`, default `any`, values `aligned` /
11
11
  `any` / `compact` / `tight`)
12
12
 
13
+ Fixable: Some violations can be fixed by tooling
14
+
13
15
  This rule is triggered when the column separator pipe characters (`|`) of a
14
16
  [GitHub Flavored Markdown table][gfm-table-060] are used inconsistently.
15
17
 
@@ -81,6 +83,16 @@ Style `tight` with `aligned_delimiter`:
81
83
  |N|No|
82
84
  ```
83
85
 
86
+ Violations for styles `compact` and `tight` are simple/independent and can be
87
+ fixed automatically. However, fixing even single violations for style `aligned`
88
+ may require modifying the entire table, and therefore are not automatic:
89
+
90
+ ```markdown
91
+ |Alpha |Delta|
92
+ |------|-----|
93
+ |Charlie|Beta|
94
+ ```
95
+
84
96
  **Note**: This rule does not require leading/trailing pipe characters, so this
85
97
  is also a valid table for style `compact`:
86
98
 
@@ -112,7 +124,7 @@ appear aligned in most editors and monospaced fonts:
112
124
 
113
125
  Rationale: Consistent formatting makes it easier to understand a document.
114
126
 
115
- [cjk-characters]: https://en.wikipedia.org/wiki/CJK_characters
116
- [emoji]: https://en.wikipedia.org/wiki/Emoji
127
+ [cjk-characters]: https://wikipedia.org/wiki/CJK_characters
128
+ [emoji]: https://wikipedia.org/wiki/Emoji
117
129
  [gfm-table-060]: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/organizing-information-with-tables
118
- [latin-script]: https://en.wikipedia.org/wiki/Latin_script
130
+ [latin-script]: https://wikipedia.org/wiki/Latin_script
package/helpers/README.md CHANGED
@@ -22,8 +22,8 @@ be useful to custom rule authors and may avoid duplicating code.
22
22
  *None* - The entire body of code is tested to 100% coverage by the core
23
23
  `markdownlint` project, so there are no additional tests here.
24
24
 
25
- [custom-rules]: https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/CustomRules.md
26
- [jsdoc]: https://en.m.wikipedia.org/wiki/JSDoc
27
- [markdown]: https://en.wikipedia.org/wiki/Markdown
25
+ [custom-rules]: https://github.com/DavidAnson/markdownlint/blob/v0.41.1/doc/CustomRules.md
26
+ [jsdoc]: https://wikipedia.org/wiki/JSDoc
27
+ [markdown]: https://wikipedia.org/wiki/Markdown
28
28
  [markdownlint]: https://github.com/DavidAnson/markdownlint
29
- [rules]: https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/Rules.md
29
+ [rules]: https://github.com/DavidAnson/markdownlint/blob/v0.41.1/doc/Rules.md
@@ -2,11 +2,12 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const micromark = require("./micromark-helpers.cjs");
5
+ /* eslint-disable unicorn/comment-content */
6
6
 
7
- const { newLineRe, nextLinesRe } = require("./shared.cjs");
7
+ const micromark = require("./micromark-helpers.cjs");
8
+ const { newlineRe, nextLinesRe } = require("./shared.cjs");
8
9
 
9
- module.exports.newLineRe = newLineRe;
10
+ module.exports.newLineRe = newlineRe;
10
11
  module.exports.nextLinesRe = nextLinesRe;
11
12
 
12
13
  /** @typedef {import("../lib/exports.mjs").RuleOnError} RuleOnError */
@@ -20,6 +21,7 @@ module.exports.nextLinesRe = nextLinesRe;
20
21
  // Regular expression for matching common front matter (YAML and TOML)
21
22
  // @ts-ignore
22
23
  module.exports.frontMatterRe =
24
+ // eslint-disable-next-line unicorn/prefer-unicode-code-point-escapes
23
25
  /((^---[^\S\r\n\u2028\u2029]*$[\s\S]+?^---\s*)|(^\+\+\+[^\S\r\n\u2028\u2029]*$[\s\S]+?^(\+\+\+|\.\.\.)\s*)|(^\{[^\S\r\n\u2028\u2029]*$[\s\S]+?^\}\s*))(\r\n|\r|\n|$)/m;
24
26
 
25
27
  // Regular expression for matching the start of inline disable/enable comments
@@ -178,6 +180,7 @@ const startsWithPipeRe = /^ *\|/;
178
180
  const notCrLfRe = /[^\r\n]/g;
179
181
  const notSpaceCrLfRe = /[^ \r\n]/g;
180
182
  const trailingSpaceRe = / +[\r\n]/g;
183
+ // eslint-disable-next-line unicorn/no-unsafe-string-replacement
181
184
  const replaceTrailingSpace = (/** @type {string} */ s) => s.replace(notCrLfRe, safeCommentCharacter);
182
185
  module.exports.clearHtmlCommentText = function clearHtmlCommentText(/** @type {string} */ text) {
183
186
  let i = 0;
@@ -207,7 +210,9 @@ module.exports.clearHtmlCommentText = function clearHtmlCommentText(/** @type {s
207
210
  // If a valid block/inline comment...
208
211
  if (isValid) {
209
212
  const clearedContent = content
213
+ // eslint-disable-next-line unicorn/no-unsafe-string-replacement
210
214
  .replace(notSpaceCrLfRe, safeCommentCharacter)
215
+ // eslint-disable-next-line unicorn/no-unsafe-string-replacement
211
216
  .replace(trailingSpaceRe, replaceTrailingSpace);
212
217
  text =
213
218
  text.slice(0, i + htmlCommentBegin.length) +
@@ -222,6 +227,7 @@ module.exports.clearHtmlCommentText = function clearHtmlCommentText(/** @type {s
222
227
 
223
228
  // Escapes a string for use in a RegExp
224
229
  module.exports.escapeForRegExp = function escapeForRegExp(/** @type {string} */ str) {
230
+ // eslint-disable-next-line unicorn/prefer-regexp-escape
225
231
  return str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
226
232
  };
227
233
 
@@ -311,7 +317,7 @@ module.exports.addErrorDetailIf = addErrorDetailIf;
311
317
  */
312
318
  function addErrorContext(onError, lineNumber, context, start, end, range, fixInfo) {
313
319
  // Normalize new line characters so Linux and Windows trim consistently
314
- context = ellipsify(context.replace(newLineRe, "\n"), start, end);
320
+ context = ellipsify(context.replace(newlineRe, "\n"), start, end);
315
321
  addError(onError, lineNumber, undefined, context, range, fixInfo);
316
322
  }
317
323
  module.exports.addErrorContext = addErrorContext;
@@ -438,59 +444,59 @@ function getReferenceLinkImageData(tokens) {
438
444
  case "gfmFootnoteDefinitionLabelString":
439
445
  labelPrefix = "^";
440
446
  case "definitionLabelString": // eslint-disable-line no-fallthrough
441
- {
442
- // definitions and definitionLineIndices
443
- const reference = normalizeReference(`${labelPrefix}${token.text}`);
444
- if (definitions.has(reference)) {
445
- duplicateDefinitions.push([ reference, token.startLine - 1 ]);
446
- } else {
447
- const parent =
448
- micromark.getParentOfType(token, [ "definition" ]);
449
- const destinationString = parent &&
450
- micromark.getDescendantsByType(parent, [ "definitionDestination", "definitionDestinationRaw", "definitionDestinationString" ])[0]?.text;
451
- definitions.set(
452
- reference,
453
- [ token.startLine - 1, destinationString || "" ]
454
- );
455
- }
447
+ {
448
+ // definitions and definitionLineIndices
449
+ const reference = normalizeReference(`${labelPrefix}${token.text}`);
450
+ if (definitions.has(reference)) {
451
+ duplicateDefinitions.push([ reference, token.startLine - 1 ]);
452
+ } else {
453
+ const parent =
454
+ micromark.getParentOfType(token, [ "definition" ]);
455
+ const destinationString = parent &&
456
+ micromark.getDescendantsByType(parent, [ "definitionDestination", "definitionDestinationRaw", "definitionDestinationString" ])[0]?.text;
457
+ definitions.set(
458
+ reference,
459
+ [ token.startLine - 1, destinationString || "" ]
460
+ );
456
461
  }
457
462
  break;
463
+ }
458
464
  case "gfmFootnoteCall":
459
465
  case "image":
460
466
  case "link":
461
- {
462
- // Identify if shortcut or full/collapsed
463
- let isShortcut = (token.children.length === 1);
464
- const isFullOrCollapsed = (token.children.length === 2) && !token.children.some((t) => t.type === "resource");
465
- const [ labelText ] = micromark.getDescendantsByType(token, [ "label", "labelText" ]);
466
- const [ referenceString ] = micromark.getDescendantsByType(token, [ "reference", "referenceString" ]);
467
- let label = getText(labelText);
468
- // Identify if footnote
469
- if (!isShortcut && !isFullOrCollapsed) {
470
- const [ footnoteCallMarker, footnoteCallString ] = token.children.filter(
471
- (t) => [ "gfmFootnoteCallMarker", "gfmFootnoteCallString" ].includes(t.type)
472
- );
473
- if (footnoteCallMarker && footnoteCallString) {
474
- label = `${footnoteCallMarker.text}${footnoteCallString.text}`;
475
- isShortcut = true;
476
- }
477
- }
478
- // Track link (handle shortcuts separately due to ambiguity in "text [text] text")
479
- if (isShortcut || isFullOrCollapsed) {
480
- addReferenceToDictionary(token, getText(referenceString) || label, isShortcut);
467
+ {
468
+ // Identify if shortcut or full/collapsed
469
+ let isShortcut = (token.children.length === 1);
470
+ const isFullOrCollapsed = (token.children.length === 2) && !token.children.some((t) => t.type === "resource");
471
+ const [ labelText ] = micromark.getDescendantsByType(token, [ "label", "labelText" ]);
472
+ const [ referenceString ] = micromark.getDescendantsByType(token, [ "reference", "referenceString" ]);
473
+ let label = getText(labelText);
474
+ // Identify if footnote
475
+ if (!isShortcut && !isFullOrCollapsed) {
476
+ const [ footnoteCallMarker, footnoteCallString ] = token.children.filter(
477
+ (t) => [ "gfmFootnoteCallMarker", "gfmFootnoteCallString" ].includes(t.type)
478
+ );
479
+ if (footnoteCallMarker && footnoteCallString) {
480
+ label = `${footnoteCallMarker.text}${footnoteCallString.text}`;
481
+ isShortcut = true;
481
482
  }
482
483
  }
484
+ // Track link (handle shortcuts separately due to ambiguity in "text [text] text")
485
+ if (isShortcut || isFullOrCollapsed) {
486
+ addReferenceToDictionary(token, getText(referenceString) || label, isShortcut);
487
+ }
483
488
  break;
489
+ }
484
490
  case "undefinedReferenceCollapsed":
485
491
  case "undefinedReferenceFull":
486
492
  case "undefinedReferenceShortcut":
487
- {
488
- const undefinedReference = micromark.getDescendantsByType(token, [ "undefinedReference" ])[0];
489
- const label = undefinedReference.children.map((t) => t.text).join("");
490
- const isShortcut = (token.type === "undefinedReferenceShortcut");
491
- addReferenceToDictionary(token, label, isShortcut);
492
- }
493
+ {
494
+ const undefinedReference = micromark.getDescendantsByType(token, [ "undefinedReference" ])[0];
495
+ const label = undefinedReference.children.map((t) => t.text).join("");
496
+ const isShortcut = (token.type === "undefinedReferenceShortcut");
497
+ addReferenceToDictionary(token, label, isShortcut);
493
498
  break;
499
+ }
494
500
  }
495
501
  }
496
502
  return {
@@ -514,7 +520,7 @@ function getPreferredLineEnding(input, os) {
514
520
  let cr = 0;
515
521
  let lf = 0;
516
522
  let crlf = 0;
517
- const endings = input.match(newLineRe) || [];
523
+ const endings = input.match(newlineRe) || [];
518
524
  for (const ending of endings) {
519
525
  // eslint-disable-next-line default-case
520
526
  switch (ending) {
@@ -552,6 +558,7 @@ module.exports.getPreferredLineEnding = getPreferredLineEnding;
552
558
  */
553
559
  function expandTildePath(file, os) {
554
560
  const homedir = os && os.homedir && os.homedir();
561
+ // eslint-disable-next-line unicorn/no-unsafe-string-replacement
555
562
  return homedir ? file.replace(/^~($|\/|\\)/, `${homedir}$1`) : file;
556
563
  }
557
564
  module.exports.expandTildePath = expandTildePath;
@@ -630,9 +637,8 @@ function convertLintErrorsVersion2To0(errors) {
630
637
  function copyAndTransformResults(results, transform) {
631
638
  /** @type {Object.<string, LintErrors>} */
632
639
  const newResults = {};
633
- Object.defineProperty(newResults, "toString", { "value": results.toString });
634
- for (const key of Object.keys(results)) {
635
- const arr = results[key].map((r) => ({ ...r }));
640
+ for (const [ key, value ] of Object.entries(results)) {
641
+ const arr = value.map((r) => ({ ...r }));
636
642
  newResults[key] = transform(arr);
637
643
  }
638
644
  // @ts-ignore
@@ -2,7 +2,7 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { flatTokensSymbol, htmlFlowSymbol, newLineRe } = require("./shared.cjs");
5
+ const { flatTokensSymbol, htmlFlowSymbol, newlineRe } = require("./shared.cjs");
6
6
 
7
7
  // eslint-disable-next-line jsdoc/valid-types
8
8
  /** @typedef {import("micromark-util-types", { with: { "resolution-mode": "import" } }).TokenType} TokenType */
@@ -17,8 +17,7 @@ const { flatTokensSymbol, htmlFlowSymbol, newLineRe } = require("./shared.cjs");
17
17
  * @returns {boolean} True iff the token is within an htmlFlow type.
18
18
  */
19
19
  function inHtmlFlow(token) {
20
- // @ts-ignore
21
- return Boolean(token[htmlFlowSymbol]);
20
+ return Object.hasOwn(token, htmlFlowSymbol);
22
21
  }
23
22
 
24
23
  /**
@@ -152,7 +151,6 @@ function getBlockQuotePrefixText(tokens, lineNumber, count = 1) {
152
151
  .map((prefix) => prefix.text)
153
152
  .join("")
154
153
  .trimEnd()
155
- // eslint-disable-next-line unicorn/prefer-spread
156
154
  .concat("\n")
157
155
  .repeat(count);
158
156
  };
@@ -224,7 +222,7 @@ function getHeadingText(heading) {
224
222
  .flatMap((descendant) => descendant.children.filter((child) => child.type !== "htmlText"))
225
223
  .map((data) => data.text)
226
224
  .join("")
227
- .replace(newLineRe, " ");
225
+ .replace(newlineRe, " ");
228
226
  }
229
227
 
230
228
  /**
@@ -285,7 +283,9 @@ function isDocfxTab(heading) {
285
283
  // See https://dotnet.github.io/docfx/docs/markdown.html?tabs=linux%2Cdotnet#tabs
286
284
  if (heading?.type === "atxHeading") {
287
285
  const headingTexts = getDescendantsByType(heading, [ "atxHeadingText" ]);
286
+ // eslint-disable-next-line unicorn/better-dom-traversing
288
287
  if ((headingTexts.length === 1) && (headingTexts[0].children.length === 1) && (headingTexts[0].children[0].type === "link")) {
288
+ // eslint-disable-next-line unicorn/better-dom-traversing
289
289
  const resourceDestinationStrings = filterByTypes(headingTexts[0].children[0].children, [ "resourceDestinationString" ]);
290
290
  return (resourceDestinationStrings.length === 1) && docfxTabSyntaxRe.test(resourceDestinationStrings[0].text);
291
291
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdownlint-rule-helpers",
3
- "version": "0.30.0",
3
+ "version": "0.31.0",
4
4
  "description": "A collection of markdownlint helper functions for custom rules",
5
5
  "main": "./helpers.cjs",
6
6
  "exports": {
@@ -17,7 +17,7 @@
17
17
  "bugs": "https://github.com/DavidAnson/markdownlint/issues",
18
18
  "funding": "https://github.com/sponsors/DavidAnson",
19
19
  "engines": {
20
- "node": ">=18"
20
+ "node": ">=22"
21
21
  },
22
22
  "keywords": [
23
23
  "markdownlint",
@@ -2,7 +2,7 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- // Symbol for identifing the flat tokens array from micromark parse
5
+ // Symbol for identifying the flat tokens array from micromark parse
6
6
  module.exports.flatTokensSymbol = Symbol("flat-tokens");
7
7
 
8
8
  // Symbol for identifying the htmlFlow token from micromark parse
@@ -10,7 +10,7 @@ module.exports.htmlFlowSymbol = Symbol("html-flow");
10
10
 
11
11
  // Regular expression for matching common newline characters
12
12
  // See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
13
- module.exports.newLineRe = /\r\n?|\n/g;
13
+ module.exports.newlineRe = /\r\n?|\n/g;
14
14
 
15
15
  // Regular expression for matching next lines
16
16
  module.exports.nextLinesRe = /[\r\n][\s\S]*$/;
package/lib/cache.mjs CHANGED
@@ -21,6 +21,7 @@ let params = undefined;
21
21
  */
22
22
  export function initialize(p) {
23
23
  map.clear();
24
+ // eslint-disable-next-line unicorn/no-top-level-assignment-in-function
24
25
  params = p;
25
26
  }
26
27