markdownlint 0.37.4 → 0.39.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/CHANGELOG.md +20 -0
- package/CONTRIBUTING.md +2 -3
- package/README.md +175 -87
- package/doc/Rules.md +273 -47
- package/doc/md001.md +14 -0
- package/doc/md009.md +8 -9
- package/doc/md010.md +6 -0
- package/doc/md027.md +8 -0
- package/doc/md029.md +2 -0
- package/doc/md033.md +7 -1
- package/doc/md038.md +23 -11
- package/doc/md041.md +31 -16
- package/doc/md043.md +22 -4
- package/doc/md045.md +9 -1
- package/doc/md051.md +17 -0
- package/doc/md052.md +12 -0
- package/doc/md053.md +3 -3
- package/doc/md055.md +1 -1
- package/doc/md056.md +1 -1
- package/doc/md059.md +33 -0
- package/doc/md060.md +70 -0
- package/helpers/README.md +2 -2
- package/helpers/helpers.cjs +174 -20
- package/helpers/micromark-helpers.cjs +37 -7
- package/helpers/package.json +1 -1
- package/lib/cache.mjs +25 -7
- package/lib/configuration-strict.d.ts +1494 -292
- package/lib/constants.mjs +6 -5
- package/lib/defer-require.cjs +2 -0
- package/lib/exports.d.mts +2 -2
- package/lib/exports.mjs +2 -2
- package/lib/markdownit.cjs +8 -14
- package/lib/markdownlint.d.mts +136 -52
- package/lib/markdownlint.mjs +469 -409
- package/lib/md001.mjs +6 -2
- package/lib/md004.mjs +7 -18
- package/lib/md007.mjs +2 -1
- package/lib/md009.mjs +9 -5
- package/lib/md010.mjs +1 -1
- package/lib/md022.mjs +5 -2
- package/lib/md025.mjs +23 -15
- package/lib/md027.mjs +11 -1
- package/lib/md029.mjs +25 -14
- package/lib/md031.mjs +0 -1
- package/lib/md032.mjs +3 -1
- package/lib/md033.mjs +28 -20
- package/lib/md034.mjs +2 -1
- package/lib/md036.mjs +16 -4
- package/lib/md037.mjs +10 -7
- package/lib/md038.mjs +52 -71
- package/lib/md041.mjs +54 -16
- package/lib/md042.mjs +4 -2
- package/lib/md043.mjs +4 -2
- package/lib/md044.mjs +1 -1
- package/lib/md045.mjs +3 -1
- package/lib/md046.mjs +6 -6
- package/lib/md051.mjs +13 -5
- package/lib/md052.mjs +2 -1
- package/lib/md053.mjs +1 -1
- package/lib/md054.mjs +7 -5
- package/lib/md055.mjs +7 -6
- package/lib/md056.mjs +1 -1
- package/lib/md059.mjs +71 -0
- package/lib/md060.mjs +121 -0
- package/lib/micromark-parse.mjs +17 -3
- package/lib/node-imports-browser.mjs +2 -0
- package/lib/parse-configuration.mjs +47 -0
- package/lib/rules.mjs +5 -1
- package/package.json +32 -30
- package/schema/.markdownlint.jsonc +97 -62
- package/schema/.markdownlint.yaml +86 -57
- package/schema/markdownlint-config-schema-strict.json +4713 -1436
- package/schema/markdownlint-config-schema.json +4713 -1436
package/helpers/helpers.cjs
CHANGED
|
@@ -9,11 +9,8 @@ const { newLineRe, nextLinesRe } = require("./shared.cjs");
|
|
|
9
9
|
module.exports.newLineRe = newLineRe;
|
|
10
10
|
module.exports.nextLinesRe = nextLinesRe;
|
|
11
11
|
|
|
12
|
-
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/52529
|
|
13
12
|
/** @typedef {import("../lib/exports.mjs").RuleOnError} RuleOnError */
|
|
14
|
-
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/52529
|
|
15
13
|
/** @typedef {import("../lib/exports.mjs").RuleOnErrorFixInfo} RuleOnErrorFixInfo */
|
|
16
|
-
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/52529
|
|
17
14
|
/** @typedef {import("../lib/exports.mjs").MicromarkToken} MicromarkToken */
|
|
18
15
|
// eslint-disable-next-line jsdoc/valid-types
|
|
19
16
|
/** @typedef {import("micromark-extension-gfm-footnote", { with: { "resolution-mode": "import" } })} */
|
|
@@ -114,10 +111,11 @@ module.exports.cloneIfArray = cloneIfArray;
|
|
|
114
111
|
/**
|
|
115
112
|
* Clones the input if it is a URL.
|
|
116
113
|
*
|
|
117
|
-
* @param {Object} url Object of unknown type.
|
|
114
|
+
* @param {Object | undefined} url Object of unknown type.
|
|
118
115
|
* @returns {Object} Clone of obj iff obj is a URL.
|
|
119
116
|
*/
|
|
120
117
|
function cloneIfUrl(url) {
|
|
118
|
+
// @ts-ignore
|
|
121
119
|
return isUrl(url) ? new URL(url) : url;
|
|
122
120
|
}
|
|
123
121
|
module.exports.cloneIfUrl = cloneIfUrl;
|
|
@@ -142,7 +140,7 @@ module.exports.getHtmlAttributeRe = function getHtmlAttributeRe(name) {
|
|
|
142
140
|
function isBlankLine(line) {
|
|
143
141
|
const startComment = "<!--";
|
|
144
142
|
const endComment = "-->";
|
|
145
|
-
const removeComments = (s) => {
|
|
143
|
+
const removeComments = (/** @type {string} */ s) => {
|
|
146
144
|
while (true) {
|
|
147
145
|
const start = s.indexOf(startComment);
|
|
148
146
|
const end = s.indexOf(endComment);
|
|
@@ -180,8 +178,8 @@ const startsWithPipeRe = /^ *\|/;
|
|
|
180
178
|
const notCrLfRe = /[^\r\n]/g;
|
|
181
179
|
const notSpaceCrLfRe = /[^ \r\n]/g;
|
|
182
180
|
const trailingSpaceRe = / +[\r\n]/g;
|
|
183
|
-
const replaceTrailingSpace = (s) => s.replace(notCrLfRe, safeCommentCharacter);
|
|
184
|
-
module.exports.clearHtmlCommentText = function clearHtmlCommentText(text) {
|
|
181
|
+
const replaceTrailingSpace = (/** @type {string} */ s) => s.replace(notCrLfRe, safeCommentCharacter);
|
|
182
|
+
module.exports.clearHtmlCommentText = function clearHtmlCommentText(/** @type {string} */ text) {
|
|
185
183
|
let i = 0;
|
|
186
184
|
while ((i = text.indexOf(htmlCommentBegin, i)) !== -1) {
|
|
187
185
|
const j = text.indexOf(htmlCommentEnd, i + 2);
|
|
@@ -223,7 +221,7 @@ module.exports.clearHtmlCommentText = function clearHtmlCommentText(text) {
|
|
|
223
221
|
};
|
|
224
222
|
|
|
225
223
|
// Escapes a string for use in a RegExp
|
|
226
|
-
module.exports.escapeForRegExp = function escapeForRegExp(str) {
|
|
224
|
+
module.exports.escapeForRegExp = function escapeForRegExp(/** @type {string} */ str) {
|
|
227
225
|
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
228
226
|
};
|
|
229
227
|
|
|
@@ -311,9 +309,9 @@ module.exports.addErrorDetailIf = addErrorDetailIf;
|
|
|
311
309
|
* @param {RuleOnErrorFixInfo} [fixInfo] RuleOnErrorFixInfo instance.
|
|
312
310
|
* @returns {void}
|
|
313
311
|
*/
|
|
314
|
-
function addErrorContext(
|
|
315
|
-
|
|
316
|
-
context = ellipsify(context, start, end);
|
|
312
|
+
function addErrorContext(onError, lineNumber, context, start, end, range, fixInfo) {
|
|
313
|
+
// Normalize new line characters so Linux and Windows trim consistently
|
|
314
|
+
context = ellipsify(context.replace(newLineRe, "\n"), start, end);
|
|
317
315
|
addError(onError, lineNumber, undefined, context, range, fixInfo);
|
|
318
316
|
}
|
|
319
317
|
module.exports.addErrorContext = addErrorContext;
|
|
@@ -358,7 +356,7 @@ module.exports.hasOverlap = function hasOverlap(rangeA, rangeB) {
|
|
|
358
356
|
|
|
359
357
|
// Determines if the front matter includes a title
|
|
360
358
|
module.exports.frontMatterHasTitle =
|
|
361
|
-
function frontMatterHasTitle(frontMatterLines, frontMatterTitlePattern) {
|
|
359
|
+
function frontMatterHasTitle(/** @type {readonly string[]} */ frontMatterLines, /** @type {string} */ frontMatterTitlePattern) {
|
|
362
360
|
const ignoreFrontMatter =
|
|
363
361
|
(frontMatterTitlePattern !== undefined) && !frontMatterTitlePattern;
|
|
364
362
|
const frontMatterTitleRe =
|
|
@@ -370,17 +368,31 @@ module.exports.frontMatterHasTitle =
|
|
|
370
368
|
frontMatterLines.some((line) => frontMatterTitleRe.test(line));
|
|
371
369
|
};
|
|
372
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Result object for getReferenceLinkImageData.
|
|
373
|
+
*
|
|
374
|
+
* @typedef {Object} GetReferenceLinkImageDataResult
|
|
375
|
+
* @property {Map<string, number[][]>} references References.
|
|
376
|
+
* @property {Map<string, number[][]>} shortcuts Shortcuts.
|
|
377
|
+
* @property {Map<string, [number, string]>} definitions Definitions.
|
|
378
|
+
* @property {[string, number][]} duplicateDefinitions Duplicate definitions.
|
|
379
|
+
* @property {number[]} definitionLineIndices Definition line indices.
|
|
380
|
+
*/
|
|
381
|
+
|
|
373
382
|
/**
|
|
374
383
|
* Returns an object with information about reference links and images.
|
|
375
384
|
*
|
|
376
385
|
* @param {MicromarkToken[]} tokens Micromark tokens.
|
|
377
|
-
* @returns {
|
|
386
|
+
* @returns {GetReferenceLinkImageDataResult} Reference link/image data.
|
|
378
387
|
*/
|
|
379
388
|
function getReferenceLinkImageData(tokens) {
|
|
380
|
-
const normalizeReference = (s) => s.toLowerCase().trim().replace(/\s+/g, " ");
|
|
389
|
+
const normalizeReference = (/** @type {string} */ s) => s.toLowerCase().trim().replace(/\s+/g, " ");
|
|
390
|
+
const getText = (/** @type {MicromarkToken} */ t) => t?.children.filter((c) => c.type !== "blockQuotePrefix").map((c) => c.text).join("");
|
|
391
|
+
/** @type {Map<string, number[][]>} */
|
|
381
392
|
const references = new Map();
|
|
393
|
+
/** @type {Map<string, number[][]>} */
|
|
382
394
|
const shortcuts = new Map();
|
|
383
|
-
const addReferenceToDictionary = (token, label, isShortcut) => {
|
|
395
|
+
const addReferenceToDictionary = (/** @type {MicromarkToken} */ token, /** @type {string} */ label, /** @type {boolean} */ isShortcut) => {
|
|
384
396
|
const referenceDatum = [
|
|
385
397
|
token.startLine - 1,
|
|
386
398
|
token.startColumn - 1,
|
|
@@ -392,8 +404,11 @@ function getReferenceLinkImageData(tokens) {
|
|
|
392
404
|
referenceData.push(referenceDatum);
|
|
393
405
|
dictionary.set(reference, referenceData);
|
|
394
406
|
};
|
|
407
|
+
/** @type {Map<string, [number, string]>} */
|
|
395
408
|
const definitions = new Map();
|
|
409
|
+
/** @type {number[]} */
|
|
396
410
|
const definitionLineIndices = [];
|
|
411
|
+
/** @type {[string, number][]} */
|
|
397
412
|
const duplicateDefinitions = [];
|
|
398
413
|
const filteredTokens =
|
|
399
414
|
micromark.filterByTypes(
|
|
@@ -435,7 +450,7 @@ function getReferenceLinkImageData(tokens) {
|
|
|
435
450
|
micromark.getDescendantsByType(parent, [ "definitionDestination", "definitionDestinationRaw", "definitionDestinationString" ])[0]?.text;
|
|
436
451
|
definitions.set(
|
|
437
452
|
reference,
|
|
438
|
-
[ token.startLine - 1, destinationString ]
|
|
453
|
+
[ token.startLine - 1, destinationString || "" ]
|
|
439
454
|
);
|
|
440
455
|
}
|
|
441
456
|
}
|
|
@@ -449,7 +464,7 @@ function getReferenceLinkImageData(tokens) {
|
|
|
449
464
|
const isFullOrCollapsed = (token.children.length === 2) && !token.children.some((t) => t.type === "resource");
|
|
450
465
|
const [ labelText ] = micromark.getDescendantsByType(token, [ "label", "labelText" ]);
|
|
451
466
|
const [ referenceString ] = micromark.getDescendantsByType(token, [ "reference", "referenceString" ]);
|
|
452
|
-
let label = labelText
|
|
467
|
+
let label = getText(labelText);
|
|
453
468
|
// Identify if footnote
|
|
454
469
|
if (!isShortcut && !isFullOrCollapsed) {
|
|
455
470
|
const [ footnoteCallMarker, footnoteCallString ] = token.children.filter(
|
|
@@ -462,7 +477,7 @@ function getReferenceLinkImageData(tokens) {
|
|
|
462
477
|
}
|
|
463
478
|
// Track link (handle shortcuts separately due to ambiguity in "text [text] text")
|
|
464
479
|
if (isShortcut || isFullOrCollapsed) {
|
|
465
|
-
addReferenceToDictionary(token, referenceString
|
|
480
|
+
addReferenceToDictionary(token, getText(referenceString) || label, isShortcut);
|
|
466
481
|
}
|
|
467
482
|
}
|
|
468
483
|
break;
|
|
@@ -492,7 +507,7 @@ module.exports.getReferenceLinkImageData = getReferenceLinkImageData;
|
|
|
492
507
|
* Gets the most common line ending, falling back to the platform default.
|
|
493
508
|
*
|
|
494
509
|
* @param {string} input Markdown content to analyze.
|
|
495
|
-
* @param {
|
|
510
|
+
* @param {{EOL: string}} [os] Node.js "os" module.
|
|
496
511
|
* @returns {string} Preferred line ending.
|
|
497
512
|
*/
|
|
498
513
|
function getPreferredLineEnding(input, os) {
|
|
@@ -532,7 +547,7 @@ module.exports.getPreferredLineEnding = getPreferredLineEnding;
|
|
|
532
547
|
* Expands a path with a tilde to an absolute path.
|
|
533
548
|
*
|
|
534
549
|
* @param {string} file Path that may begin with a tilde.
|
|
535
|
-
* @param {
|
|
550
|
+
* @param {{homedir: () => string}} os Node.js "os" module.
|
|
536
551
|
* @returns {string} Absolute path (or original path).
|
|
537
552
|
*/
|
|
538
553
|
function expandTildePath(file, os) {
|
|
@@ -540,3 +555,142 @@ function expandTildePath(file, os) {
|
|
|
540
555
|
return homedir ? file.replace(/^~($|\/|\\)/, `${homedir}$1`) : file;
|
|
541
556
|
}
|
|
542
557
|
module.exports.expandTildePath = expandTildePath;
|
|
558
|
+
|
|
559
|
+
/** @typedef {import("../lib/markdownlint.mjs").LintError[]} LintErrors */
|
|
560
|
+
/** @typedef {import("../lib/markdownlint.mjs").LintResults} LintResults */
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Converts lint errors from resultVersion 3 to 2.
|
|
564
|
+
*
|
|
565
|
+
* @param {LintErrors} errors Lint errors (v3).
|
|
566
|
+
* @returns {LintErrors} Lint errors (v2).
|
|
567
|
+
*/
|
|
568
|
+
function convertLintErrorsVersion3To2(errors) {
|
|
569
|
+
const noPrevious = {
|
|
570
|
+
"ruleNames": [],
|
|
571
|
+
"lineNumber": -1
|
|
572
|
+
};
|
|
573
|
+
return errors.filter((error, index, array) => {
|
|
574
|
+
// @ts-ignore
|
|
575
|
+
delete error.fixInfo;
|
|
576
|
+
// @ts-ignore
|
|
577
|
+
delete error.severity;
|
|
578
|
+
const previous = array[index - 1] || noPrevious;
|
|
579
|
+
return (
|
|
580
|
+
(error.ruleNames[0] !== previous.ruleNames[0]) ||
|
|
581
|
+
(error.lineNumber !== previous.lineNumber)
|
|
582
|
+
);
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Converts lint errors from resultVersion 2 to 1.
|
|
588
|
+
*
|
|
589
|
+
* @param {LintErrors} errors Lint errors (v2).
|
|
590
|
+
* @returns {LintErrors} Lint errors (v1).
|
|
591
|
+
*/
|
|
592
|
+
function convertLintErrorsVersion2To1(errors) {
|
|
593
|
+
for (const error of errors) {
|
|
594
|
+
// @ts-ignore
|
|
595
|
+
error.ruleName = error.ruleNames[0];
|
|
596
|
+
// @ts-ignore
|
|
597
|
+
error.ruleAlias = error.ruleNames[1] || error.ruleName;
|
|
598
|
+
// @ts-ignore
|
|
599
|
+
delete error.ruleNames;
|
|
600
|
+
}
|
|
601
|
+
return errors;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Converts lint errors from resultVersion 2 to 0.
|
|
606
|
+
*
|
|
607
|
+
* @param {LintErrors} errors Lint errors (v2).
|
|
608
|
+
* @returns {LintErrors} Lint errors (v0).
|
|
609
|
+
*/
|
|
610
|
+
function convertLintErrorsVersion2To0(errors) {
|
|
611
|
+
/** @type {Object.<string, number[]>} */
|
|
612
|
+
const dictionary = {};
|
|
613
|
+
for (const error of errors) {
|
|
614
|
+
const ruleName = error.ruleNames[0];
|
|
615
|
+
const ruleLines = dictionary[ruleName] || [];
|
|
616
|
+
ruleLines.push(error.lineNumber);
|
|
617
|
+
dictionary[ruleName] = ruleLines;
|
|
618
|
+
}
|
|
619
|
+
// @ts-ignore
|
|
620
|
+
return dictionary;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Copies and transforms lint results from resultVersion 3 to ?.
|
|
625
|
+
*
|
|
626
|
+
* @param {LintResults} results Lint results (v3).
|
|
627
|
+
* @param {(errors: LintErrors) => LintErrors} transform Lint errors (v?).
|
|
628
|
+
* @returns {LintResults} Lint results (v?).
|
|
629
|
+
*/
|
|
630
|
+
function copyAndTransformResults(results, transform) {
|
|
631
|
+
/** @type {Object.<string, LintErrors>} */
|
|
632
|
+
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 }));
|
|
636
|
+
newResults[key] = transform(arr);
|
|
637
|
+
}
|
|
638
|
+
// @ts-ignore
|
|
639
|
+
return newResults;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Converts lint results from resultVersion 3 to 0.
|
|
644
|
+
*
|
|
645
|
+
* @param {LintResults} results Lint results (v3).
|
|
646
|
+
* @returns {LintResults} Lint results (v0).
|
|
647
|
+
*/
|
|
648
|
+
module.exports.convertToResultVersion0 = function convertToResultVersion0(results) {
|
|
649
|
+
return copyAndTransformResults(results, (r) => convertLintErrorsVersion2To0(convertLintErrorsVersion3To2(r)));
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Converts lint results from resultVersion 3 to 1.
|
|
654
|
+
*
|
|
655
|
+
* @param {LintResults} results Lint results (v3).
|
|
656
|
+
* @returns {LintResults} Lint results (v1).
|
|
657
|
+
*/
|
|
658
|
+
module.exports.convertToResultVersion1 = function convertToResultVersion1(results) {
|
|
659
|
+
return copyAndTransformResults(results, (r) => convertLintErrorsVersion2To1(convertLintErrorsVersion3To2(r)));
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Converts lint results from resultVersion 3 to 2.
|
|
664
|
+
*
|
|
665
|
+
* @param {LintResults} results Lint results (v3).
|
|
666
|
+
* @returns {LintResults} Lint results (v2).
|
|
667
|
+
*/
|
|
668
|
+
module.exports.convertToResultVersion2 = function convertToResultVersion2(results) {
|
|
669
|
+
return copyAndTransformResults(results, convertLintErrorsVersion3To2);
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Formats lint results to an array of strings.
|
|
674
|
+
*
|
|
675
|
+
* @param {LintResults|undefined} lintResults Lint results.
|
|
676
|
+
* @returns {string[]} Lint error strings.
|
|
677
|
+
*/
|
|
678
|
+
module.exports.formatLintResults = function formatLintResults(lintResults) {
|
|
679
|
+
const results = [];
|
|
680
|
+
const entries = Object.entries(lintResults || {});
|
|
681
|
+
entries.sort((a, b) => a[0].localeCompare(b[0]));
|
|
682
|
+
for (const [ source, lintErrors ] of entries) {
|
|
683
|
+
for (const lintError of lintErrors) {
|
|
684
|
+
const { lineNumber, ruleNames, ruleDescription, errorDetail, errorContext, errorRange, severity } = lintError;
|
|
685
|
+
const rule = ruleNames.join("/");
|
|
686
|
+
const line = `:${lineNumber}`;
|
|
687
|
+
const rangeStart = (errorRange && errorRange[0]) || 0;
|
|
688
|
+
const column = rangeStart ? `:${rangeStart}` : "";
|
|
689
|
+
const description = ruleDescription;
|
|
690
|
+
const detail = (errorDetail ? ` [${errorDetail}]` : "");
|
|
691
|
+
const context = (errorContext ? ` [Context: "${errorContext}"]` : "");
|
|
692
|
+
results.push(`${source}${line}${column} ${severity} ${rule} ${description}${detail}${context}`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return results;
|
|
696
|
+
};
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
|
-
const { flatTokensSymbol, htmlFlowSymbol } = 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 */
|
|
9
|
-
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/52529
|
|
10
9
|
/** @typedef {import("../lib/exports.mjs").MicromarkToken} Token */
|
|
10
|
+
// eslint-disable-next-line jsdoc/valid-types
|
|
11
|
+
/** @typedef {import("../lib/micromark-types.d.mts", { with: { "resolution-mode": "import" } })} */
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Determines if a Micromark token is within an htmlFlow type.
|
|
@@ -16,6 +17,7 @@ const { flatTokensSymbol, htmlFlowSymbol } = require("./shared.cjs");
|
|
|
16
17
|
* @returns {boolean} True iff the token is within an htmlFlow type.
|
|
17
18
|
*/
|
|
18
19
|
function inHtmlFlow(token) {
|
|
20
|
+
// @ts-ignore
|
|
19
21
|
return Boolean(token[htmlFlowSymbol]);
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -125,8 +127,11 @@ function filterByPredicate(tokens, allowed, transformChildren) {
|
|
|
125
127
|
* @returns {Token[]} Filtered tokens.
|
|
126
128
|
*/
|
|
127
129
|
function filterByTypes(tokens, types, htmlFlow) {
|
|
128
|
-
const predicate = (token) => types.includes(token.type) && (htmlFlow || !inHtmlFlow(token));
|
|
129
|
-
|
|
130
|
+
const predicate = (/** @type {Token} */ token) => types.includes(token.type) && (htmlFlow || !inHtmlFlow(token));
|
|
131
|
+
/** @type {Token[]} */
|
|
132
|
+
const flatTokens =
|
|
133
|
+
// @ts-ignore
|
|
134
|
+
tokens[flatTokensSymbol];
|
|
130
135
|
if (flatTokens) {
|
|
131
136
|
return flatTokens.filter(predicate);
|
|
132
137
|
}
|
|
@@ -162,7 +167,7 @@ function getBlockQuotePrefixText(tokens, lineNumber, count = 1) {
|
|
|
162
167
|
function getDescendantsByType(parent, typePath) {
|
|
163
168
|
let tokens = Array.isArray(parent) ? parent : [ parent ];
|
|
164
169
|
for (const type of typePath) {
|
|
165
|
-
const predicate = (token) => Array.isArray(type) ? type.includes(token.type) : (type === token.type);
|
|
170
|
+
const predicate = (/** @type {Token} */ token) => Array.isArray(type) ? type.includes(token.type) : (type === token.type);
|
|
166
171
|
tokens = tokens.flatMap((t) => t.children.filter(predicate));
|
|
167
172
|
}
|
|
168
173
|
return tokens;
|
|
@@ -215,8 +220,11 @@ function getHeadingStyle(heading) {
|
|
|
215
220
|
* @returns {string} Heading text.
|
|
216
221
|
*/
|
|
217
222
|
function getHeadingText(heading) {
|
|
218
|
-
|
|
219
|
-
|
|
223
|
+
return getDescendantsByType(heading, [ [ "atxHeadingText", "setextHeadingText" ] ])
|
|
224
|
+
.flatMap((descendant) => descendant.children.filter((child) => child.type !== "htmlText"))
|
|
225
|
+
.map((data) => data.text)
|
|
226
|
+
.join("")
|
|
227
|
+
.replace(newLineRe, " ");
|
|
220
228
|
}
|
|
221
229
|
|
|
222
230
|
/**
|
|
@@ -265,6 +273,26 @@ function getParentOfType(token, types) {
|
|
|
265
273
|
return current;
|
|
266
274
|
}
|
|
267
275
|
|
|
276
|
+
const docfxTabSyntaxRe = /^#tab\//;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Returns whether the specified Micromark token looks like a Docfx tab.
|
|
280
|
+
*
|
|
281
|
+
* @param {Token | null} heading Micromark token.
|
|
282
|
+
* @returns {boolean} True iff the token looks like a Docfx tab.
|
|
283
|
+
*/
|
|
284
|
+
function isDocfxTab(heading) {
|
|
285
|
+
// See https://dotnet.github.io/docfx/docs/markdown.html?tabs=linux%2Cdotnet#tabs
|
|
286
|
+
if (heading?.type === "atxHeading") {
|
|
287
|
+
const headingTexts = getDescendantsByType(heading, [ "atxHeadingText" ]);
|
|
288
|
+
if ((headingTexts.length === 1) && (headingTexts[0].children.length === 1) && (headingTexts[0].children[0].type === "link")) {
|
|
289
|
+
const resourceDestinationStrings = filterByTypes(headingTexts[0].children[0].children, [ "resourceDestinationString" ]);
|
|
290
|
+
return (resourceDestinationStrings.length === 1) && docfxTabSyntaxRe.test(resourceDestinationStrings[0].text);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
|
|
268
296
|
/**
|
|
269
297
|
* Set containing token types that do not contain content.
|
|
270
298
|
*
|
|
@@ -274,6 +302,7 @@ const nonContentTokens = new Set([
|
|
|
274
302
|
"blockQuoteMarker",
|
|
275
303
|
"blockQuotePrefix",
|
|
276
304
|
"blockQuotePrefixWhitespace",
|
|
305
|
+
"gfmFootnoteDefinitionIndent",
|
|
277
306
|
"lineEnding",
|
|
278
307
|
"lineEndingBlank",
|
|
279
308
|
"linePrefix",
|
|
@@ -296,6 +325,7 @@ module.exports = {
|
|
|
296
325
|
getHtmlTagInfo,
|
|
297
326
|
getParentOfType,
|
|
298
327
|
inHtmlFlow,
|
|
328
|
+
isDocfxTab,
|
|
299
329
|
isHtmlFlowComment,
|
|
300
330
|
nonContentTokens
|
|
301
331
|
};
|
package/helpers/package.json
CHANGED
package/lib/cache.mjs
CHANGED
|
@@ -3,14 +3,20 @@
|
|
|
3
3
|
import { getReferenceLinkImageData as helpersGetReferenceLinkImageData } from "../helpers/helpers.cjs";
|
|
4
4
|
import { filterByTypes } from "../helpers/micromark-helpers.cjs";
|
|
5
5
|
|
|
6
|
+
/** @typedef {import("markdownlint").RuleParams} RuleParams */
|
|
7
|
+
/** @typedef {import("markdownlint").MicromarkToken} MicromarkToken */
|
|
8
|
+
/** @typedef {import("markdownlint").MicromarkTokenType} MicromarkTokenType */
|
|
9
|
+
/** @typedef {import("../helpers/helpers.cjs").GetReferenceLinkImageDataResult} GetReferenceLinkImageDataResult */
|
|
10
|
+
|
|
6
11
|
/** @type {Map<string, object>} */
|
|
7
12
|
const map = new Map();
|
|
13
|
+
/** @type {RuleParams | undefined} */
|
|
8
14
|
let params = undefined;
|
|
9
15
|
|
|
10
16
|
/**
|
|
11
17
|
* Initializes (resets) the cache.
|
|
12
18
|
*
|
|
13
|
-
* @param {
|
|
19
|
+
* @param {RuleParams} [p] Rule parameters object.
|
|
14
20
|
* @returns {void}
|
|
15
21
|
*/
|
|
16
22
|
export function initialize(p) {
|
|
@@ -18,15 +24,25 @@ export function initialize(p) {
|
|
|
18
24
|
params = p;
|
|
19
25
|
}
|
|
20
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Gets the cached Micromark token array (for testing).
|
|
29
|
+
*
|
|
30
|
+
* @returns {MicromarkToken[]} Micromark tokens.
|
|
31
|
+
*/
|
|
32
|
+
export function micromarkTokens() {
|
|
33
|
+
return params?.parsers.micromark.tokens || [];
|
|
34
|
+
}
|
|
35
|
+
|
|
21
36
|
/**
|
|
22
37
|
* Gets a cached object value - computes it and caches it.
|
|
23
38
|
*
|
|
24
39
|
* @param {string} name Cache object name.
|
|
25
|
-
* @param {
|
|
40
|
+
* @param {() => Object} getValue Getter for object value.
|
|
26
41
|
* @returns {Object} Object value.
|
|
27
42
|
*/
|
|
28
43
|
function getCached(name, getValue) {
|
|
29
44
|
if (map.has(name)) {
|
|
45
|
+
// @ts-ignore
|
|
30
46
|
return map.get(name);
|
|
31
47
|
}
|
|
32
48
|
const value = getValue();
|
|
@@ -37,26 +53,28 @@ function getCached(name, getValue) {
|
|
|
37
53
|
/**
|
|
38
54
|
* Filters a list of Micromark tokens by type and caches the result.
|
|
39
55
|
*
|
|
40
|
-
* @param {
|
|
56
|
+
* @param {MicromarkTokenType[]} types Types to allow.
|
|
41
57
|
* @param {boolean} [htmlFlow] Whether to include htmlFlow content.
|
|
42
|
-
* @returns {
|
|
58
|
+
* @returns {MicromarkToken[]} Filtered tokens.
|
|
43
59
|
*/
|
|
44
60
|
export function filterByTypesCached(types, htmlFlow) {
|
|
61
|
+
// @ts-ignore
|
|
45
62
|
return getCached(
|
|
46
63
|
// eslint-disable-next-line prefer-rest-params
|
|
47
64
|
JSON.stringify(arguments),
|
|
48
|
-
() => filterByTypes(
|
|
65
|
+
() => filterByTypes(micromarkTokens(), types, htmlFlow)
|
|
49
66
|
);
|
|
50
67
|
}
|
|
51
68
|
|
|
52
69
|
/**
|
|
53
70
|
* Gets a reference link and image data object.
|
|
54
71
|
*
|
|
55
|
-
* @returns {
|
|
72
|
+
* @returns {GetReferenceLinkImageDataResult} Reference link and image data object.
|
|
56
73
|
*/
|
|
57
74
|
export function getReferenceLinkImageData() {
|
|
75
|
+
// @ts-ignore
|
|
58
76
|
return getCached(
|
|
59
77
|
getReferenceLinkImageData.name,
|
|
60
|
-
() => helpersGetReferenceLinkImageData(
|
|
78
|
+
() => helpersGetReferenceLinkImageData(micromarkTokens())
|
|
61
79
|
);
|
|
62
80
|
}
|