mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-16 22:10:13 +01:00
Add MD052/reference-links-images and MD053/link-image-reference-definitions for reporting issues with link and image references (fixes #144, fixes #390, fixes #425, fixes #456).
This commit is contained in:
parent
2c947abf7b
commit
c5ca661b96
21 changed files with 1333 additions and 65 deletions
|
|
@ -108,6 +108,8 @@ playground for learning and exploring.
|
||||||
* **[MD049](doc/Rules.md#md049)** *emphasis-style* - Emphasis style should be consistent
|
* **[MD049](doc/Rules.md#md049)** *emphasis-style* - Emphasis style should be consistent
|
||||||
* **[MD050](doc/Rules.md#md050)** *strong-style* - Strong style should be consistent
|
* **[MD050](doc/Rules.md#md050)** *strong-style* - Strong style should be consistent
|
||||||
* **[MD051](doc/Rules.md#md051)** *link-fragments* - Link fragments should be valid
|
* **[MD051](doc/Rules.md#md051)** *link-fragments* - Link fragments should be valid
|
||||||
|
* **[MD052](doc/Rules.md#md052)** *reference-links-images* - Reference links and images should use a label that is defined
|
||||||
|
* **[MD053](doc/Rules.md#md053)** *link-image-reference-definitions* - Link and image reference definitions should be needed
|
||||||
|
|
||||||
<!-- markdownlint-restore -->
|
<!-- markdownlint-restore -->
|
||||||
|
|
||||||
|
|
@ -139,11 +141,11 @@ rules at once.
|
||||||
MD023, MD024, MD025, MD026, MD036, MD041, MD043
|
MD023, MD024, MD025, MD026, MD036, MD041, MD043
|
||||||
* **hr** - MD035
|
* **hr** - MD035
|
||||||
* **html** - MD033
|
* **html** - MD033
|
||||||
* **images** - MD045
|
* **images** - MD045, MD052, MD053
|
||||||
* **indentation** - MD005, MD006, MD007, MD027
|
* **indentation** - MD005, MD006, MD007, MD027
|
||||||
* **language** - MD040
|
* **language** - MD040
|
||||||
* **line_length** - MD013
|
* **line_length** - MD013
|
||||||
* **links** - MD011, MD034, MD039, MD042, MD051
|
* **links** - MD011, MD034, MD039, MD042, MD051, MD052, MD053
|
||||||
* **ol** - MD029, MD030, MD032
|
* **ol** - MD029, MD030, MD032
|
||||||
* **spaces** - MD018, MD019, MD020, MD021, MD023
|
* **spaces** - MD018, MD019, MD020, MD021, MD023
|
||||||
* **spelling** - MD044
|
* **spelling** - MD044
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,11 @@ module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
|
||||||
module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
|
module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
|
||||||
// Regular expression for all instances of emphasis markers
|
// Regular expression for all instances of emphasis markers
|
||||||
var emphasisMarkersRe = /[_*]/g;
|
var emphasisMarkersRe = /[_*]/g;
|
||||||
// Regular expression for link reference definition lines
|
// Regular expression for reference links (full and collapsed but not shortcut)
|
||||||
module.exports.linkReferenceRe = /^ {0,3}\[[^\]]+]:\s.*$/;
|
var referenceLinkRe = /!?\\?\[((?:\[[^\]]*]|[^\]])*)](?:(?:\[([^\]]*)\])|[^(]|$)/g;
|
||||||
|
// Regular expression for link reference definitions
|
||||||
|
var linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])]:/;
|
||||||
|
module.exports.linkReferenceDefinitionRe = linkReferenceDefinitionRe;
|
||||||
// All punctuation characters (normal and full-width)
|
// All punctuation characters (normal and full-width)
|
||||||
var allPunctuation = ".,;:!?。,;:!?";
|
var allPunctuation = ".,;:!?。,;:!?";
|
||||||
module.exports.allPunctuation = allPunctuation;
|
module.exports.allPunctuation = allPunctuation;
|
||||||
|
|
@ -521,6 +524,30 @@ function forEachInlineCodeSpan(input, handler) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.exports.forEachInlineCodeSpan = forEachInlineCodeSpan;
|
module.exports.forEachInlineCodeSpan = forEachInlineCodeSpan;
|
||||||
|
/**
|
||||||
|
* Adds ellipsis to the left/right/middle of the specified text.
|
||||||
|
*
|
||||||
|
* @param {string} text Text to ellipsify.
|
||||||
|
* @param {boolean} [start] True iff the start of the text is important.
|
||||||
|
* @param {boolean} [end] True iff the end of the text is important.
|
||||||
|
* @returns {string} Ellipsified text.
|
||||||
|
*/
|
||||||
|
function ellipsify(text, start, end) {
|
||||||
|
if (text.length <= 30) {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
else if (start && end) {
|
||||||
|
text = text.slice(0, 15) + "..." + text.slice(-15);
|
||||||
|
}
|
||||||
|
else if (end) {
|
||||||
|
text = "..." + text.slice(-30);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text = text.slice(0, 30) + "...";
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
module.exports.ellipsify = ellipsify;
|
||||||
/**
|
/**
|
||||||
* Adds a generic error object via the onError callback.
|
* Adds a generic error object via the onError callback.
|
||||||
*
|
*
|
||||||
|
|
@ -551,18 +578,7 @@ module.exports.addErrorDetailIf = function addErrorDetailIf(onError, lineNumber,
|
||||||
};
|
};
|
||||||
// Adds an error object with context via the onError callback
|
// Adds an error object with context via the onError callback
|
||||||
module.exports.addErrorContext = function addErrorContext(onError, lineNumber, context, left, right, range, fixInfo) {
|
module.exports.addErrorContext = function addErrorContext(onError, lineNumber, context, left, right, range, fixInfo) {
|
||||||
if (context.length <= 30) {
|
context = ellipsify(context, left, right);
|
||||||
// Nothing to do
|
|
||||||
}
|
|
||||||
else if (left && right) {
|
|
||||||
context = context.substr(0, 15) + "..." + context.substr(-15);
|
|
||||||
}
|
|
||||||
else if (right) {
|
|
||||||
context = "..." + context.substr(-30);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
context = context.substr(0, 30) + "...";
|
|
||||||
}
|
|
||||||
addError(onError, lineNumber, null, context, range, fixInfo);
|
addError(onError, lineNumber, null, context, range, fixInfo);
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
|
|
@ -628,6 +644,18 @@ module.exports.htmlElementRanges = function (params, lineMetadata) {
|
||||||
module.exports.overlapsAnyRange = function (ranges, lineIndex, index, length) { return (!ranges.every(function (span) { return ((lineIndex !== span[0]) ||
|
module.exports.overlapsAnyRange = function (ranges, lineIndex, index, length) { return (!ranges.every(function (span) { return ((lineIndex !== span[0]) ||
|
||||||
(index + length < span[1]) ||
|
(index + length < span[1]) ||
|
||||||
(index > span[1] + span[2])); })); };
|
(index > span[1] + span[2])); })); };
|
||||||
|
/**
|
||||||
|
* Determines whether the specified range is within another range.
|
||||||
|
*
|
||||||
|
* @param {number[][]} ranges Array of ranges (line, index, length).
|
||||||
|
* @param {number} lineIndex Line index to check.
|
||||||
|
* @param {number} index Index to check.
|
||||||
|
* @param {number} length Length to check.
|
||||||
|
* @returns {boolean} True iff the specified range is within.
|
||||||
|
*/
|
||||||
|
var withinAnyRange = function (ranges, lineIndex, index, length) { return (!ranges.every(function (span) { return ((lineIndex !== span[0]) ||
|
||||||
|
(index < span[1]) ||
|
||||||
|
(index + length > span[1] + span[2])); })); };
|
||||||
// Returns a range object for a line by applying a RegExp
|
// Returns a range object for a line by applying a RegExp
|
||||||
module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
|
module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
|
||||||
var range = null;
|
var range = null;
|
||||||
|
|
@ -773,6 +801,134 @@ function emphasisMarkersInContent(params) {
|
||||||
return byLine;
|
return byLine;
|
||||||
}
|
}
|
||||||
module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
|
module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
|
||||||
|
/**
|
||||||
|
* Returns an object with information about reference links and images.
|
||||||
|
*
|
||||||
|
* @param {Object} params RuleParams instance.
|
||||||
|
* @param {Object} lineMetadata Line metadata object.
|
||||||
|
* @returns {Object} Reference link/image data.
|
||||||
|
*/
|
||||||
|
function getReferenceLinkImageData(params, lineMetadata) {
|
||||||
|
// Initialize return values
|
||||||
|
var references = new Map();
|
||||||
|
var shortcuts = new Set();
|
||||||
|
var definitions = new Map();
|
||||||
|
var duplicateDefinitions = [];
|
||||||
|
// Define helper functions
|
||||||
|
var normalizeLabel = function (s) { return s.toLowerCase().trim().replace(/\s+/g, " "); };
|
||||||
|
var exclusions = [];
|
||||||
|
var excluded = function (match) { return withinAnyRange(exclusions, 0, match.index, match[0].length); };
|
||||||
|
// Convert input to single-line so multi-line links/images are easier
|
||||||
|
var lineOffsets = [];
|
||||||
|
var currentOffset = 0;
|
||||||
|
var contentLines = [];
|
||||||
|
forEachLine(lineMetadata, function (line, lineIndex, inCode) {
|
||||||
|
lineOffsets[lineIndex] = currentOffset;
|
||||||
|
if (!inCode) {
|
||||||
|
if (line.trim().length === 0) {
|
||||||
|
// Close any unclosed brackets at the end of a block
|
||||||
|
line = "]";
|
||||||
|
}
|
||||||
|
contentLines.push(line);
|
||||||
|
currentOffset += line.length + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lineOffsets.push(currentOffset);
|
||||||
|
var contentLine = contentLines.join(" ");
|
||||||
|
// Determine single-line exclusions for inline code spans
|
||||||
|
forEachInlineCodeSpan(contentLine, function (code, lineIndex, columnIndex) {
|
||||||
|
exclusions.push([0, columnIndex, code.length]);
|
||||||
|
});
|
||||||
|
// Identify all link/image reference definitions
|
||||||
|
forEachLine(lineMetadata, function (line, lineIndex, inCode) {
|
||||||
|
if (!inCode) {
|
||||||
|
var linkReferenceDefinitionMatch = linkReferenceDefinitionRe.exec(line);
|
||||||
|
if (linkReferenceDefinitionMatch) {
|
||||||
|
var label = normalizeLabel(linkReferenceDefinitionMatch[1]);
|
||||||
|
if (definitions.has(label)) {
|
||||||
|
duplicateDefinitions.push([label, lineIndex]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
definitions.set(label, lineIndex);
|
||||||
|
}
|
||||||
|
exclusions.push([0, lineOffsets[lineIndex], line.length]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Identify all link and image references
|
||||||
|
var lineIndex = 0;
|
||||||
|
var pendingContents = [
|
||||||
|
{
|
||||||
|
"content": contentLine,
|
||||||
|
"contentLineIndex": 0,
|
||||||
|
"contentIndex": 0,
|
||||||
|
"topLevel": true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
var pendingContent = null;
|
||||||
|
while ((pendingContent = pendingContents.shift())) {
|
||||||
|
var content = pendingContent.content, contentLineIndex = pendingContent.contentLineIndex, contentIndex = pendingContent.contentIndex, topLevel = pendingContent.topLevel;
|
||||||
|
var referenceLinkMatch = null;
|
||||||
|
while ((referenceLinkMatch = referenceLinkRe.exec(content)) !== null) {
|
||||||
|
var matchString = referenceLinkMatch[0], matchText = referenceLinkMatch[1], matchLabel = referenceLinkMatch[2];
|
||||||
|
if (!matchString.startsWith("\\") &&
|
||||||
|
!matchString.startsWith("!\\") &&
|
||||||
|
!matchText.endsWith("\\") &&
|
||||||
|
!(matchLabel || "").endsWith("\\") &&
|
||||||
|
(topLevel || matchString.startsWith("!")) &&
|
||||||
|
!excluded(referenceLinkMatch)) {
|
||||||
|
var shortcutLink = (matchLabel === undefined);
|
||||||
|
var collapsedLink = (!shortcutLink && (matchLabel.length === 0));
|
||||||
|
var label = normalizeLabel((shortcutLink || collapsedLink) ? matchText : matchLabel);
|
||||||
|
if (label.length > 0) {
|
||||||
|
if (shortcutLink) {
|
||||||
|
// Track, but don't validate due to ambiguity: "text [text] text"
|
||||||
|
shortcuts.add(label);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var referenceindex = referenceLinkMatch.index;
|
||||||
|
if (topLevel) {
|
||||||
|
// Calculate line index
|
||||||
|
while (lineOffsets[lineIndex + 1] <= referenceindex) {
|
||||||
|
lineIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Use provided line index
|
||||||
|
lineIndex = contentLineIndex;
|
||||||
|
}
|
||||||
|
var referenceIndex = referenceindex +
|
||||||
|
(topLevel ? -lineOffsets[lineIndex] : contentIndex);
|
||||||
|
// Track reference and location
|
||||||
|
var referenceData = references.get(label) || [];
|
||||||
|
referenceData.push([
|
||||||
|
lineIndex,
|
||||||
|
referenceIndex,
|
||||||
|
matchString.length
|
||||||
|
]);
|
||||||
|
references.set(label, referenceData);
|
||||||
|
// Check for images embedded in top-level link text
|
||||||
|
if (!matchString.startsWith("!")) {
|
||||||
|
pendingContents.push({
|
||||||
|
"content": matchText,
|
||||||
|
"contentLineIndex": lineIndex,
|
||||||
|
"contentIndex": referenceIndex + 1,
|
||||||
|
"topLevel": false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
references: references,
|
||||||
|
shortcuts: shortcuts,
|
||||||
|
definitions: definitions,
|
||||||
|
duplicateDefinitions: duplicateDefinitions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
module.exports.getReferenceLinkImageData = getReferenceLinkImageData;
|
||||||
/**
|
/**
|
||||||
* Gets the most common line ending, falling back to the platform default.
|
* Gets the most common line ending, falling back to the platform default.
|
||||||
*
|
*
|
||||||
|
|
@ -1109,11 +1265,19 @@ module.exports.lineMetadata = function (value) {
|
||||||
}
|
}
|
||||||
return lineMetadata;
|
return lineMetadata;
|
||||||
};
|
};
|
||||||
|
var referenceLinkImageData = null;
|
||||||
|
module.exports.referenceLinkImageData = function (value) {
|
||||||
|
if (value) {
|
||||||
|
referenceLinkImageData = value;
|
||||||
|
}
|
||||||
|
return referenceLinkImageData;
|
||||||
|
};
|
||||||
module.exports.clear = function () {
|
module.exports.clear = function () {
|
||||||
codeBlockAndSpanRanges = null;
|
codeBlockAndSpanRanges = null;
|
||||||
flattenedLists = null;
|
flattenedLists = null;
|
||||||
htmlElementRanges = null;
|
htmlElementRanges = null;
|
||||||
lineMetadata = null;
|
lineMetadata = null;
|
||||||
|
referenceLinkImageData = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1612,10 +1776,11 @@ function lintContent(ruleList, name, content, md, config, frontMatter, handleRul
|
||||||
frontMatterLines: frontMatterLines
|
frontMatterLines: frontMatterLines
|
||||||
});
|
});
|
||||||
var lineMetadata = helpers.getLineMetadata(paramsBase);
|
var lineMetadata = helpers.getLineMetadata(paramsBase);
|
||||||
cache.lineMetadata(lineMetadata);
|
|
||||||
cache.codeBlockAndSpanRanges(helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata));
|
cache.codeBlockAndSpanRanges(helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata));
|
||||||
cache.htmlElementRanges(helpers.htmlElementRanges(paramsBase, lineMetadata));
|
|
||||||
cache.flattenedLists(helpers.flattenLists(paramsBase.tokens));
|
cache.flattenedLists(helpers.flattenLists(paramsBase.tokens));
|
||||||
|
cache.htmlElementRanges(helpers.htmlElementRanges(paramsBase, lineMetadata));
|
||||||
|
cache.lineMetadata(lineMetadata);
|
||||||
|
cache.referenceLinkImageData(helpers.getReferenceLinkImageData(paramsBase, lineMetadata));
|
||||||
// Function to run for each rule
|
// Function to run for each rule
|
||||||
var results = [];
|
var results = [];
|
||||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||||
|
|
@ -2723,12 +2888,11 @@ module.exports = {
|
||||||
"use strict";
|
"use strict";
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addErrorDetailIf = _a.addErrorDetailIf, filterTokens = _a.filterTokens, forEachHeading = _a.forEachHeading, forEachLine = _a.forEachLine, includesSorted = _a.includesSorted;
|
var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addErrorDetailIf = _a.addErrorDetailIf, filterTokens = _a.filterTokens, forEachHeading = _a.forEachHeading, forEachLine = _a.forEachLine, includesSorted = _a.includesSorted, linkReferenceDefinitionRe = _a.linkReferenceDefinitionRe;
|
||||||
var lineMetadata = (__webpack_require__(/*! ./cache */ "../lib/cache.js").lineMetadata);
|
var lineMetadata = (__webpack_require__(/*! ./cache */ "../lib/cache.js").lineMetadata);
|
||||||
var longLineRePrefix = "^.{";
|
var longLineRePrefix = "^.{";
|
||||||
var longLineRePostfixRelaxed = "}.*\\s.*$";
|
var longLineRePostfixRelaxed = "}.*\\s.*$";
|
||||||
var longLineRePostfixStrict = "}.+$";
|
var longLineRePostfixStrict = "}.+$";
|
||||||
var labelRe = /^\s*\[.*[^\\]]:/;
|
|
||||||
var linkOrImageOnlyLineRe = /^[es]*(lT?L|I)[ES]*$/;
|
var linkOrImageOnlyLineRe = /^[es]*(lT?L|I)[ES]*$/;
|
||||||
var sternModeRe = /^([#>\s]*\s)?\S*$/;
|
var sternModeRe = /^([#>\s]*\s)?\S*$/;
|
||||||
var tokenTypeMap = {
|
var tokenTypeMap = {
|
||||||
|
|
@ -2795,7 +2959,7 @@ module.exports = {
|
||||||
(strict ||
|
(strict ||
|
||||||
(!(stern && sternModeRe.test(line)) &&
|
(!(stern && sternModeRe.test(line)) &&
|
||||||
!includesSorted(linkOnlyLineNumbers, lineNumber) &&
|
!includesSorted(linkOnlyLineNumbers, lineNumber) &&
|
||||||
!labelRe.test(line))) &&
|
!linkReferenceDefinitionRe.test(line))) &&
|
||||||
lengthRe.test(line)) {
|
lengthRe.test(line)) {
|
||||||
addErrorDetailIf(onError, lineNumber, length, line.length, null, null, [length + 1, line.length - length]);
|
addErrorDetailIf(onError, lineNumber, length, line.length, null, null, [length + 1, line.length - length]);
|
||||||
}
|
}
|
||||||
|
|
@ -4243,7 +4407,7 @@ module.exports = {
|
||||||
"use strict";
|
"use strict";
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addErrorDetailIf = _a.addErrorDetailIf, bareUrlRe = _a.bareUrlRe, escapeForRegExp = _a.escapeForRegExp, forEachLine = _a.forEachLine, forEachLink = _a.forEachLink, overlapsAnyRange = _a.overlapsAnyRange, linkReferenceRe = _a.linkReferenceRe;
|
var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addErrorDetailIf = _a.addErrorDetailIf, bareUrlRe = _a.bareUrlRe, escapeForRegExp = _a.escapeForRegExp, forEachLine = _a.forEachLine, forEachLink = _a.forEachLink, overlapsAnyRange = _a.overlapsAnyRange, linkReferenceDefinitionRe = _a.linkReferenceDefinitionRe;
|
||||||
var _b = __webpack_require__(/*! ./cache */ "../lib/cache.js"), codeBlockAndSpanRanges = _b.codeBlockAndSpanRanges, htmlElementRanges = _b.htmlElementRanges, lineMetadata = _b.lineMetadata;
|
var _b = __webpack_require__(/*! ./cache */ "../lib/cache.js"), codeBlockAndSpanRanges = _b.codeBlockAndSpanRanges, htmlElementRanges = _b.htmlElementRanges, lineMetadata = _b.lineMetadata;
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"names": ["MD044", "proper-names"],
|
"names": ["MD044", "proper-names"],
|
||||||
|
|
@ -4259,7 +4423,7 @@ module.exports = {
|
||||||
var includeHtmlElements = (htmlElements === undefined) ? true : !!htmlElements;
|
var includeHtmlElements = (htmlElements === undefined) ? true : !!htmlElements;
|
||||||
var exclusions = [];
|
var exclusions = [];
|
||||||
forEachLine(lineMetadata(), function (line, lineIndex) {
|
forEachLine(lineMetadata(), function (line, lineIndex) {
|
||||||
if (linkReferenceRe.test(line)) {
|
if (linkReferenceDefinitionRe.test(line)) {
|
||||||
exclusions.push([lineIndex, 0, line.length]);
|
exclusions.push([lineIndex, 0, line.length]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -4581,6 +4745,88 @@ module.exports = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "../lib/md052.js":
|
||||||
|
/*!***********************!*\
|
||||||
|
!*** ../lib/md052.js ***!
|
||||||
|
\***********************/
|
||||||
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
var addError = (__webpack_require__(/*! ../helpers */ "../helpers/helpers.js").addError);
|
||||||
|
var referenceLinkImageData = (__webpack_require__(/*! ./cache */ "../lib/cache.js").referenceLinkImageData);
|
||||||
|
module.exports = {
|
||||||
|
"names": ["MD052", "reference-links-images"],
|
||||||
|
"description": "Reference links and images should use a label that is defined",
|
||||||
|
"tags": ["images", "links"],
|
||||||
|
"function": function MD052(params, onError) {
|
||||||
|
var lines = params.lines;
|
||||||
|
var _a = referenceLinkImageData(), references = _a.references, definitions = _a.definitions;
|
||||||
|
// Look for links/images that use an undefined link reference
|
||||||
|
for (var _i = 0, _b = references.entries(); _i < _b.length; _i++) {
|
||||||
|
var reference = _b[_i];
|
||||||
|
var label = reference[0], datas = reference[1];
|
||||||
|
if (!definitions.has(label)) {
|
||||||
|
for (var _c = 0, datas_1 = datas; _c < datas_1.length; _c++) {
|
||||||
|
var data = datas_1[_c];
|
||||||
|
var lineIndex = data[0], index = data[1], length = data[2];
|
||||||
|
// Context will be incomplete if reporting for a multi-line link
|
||||||
|
var context = lines[lineIndex].slice(index, index + length);
|
||||||
|
addError(onError, lineIndex + 1, "Missing link or image reference definition: \"".concat(label, "\""), context, [index + 1, context.length]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "../lib/md053.js":
|
||||||
|
/*!***********************!*\
|
||||||
|
!*** ../lib/md053.js ***!
|
||||||
|
\***********************/
|
||||||
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
var _a = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js"), addError = _a.addError, ellipsify = _a.ellipsify, linkReferenceDefinitionRe = _a.linkReferenceDefinitionRe;
|
||||||
|
var referenceLinkImageData = (__webpack_require__(/*! ./cache */ "../lib/cache.js").referenceLinkImageData);
|
||||||
|
module.exports = {
|
||||||
|
"names": ["MD053", "link-image-reference-definitions"],
|
||||||
|
"description": "Link and image reference definitions should be needed",
|
||||||
|
"tags": ["images", "links"],
|
||||||
|
"function": function MD053(params, onError) {
|
||||||
|
var lines = params.lines;
|
||||||
|
var _a = referenceLinkImageData(), references = _a.references, shortcuts = _a.shortcuts, definitions = _a.definitions, duplicateDefinitions = _a.duplicateDefinitions;
|
||||||
|
var singleLineDefinition = function (line) { return (line.replace(linkReferenceDefinitionRe, "").trim().length > 0); };
|
||||||
|
var deleteFixInfo = {
|
||||||
|
"deleteCount": -1
|
||||||
|
};
|
||||||
|
// Look for unused link references (unreferenced by any link/image)
|
||||||
|
for (var _i = 0, _b = definitions.entries(); _i < _b.length; _i++) {
|
||||||
|
var definition = _b[_i];
|
||||||
|
var label = definition[0], lineIndex = definition[1];
|
||||||
|
if (!references.has(label) && !shortcuts.has(label)) {
|
||||||
|
var line = lines[lineIndex];
|
||||||
|
addError(onError, lineIndex + 1, "Unused link or image reference definition: \"".concat(label, "\""), ellipsify(line), [1, line.length], singleLineDefinition(line) ? deleteFixInfo : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Look for duplicate link references (defined more than once)
|
||||||
|
for (var _c = 0, duplicateDefinitions_1 = duplicateDefinitions; _c < duplicateDefinitions_1.length; _c++) {
|
||||||
|
var duplicateDefinition = duplicateDefinitions_1[_c];
|
||||||
|
var label = duplicateDefinition[0], lineIndex = duplicateDefinition[1];
|
||||||
|
var line = lines[lineIndex];
|
||||||
|
addError(onError, lineIndex + 1, "Duplicate link or image reference definition: \"".concat(label, "\""), ellipsify(line), [1, line.length], singleLineDefinition(line) ? deleteFixInfo : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ "../lib/rules.js":
|
/***/ "../lib/rules.js":
|
||||||
|
|
@ -4648,7 +4894,9 @@ var rules = __spreadArray(__spreadArray([
|
||||||
__webpack_require__(/*! ./md047 */ "../lib/md047.js"),
|
__webpack_require__(/*! ./md047 */ "../lib/md047.js"),
|
||||||
__webpack_require__(/*! ./md048 */ "../lib/md048.js")
|
__webpack_require__(/*! ./md048 */ "../lib/md048.js")
|
||||||
], __webpack_require__(/*! ./md049-md050 */ "../lib/md049-md050.js"), true), [
|
], __webpack_require__(/*! ./md049-md050 */ "../lib/md049-md050.js"), true), [
|
||||||
__webpack_require__(/*! ./md051 */ "../lib/md051.js")
|
__webpack_require__(/*! ./md051 */ "../lib/md051.js"),
|
||||||
|
__webpack_require__(/*! ./md052 */ "../lib/md052.js"),
|
||||||
|
__webpack_require__(/*! ./md053 */ "../lib/md053.js")
|
||||||
], false);
|
], false);
|
||||||
rules.forEach(function (rule) {
|
rules.forEach(function (rule) {
|
||||||
var name = rule.names[0].toLowerCase();
|
var name = rule.names[0].toLowerCase();
|
||||||
|
|
|
||||||
64
doc/Rules.md
64
doc/Rules.md
|
|
@ -2031,6 +2031,70 @@ Note: Creating anchors for headings is not part of the CommonMark specification.
|
||||||
|
|
||||||
[github-section-links]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links
|
[github-section-links]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links
|
||||||
|
|
||||||
|
<a name="md052"></a>
|
||||||
|
|
||||||
|
## MD052 - Reference links and images should use a label that is defined
|
||||||
|
|
||||||
|
Tags: images, links
|
||||||
|
|
||||||
|
Aliases: reference-links-images
|
||||||
|
|
||||||
|
Links and images in Markdown can provide the link destination or image source
|
||||||
|
at the time of use or can define it elsewhere and use a label for reference.
|
||||||
|
The reference format is convenient for keeping paragraph text clutter-free
|
||||||
|
and makes it easy to reuse the same URL in multiple places.
|
||||||
|
|
||||||
|
There are three kinds of reference links and images:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Full: [text][label]
|
||||||
|
Collapsed: [label][]
|
||||||
|
Shortcut: [label]
|
||||||
|
|
||||||
|
Full: ![text][image]
|
||||||
|
Collapsed: ![image][]
|
||||||
|
Shortcut: ![image]
|
||||||
|
|
||||||
|
[label]: https://example.com/label
|
||||||
|
[image]: https://example.com/image
|
||||||
|
```
|
||||||
|
|
||||||
|
A link or image renders correctly when a corresponding label is defined, but
|
||||||
|
the text displays with brackets if the label is not present. This rule warns
|
||||||
|
of undefined labels for "full" and "collapsed" reference syntax.
|
||||||
|
|
||||||
|
> "Shortcut" syntax is ambiguous and a missing label will not generate an
|
||||||
|
error. For example, `[shortcut]` could be a shortcut link or the text
|
||||||
|
"shortcut" in brackets.
|
||||||
|
|
||||||
|
<a name="md053"></a>
|
||||||
|
|
||||||
|
## MD053 - Link and image reference definitions should be needed
|
||||||
|
|
||||||
|
Tags: images, links
|
||||||
|
|
||||||
|
Aliases: link-image-reference-definitions
|
||||||
|
|
||||||
|
Fixable: Most violations can be fixed by tooling
|
||||||
|
|
||||||
|
Links and images in Markdown can provide the link destination or image source
|
||||||
|
at the time of use or can define it elsewhere and use a label for reference.
|
||||||
|
The reference format is convenient for keeping paragraph text clutter-free
|
||||||
|
and makes it easy to reuse the same URL in multiple places.
|
||||||
|
|
||||||
|
Because link and image reference definitions are located separately from
|
||||||
|
where they are used, there are two scenarios where a definition can be
|
||||||
|
unnecessary:
|
||||||
|
|
||||||
|
1. If a label is not referenced by any link or image in a document, that
|
||||||
|
definition is unused and can be deleted.
|
||||||
|
1. If a label is defined multiple times in a document, the first definition is
|
||||||
|
used and the others can be deleted.
|
||||||
|
|
||||||
|
This rule considers a reference definition to be used if any link or image
|
||||||
|
reference has the corresponding label. "Full", "collapsed", and "shortcut"
|
||||||
|
formats are all supported.
|
||||||
|
|
||||||
<!-- markdownlint-configure-file {
|
<!-- markdownlint-configure-file {
|
||||||
"no-inline-html": {
|
"no-inline-html": {
|
||||||
"allowed_elements": [
|
"allowed_elements": [
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,13 @@ module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
|
||||||
// Regular expression for all instances of emphasis markers
|
// Regular expression for all instances of emphasis markers
|
||||||
const emphasisMarkersRe = /[_*]/g;
|
const emphasisMarkersRe = /[_*]/g;
|
||||||
|
|
||||||
// Regular expression for link reference definition lines
|
// Regular expression for reference links (full and collapsed but not shortcut)
|
||||||
module.exports.linkReferenceRe = /^ {0,3}\[[^\]]+]:\s.*$/;
|
const referenceLinkRe =
|
||||||
|
/!?\\?\[((?:\[[^\]]*]|[^\]])*)](?:(?:\[([^\]]*)\])|[^(]|$)/g;
|
||||||
|
|
||||||
|
// Regular expression for link reference definitions
|
||||||
|
const linkReferenceDefinitionRe = /^ {0,3}\[([^\]]*[^\\])]:/;
|
||||||
|
module.exports.linkReferenceDefinitionRe = linkReferenceDefinitionRe;
|
||||||
|
|
||||||
// All punctuation characters (normal and full-width)
|
// All punctuation characters (normal and full-width)
|
||||||
const allPunctuation = ".,;:!?。,;:!?";
|
const allPunctuation = ".,;:!?。,;:!?";
|
||||||
|
|
@ -515,6 +520,28 @@ function forEachInlineCodeSpan(input, handler) {
|
||||||
}
|
}
|
||||||
module.exports.forEachInlineCodeSpan = forEachInlineCodeSpan;
|
module.exports.forEachInlineCodeSpan = forEachInlineCodeSpan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds ellipsis to the left/right/middle of the specified text.
|
||||||
|
*
|
||||||
|
* @param {string} text Text to ellipsify.
|
||||||
|
* @param {boolean} [start] True iff the start of the text is important.
|
||||||
|
* @param {boolean} [end] True iff the end of the text is important.
|
||||||
|
* @returns {string} Ellipsified text.
|
||||||
|
*/
|
||||||
|
function ellipsify(text, start, end) {
|
||||||
|
if (text.length <= 30) {
|
||||||
|
// Nothing to do
|
||||||
|
} else if (start && end) {
|
||||||
|
text = text.slice(0, 15) + "..." + text.slice(-15);
|
||||||
|
} else if (end) {
|
||||||
|
text = "..." + text.slice(-30);
|
||||||
|
} else {
|
||||||
|
text = text.slice(0, 30) + "...";
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
module.exports.ellipsify = ellipsify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a generic error object via the onError callback.
|
* Adds a generic error object via the onError callback.
|
||||||
*
|
*
|
||||||
|
|
@ -555,15 +582,7 @@ module.exports.addErrorDetailIf = function addErrorDetailIf(
|
||||||
// Adds an error object with context via the onError callback
|
// Adds an error object with context via the onError callback
|
||||||
module.exports.addErrorContext = function addErrorContext(
|
module.exports.addErrorContext = function addErrorContext(
|
||||||
onError, lineNumber, context, left, right, range, fixInfo) {
|
onError, lineNumber, context, left, right, range, fixInfo) {
|
||||||
if (context.length <= 30) {
|
context = ellipsify(context, left, right);
|
||||||
// Nothing to do
|
|
||||||
} else if (left && right) {
|
|
||||||
context = context.substr(0, 15) + "..." + context.substr(-15);
|
|
||||||
} else if (right) {
|
|
||||||
context = "..." + context.substr(-30);
|
|
||||||
} else {
|
|
||||||
context = context.substr(0, 30) + "...";
|
|
||||||
}
|
|
||||||
addError(onError, lineNumber, null, context, range, fixInfo);
|
addError(onError, lineNumber, null, context, range, fixInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -640,6 +659,23 @@ module.exports.overlapsAnyRange = (ranges, lineIndex, index, length) => (
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the specified range is within another range.
|
||||||
|
*
|
||||||
|
* @param {number[][]} ranges Array of ranges (line, index, length).
|
||||||
|
* @param {number} lineIndex Line index to check.
|
||||||
|
* @param {number} index Index to check.
|
||||||
|
* @param {number} length Length to check.
|
||||||
|
* @returns {boolean} True iff the specified range is within.
|
||||||
|
*/
|
||||||
|
const withinAnyRange = (ranges, lineIndex, index, length) => (
|
||||||
|
!ranges.every((span) => (
|
||||||
|
(lineIndex !== span[0]) ||
|
||||||
|
(index < span[1]) ||
|
||||||
|
(index + length > span[1] + span[2])
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
// Returns a range object for a line by applying a RegExp
|
// Returns a range object for a line by applying a RegExp
|
||||||
module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
|
module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
|
||||||
let range = null;
|
let range = null;
|
||||||
|
|
@ -789,6 +825,142 @@ function emphasisMarkersInContent(params) {
|
||||||
}
|
}
|
||||||
module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
|
module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object with information about reference links and images.
|
||||||
|
*
|
||||||
|
* @param {Object} params RuleParams instance.
|
||||||
|
* @param {Object} lineMetadata Line metadata object.
|
||||||
|
* @returns {Object} Reference link/image data.
|
||||||
|
*/
|
||||||
|
function getReferenceLinkImageData(params, lineMetadata) {
|
||||||
|
// Initialize return values
|
||||||
|
const references = new Map();
|
||||||
|
const shortcuts = new Set();
|
||||||
|
const definitions = new Map();
|
||||||
|
const duplicateDefinitions = [];
|
||||||
|
// Define helper functions
|
||||||
|
const normalizeLabel = (s) => s.toLowerCase().trim().replace(/\s+/g, " ");
|
||||||
|
const exclusions = [];
|
||||||
|
const excluded = (match) => withinAnyRange(
|
||||||
|
exclusions, 0, match.index, match[0].length
|
||||||
|
);
|
||||||
|
// Convert input to single-line so multi-line links/images are easier
|
||||||
|
const lineOffsets = [];
|
||||||
|
let currentOffset = 0;
|
||||||
|
const contentLines = [];
|
||||||
|
forEachLine(lineMetadata, (line, lineIndex, inCode) => {
|
||||||
|
lineOffsets[lineIndex] = currentOffset;
|
||||||
|
if (!inCode) {
|
||||||
|
if (line.trim().length === 0) {
|
||||||
|
// Close any unclosed brackets at the end of a block
|
||||||
|
line = "]";
|
||||||
|
}
|
||||||
|
contentLines.push(line);
|
||||||
|
currentOffset += line.length + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lineOffsets.push(currentOffset);
|
||||||
|
const contentLine = contentLines.join(" ");
|
||||||
|
// Determine single-line exclusions for inline code spans
|
||||||
|
forEachInlineCodeSpan(contentLine, (code, lineIndex, columnIndex) => {
|
||||||
|
exclusions.push([ 0, columnIndex, code.length ]);
|
||||||
|
});
|
||||||
|
// Identify all link/image reference definitions
|
||||||
|
forEachLine(lineMetadata, (line, lineIndex, inCode) => {
|
||||||
|
if (!inCode) {
|
||||||
|
const linkReferenceDefinitionMatch = linkReferenceDefinitionRe.exec(line);
|
||||||
|
if (linkReferenceDefinitionMatch) {
|
||||||
|
const label = normalizeLabel(linkReferenceDefinitionMatch[1]);
|
||||||
|
if (definitions.has(label)) {
|
||||||
|
duplicateDefinitions.push([ label, lineIndex ]);
|
||||||
|
} else {
|
||||||
|
definitions.set(label, lineIndex);
|
||||||
|
}
|
||||||
|
exclusions.push([ 0, lineOffsets[lineIndex], line.length ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Identify all link and image references
|
||||||
|
let lineIndex = 0;
|
||||||
|
const pendingContents = [
|
||||||
|
{
|
||||||
|
"content": contentLine,
|
||||||
|
"contentLineIndex": 0,
|
||||||
|
"contentIndex": 0,
|
||||||
|
"topLevel": true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let pendingContent = null;
|
||||||
|
while ((pendingContent = pendingContents.shift())) {
|
||||||
|
const { content, contentLineIndex, contentIndex, topLevel } =
|
||||||
|
pendingContent;
|
||||||
|
let referenceLinkMatch = null;
|
||||||
|
while ((referenceLinkMatch = referenceLinkRe.exec(content)) !== null) {
|
||||||
|
const [ matchString, matchText, matchLabel ] = referenceLinkMatch;
|
||||||
|
if (
|
||||||
|
!matchString.startsWith("\\") &&
|
||||||
|
!matchString.startsWith("!\\") &&
|
||||||
|
!matchText.endsWith("\\") &&
|
||||||
|
!(matchLabel || "").endsWith("\\") &&
|
||||||
|
(topLevel || matchString.startsWith("!")) &&
|
||||||
|
!excluded(referenceLinkMatch)
|
||||||
|
) {
|
||||||
|
const shortcutLink = (matchLabel === undefined);
|
||||||
|
const collapsedLink =
|
||||||
|
(!shortcutLink && (matchLabel.length === 0));
|
||||||
|
const label = normalizeLabel(
|
||||||
|
(shortcutLink || collapsedLink) ? matchText : matchLabel
|
||||||
|
);
|
||||||
|
if (label.length > 0) {
|
||||||
|
if (shortcutLink) {
|
||||||
|
// Track, but don't validate due to ambiguity: "text [text] text"
|
||||||
|
shortcuts.add(label);
|
||||||
|
} else {
|
||||||
|
const referenceindex = referenceLinkMatch.index;
|
||||||
|
if (topLevel) {
|
||||||
|
// Calculate line index
|
||||||
|
while (lineOffsets[lineIndex + 1] <= referenceindex) {
|
||||||
|
lineIndex++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Use provided line index
|
||||||
|
lineIndex = contentLineIndex;
|
||||||
|
}
|
||||||
|
const referenceIndex = referenceindex +
|
||||||
|
(topLevel ? -lineOffsets[lineIndex] : contentIndex);
|
||||||
|
// Track reference and location
|
||||||
|
const referenceData = references.get(label) || [];
|
||||||
|
referenceData.push([
|
||||||
|
lineIndex,
|
||||||
|
referenceIndex,
|
||||||
|
matchString.length
|
||||||
|
]);
|
||||||
|
references.set(label, referenceData);
|
||||||
|
// Check for images embedded in top-level link text
|
||||||
|
if (!matchString.startsWith("!")) {
|
||||||
|
pendingContents.push(
|
||||||
|
{
|
||||||
|
"content": matchText,
|
||||||
|
"contentLineIndex": lineIndex,
|
||||||
|
"contentIndex": referenceIndex + 1,
|
||||||
|
"topLevel": false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
references,
|
||||||
|
shortcuts,
|
||||||
|
definitions,
|
||||||
|
duplicateDefinitions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
module.exports.getReferenceLinkImageData = getReferenceLinkImageData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the most common line ending, falling back to the platform default.
|
* Gets the most common line ending, falling back to the platform default.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,18 @@ module.exports.lineMetadata = (value) => {
|
||||||
return lineMetadata;
|
return lineMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let referenceLinkImageData = null;
|
||||||
|
module.exports.referenceLinkImageData = (value) => {
|
||||||
|
if (value) {
|
||||||
|
referenceLinkImageData = value;
|
||||||
|
}
|
||||||
|
return referenceLinkImageData;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports.clear = () => {
|
module.exports.clear = () => {
|
||||||
codeBlockAndSpanRanges = null;
|
codeBlockAndSpanRanges = null;
|
||||||
flattenedLists = null;
|
flattenedLists = null;
|
||||||
htmlElementRanges = null;
|
htmlElementRanges = null;
|
||||||
lineMetadata = null;
|
lineMetadata = null;
|
||||||
|
referenceLinkImageData = null;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -499,14 +499,17 @@ function lintContent(
|
||||||
frontMatterLines
|
frontMatterLines
|
||||||
});
|
});
|
||||||
const lineMetadata = helpers.getLineMetadata(paramsBase);
|
const lineMetadata = helpers.getLineMetadata(paramsBase);
|
||||||
cache.lineMetadata(lineMetadata);
|
|
||||||
cache.codeBlockAndSpanRanges(
|
cache.codeBlockAndSpanRanges(
|
||||||
helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata)
|
helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata)
|
||||||
);
|
);
|
||||||
|
cache.flattenedLists(helpers.flattenLists(paramsBase.tokens));
|
||||||
cache.htmlElementRanges(
|
cache.htmlElementRanges(
|
||||||
helpers.htmlElementRanges(paramsBase, lineMetadata)
|
helpers.htmlElementRanges(paramsBase, lineMetadata)
|
||||||
);
|
);
|
||||||
cache.flattenedLists(helpers.flattenLists(paramsBase.tokens));
|
cache.lineMetadata(lineMetadata);
|
||||||
|
cache.referenceLinkImageData(
|
||||||
|
helpers.getReferenceLinkImageData(paramsBase, lineMetadata)
|
||||||
|
);
|
||||||
// Function to run for each rule
|
// Function to run for each rule
|
||||||
let results = [];
|
let results = [];
|
||||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,12 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { addErrorDetailIf, filterTokens, forEachHeading, forEachLine,
|
const { addErrorDetailIf, filterTokens, forEachHeading, forEachLine,
|
||||||
includesSorted } = require("../helpers");
|
includesSorted, linkReferenceDefinitionRe } = require("../helpers");
|
||||||
const { lineMetadata } = require("./cache");
|
const { lineMetadata } = require("./cache");
|
||||||
|
|
||||||
const longLineRePrefix = "^.{";
|
const longLineRePrefix = "^.{";
|
||||||
const longLineRePostfixRelaxed = "}.*\\s.*$";
|
const longLineRePostfixRelaxed = "}.*\\s.*$";
|
||||||
const longLineRePostfixStrict = "}.+$";
|
const longLineRePostfixStrict = "}.+$";
|
||||||
const labelRe = /^\s*\[.*[^\\]]:/;
|
|
||||||
const linkOrImageOnlyLineRe = /^[es]*(lT?L|I)[ES]*$/;
|
const linkOrImageOnlyLineRe = /^[es]*(lT?L|I)[ES]*$/;
|
||||||
const sternModeRe = /^([#>\s]*\s)?\S*$/;
|
const sternModeRe = /^([#>\s]*\s)?\S*$/;
|
||||||
const tokenTypeMap = {
|
const tokenTypeMap = {
|
||||||
|
|
@ -83,7 +82,7 @@ module.exports = {
|
||||||
(strict ||
|
(strict ||
|
||||||
(!(stern && sternModeRe.test(line)) &&
|
(!(stern && sternModeRe.test(line)) &&
|
||||||
!includesSorted(linkOnlyLineNumbers, lineNumber) &&
|
!includesSorted(linkOnlyLineNumbers, lineNumber) &&
|
||||||
!labelRe.test(line))) &&
|
!linkReferenceDefinitionRe.test(line))) &&
|
||||||
lengthRe.test(line)) {
|
lengthRe.test(line)) {
|
||||||
addErrorDetailIf(
|
addErrorDetailIf(
|
||||||
onError,
|
onError,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { addErrorDetailIf, bareUrlRe, escapeForRegExp, forEachLine,
|
const { addErrorDetailIf, bareUrlRe, escapeForRegExp, forEachLine,
|
||||||
forEachLink, overlapsAnyRange, linkReferenceRe } = require("../helpers");
|
forEachLink, overlapsAnyRange, linkReferenceDefinitionRe } =
|
||||||
|
require("../helpers");
|
||||||
const { codeBlockAndSpanRanges, htmlElementRanges, lineMetadata } =
|
const { codeBlockAndSpanRanges, htmlElementRanges, lineMetadata } =
|
||||||
require("./cache");
|
require("./cache");
|
||||||
|
|
||||||
|
|
@ -23,7 +24,7 @@ module.exports = {
|
||||||
(htmlElements === undefined) ? true : !!htmlElements;
|
(htmlElements === undefined) ? true : !!htmlElements;
|
||||||
const exclusions = [];
|
const exclusions = [];
|
||||||
forEachLine(lineMetadata(), (line, lineIndex) => {
|
forEachLine(lineMetadata(), (line, lineIndex) => {
|
||||||
if (linkReferenceRe.test(line)) {
|
if (linkReferenceDefinitionRe.test(line)) {
|
||||||
exclusions.push([ lineIndex, 0, line.length ]);
|
exclusions.push([ lineIndex, 0, line.length ]);
|
||||||
} else {
|
} else {
|
||||||
let match = null;
|
let match = null;
|
||||||
|
|
|
||||||
35
lib/md052.js
Normal file
35
lib/md052.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { addError } = require("../helpers");
|
||||||
|
const { referenceLinkImageData } = require("./cache");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
"names": [ "MD052", "reference-links-images" ],
|
||||||
|
"description":
|
||||||
|
"Reference links and images should use a label that is defined",
|
||||||
|
"tags": [ "images", "links" ],
|
||||||
|
"function": function MD052(params, onError) {
|
||||||
|
const { lines } = params;
|
||||||
|
const { references, definitions } = referenceLinkImageData();
|
||||||
|
// Look for links/images that use an undefined link reference
|
||||||
|
for (const reference of references.entries()) {
|
||||||
|
const [ label, datas ] = reference;
|
||||||
|
if (!definitions.has(label)) {
|
||||||
|
for (const data of datas) {
|
||||||
|
const [ lineIndex, index, length ] = data;
|
||||||
|
// Context will be incomplete if reporting for a multi-line link
|
||||||
|
const context = lines[lineIndex].slice(index, index + length);
|
||||||
|
addError(
|
||||||
|
onError,
|
||||||
|
lineIndex + 1,
|
||||||
|
`Missing link or image reference definition: "${label}"`,
|
||||||
|
context,
|
||||||
|
[ index + 1, context.length ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
52
lib/md053.js
Normal file
52
lib/md053.js
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { addError, ellipsify, linkReferenceDefinitionRe } =
|
||||||
|
require("../helpers");
|
||||||
|
const { referenceLinkImageData } = require("./cache");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
"names": [ "MD053", "link-image-reference-definitions" ],
|
||||||
|
"description": "Link and image reference definitions should be needed",
|
||||||
|
"tags": [ "images", "links" ],
|
||||||
|
"function": function MD053(params, onError) {
|
||||||
|
const { lines } = params;
|
||||||
|
const { references, shortcuts, definitions, duplicateDefinitions } =
|
||||||
|
referenceLinkImageData();
|
||||||
|
const singleLineDefinition = (line) => (
|
||||||
|
line.replace(linkReferenceDefinitionRe, "").trim().length > 0
|
||||||
|
);
|
||||||
|
const deleteFixInfo = {
|
||||||
|
"deleteCount": -1
|
||||||
|
};
|
||||||
|
// Look for unused link references (unreferenced by any link/image)
|
||||||
|
for (const definition of definitions.entries()) {
|
||||||
|
const [ label, lineIndex ] = definition;
|
||||||
|
if (!references.has(label) && !shortcuts.has(label)) {
|
||||||
|
const line = lines[lineIndex];
|
||||||
|
addError(
|
||||||
|
onError,
|
||||||
|
lineIndex + 1,
|
||||||
|
`Unused link or image reference definition: "${label}"`,
|
||||||
|
ellipsify(line),
|
||||||
|
[ 1, line.length ],
|
||||||
|
singleLineDefinition(line) ? deleteFixInfo : 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Look for duplicate link references (defined more than once)
|
||||||
|
for (const duplicateDefinition of duplicateDefinitions) {
|
||||||
|
const [ label, lineIndex ] = duplicateDefinition;
|
||||||
|
const line = lines[lineIndex];
|
||||||
|
addError(
|
||||||
|
onError,
|
||||||
|
lineIndex + 1,
|
||||||
|
`Duplicate link or image reference definition: "${label}"`,
|
||||||
|
ellipsify(line),
|
||||||
|
[ 1, line.length ],
|
||||||
|
singleLineDefinition(line) ? deleteFixInfo : 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -50,7 +50,9 @@ const rules = [
|
||||||
require("./md047"),
|
require("./md047"),
|
||||||
require("./md048"),
|
require("./md048"),
|
||||||
...require("./md049-md050"),
|
...require("./md049-md050"),
|
||||||
require("./md051")
|
require("./md051"),
|
||||||
|
require("./md052"),
|
||||||
|
require("./md053")
|
||||||
];
|
];
|
||||||
rules.forEach((rule) => {
|
rules.forEach((rule) => {
|
||||||
const name = rule.names[0].toLowerCase();
|
const name = rule.names[0].toLowerCase();
|
||||||
|
|
|
||||||
|
|
@ -269,5 +269,11 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
// MD051/link-fragments - Link fragments should be valid
|
// MD051/link-fragments - Link fragments should be valid
|
||||||
"MD051": true
|
"MD051": true,
|
||||||
|
|
||||||
|
// MD052/reference-links-images - Reference links and images should use a label that is defined
|
||||||
|
"MD052": true,
|
||||||
|
|
||||||
|
// MD053/link-image-reference-definitions - Link and image reference definitions should be needed
|
||||||
|
"MD053": true
|
||||||
}
|
}
|
||||||
|
|
@ -244,3 +244,9 @@ MD050:
|
||||||
|
|
||||||
# MD051/link-fragments - Link fragments should be valid
|
# MD051/link-fragments - Link fragments should be valid
|
||||||
MD051: true
|
MD051: true
|
||||||
|
|
||||||
|
# MD052/reference-links-images - Reference links and images should use a label that is defined
|
||||||
|
MD052: true
|
||||||
|
|
||||||
|
# MD053/link-image-reference-definitions - Link and image reference definitions should be needed
|
||||||
|
MD053: true
|
||||||
|
|
@ -899,6 +899,22 @@
|
||||||
"link-fragments": {
|
"link-fragments": {
|
||||||
"$ref": "#/properties/MD051"
|
"$ref": "#/properties/MD051"
|
||||||
},
|
},
|
||||||
|
"MD052": {
|
||||||
|
"description": "MD052/reference-links-images - Reference links and images should use a label that is defined",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"reference-links-images": {
|
||||||
|
"$ref": "#/properties/MD052"
|
||||||
|
},
|
||||||
|
"MD053": {
|
||||||
|
"description": "MD053/link-image-reference-definitions - Link and image reference definitions should be needed",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"link-image-reference-definitions": {
|
||||||
|
"$ref": "#/properties/MD053"
|
||||||
|
},
|
||||||
"headings": {
|
"headings": {
|
||||||
"description": "headings - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043",
|
"description": "headings - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|
@ -935,7 +951,7 @@
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
"links": {
|
"links": {
|
||||||
"description": "links - MD011, MD034, MD039, MD042, MD051",
|
"description": "links - MD011, MD034, MD039, MD042, MD051, MD052, MD053",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
|
|
@ -1015,7 +1031,7 @@
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
"images": {
|
"images": {
|
||||||
"description": "images - MD045",
|
"description": "images - MD045, MD052, MD053",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,4 +95,7 @@ Strong **with** different style {MD050}
|
||||||
|
|
||||||
[Missing link fragment](#missing) {MD051}
|
[Missing link fragment](#missing) {MD051}
|
||||||
|
|
||||||
|
[Missing link][label] {MD052}
|
||||||
|
[unused]: link-destination {MD053}
|
||||||
|
|
||||||
EOF {MD047}
|
EOF {MD047}
|
||||||
|
|
@ -24,7 +24,7 @@ This long line includes a simple [reference][label] link and is long enough to v
|
||||||
|
|
||||||
[label]: https://example.org "Title for a link reference that is itself long enough to violate the rule"
|
[label]: https://example.org "Title for a link reference that is itself long enough to violate the rule"
|
||||||
|
|
||||||
[Link to broken label][notlabel]
|
[Link to broken label][notlabel] {MD052}
|
||||||
|
|
||||||
[notlabel\]: notlink "Invalid syntax for a link label because the right bracket is backslash-escaped {MD013}"
|
[notlabel\]: notlink "Invalid syntax for a link label because the right bracket is backslash-escaped {MD013}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,12 +125,16 @@ function excludeGlobs(rootDir, ...globs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run markdownlint the same way the corresponding repositories do
|
// Run markdownlint the same way the corresponding repositories do
|
||||||
|
/* eslint-disable max-len */
|
||||||
|
|
||||||
test("https://github.com/eslint/eslint", (t) => {
|
test("https://github.com/eslint/eslint", (t) => {
|
||||||
const rootDir = "./test-repos/eslint-eslint";
|
const rootDir = "./test-repos/eslint-eslint";
|
||||||
const globPatterns = [ join(rootDir, "docs/**/*.md") ];
|
const globPatterns = [ join(rootDir, "docs/**/*.md") ];
|
||||||
const configPath = join(rootDir, ".markdownlint.yml");
|
const configPath = join(rootDir, ".markdownlint.yml");
|
||||||
const ignoreRes = [ /^[^:]+: \d+: MD051\/.*$\r?\n?/gm ];
|
const ignoreRes = [
|
||||||
|
/^[^:]+: \d+: MD051\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/eslint-eslint\/docs\/src\/developer-guide\/nodejs-api\.md: \d+: MD053\/.*$\r?\n?/gm
|
||||||
|
];
|
||||||
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -148,7 +152,19 @@ test("https://github.com/mkdocs/mkdocs", (t) => {
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
const configPath = join(rootDir, ".markdownlintrc");
|
const configPath = join(rootDir, ".markdownlintrc");
|
||||||
const ignoreRes = [ /^[^:]+: \d+: MD051\/.*$\r?\n?/gm ];
|
const ignoreRes = [
|
||||||
|
/^[^:]+: \d+: MD051\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mkdocs-mkdocs\/docs\/about\/release-notes\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mkdocs-mkdocs\/docs\/dev-guide\/plugins\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mkdocs-mkdocs\/docs\/dev-guide\/themes\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mkdocs-mkdocs\/docs\/dev-guide\/translations\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mkdocs-mkdocs\/docs\/getting-started\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mkdocs-mkdocs\/docs\/user-guide\/configuration\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mkdocs-mkdocs\/docs\/user-guide\/customizing-your-theme\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mkdocs-mkdocs\/docs\/user-guide\/deploying-your-docs\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mkdocs-mkdocs\/docs\/user-guide\/installation\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mkdocs-mkdocs\/docs\/user-guide\/writing-your-docs\.md: \d+: MD053\/.*$\r?\n?/gm
|
||||||
|
];
|
||||||
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -163,7 +179,10 @@ test("https://github.com/mochajs/mocha", (t) => {
|
||||||
join(rootDir, "example/**/*.md")
|
join(rootDir, "example/**/*.md")
|
||||||
];
|
];
|
||||||
const configPath = join(rootDir, ".markdownlint.json");
|
const configPath = join(rootDir, ".markdownlint.json");
|
||||||
const ignoreRes = [ /^[^:]+: \d+: MD051\/.*$\r?\n?/gm ];
|
const ignoreRes = [
|
||||||
|
/^[^:]+: \d+: MD051\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/mochajs-mocha\/docs\/index\.md: \d+: MD053\/.*$\r?\n?/gm
|
||||||
|
];
|
||||||
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -171,7 +190,10 @@ test("https://github.com/pi-hole/docs", (t) => {
|
||||||
const rootDir = "./test-repos/pi-hole-docs";
|
const rootDir = "./test-repos/pi-hole-docs";
|
||||||
const globPatterns = [ join(rootDir, "**/*.md") ];
|
const globPatterns = [ join(rootDir, "**/*.md") ];
|
||||||
const configPath = join(rootDir, ".markdownlint.json");
|
const configPath = join(rootDir, ".markdownlint.json");
|
||||||
const ignoreRes = [ /^[^:]+: \d+: MD051\/.*$\r?\n?/gm ];
|
const ignoreRes = [
|
||||||
|
/^[^:]+: \d+: MD051\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/pi-hole-docs\/docs\/guides\/dns\/cloudflared\.md: \d+: MD053\/.*$\r?\n?/gm
|
||||||
|
];
|
||||||
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -182,14 +204,38 @@ test("https://github.com/webhintio/hint", (t) => {
|
||||||
...excludeGlobs(rootDir, "**/CHANGELOG.md")
|
...excludeGlobs(rootDir, "**/CHANGELOG.md")
|
||||||
];
|
];
|
||||||
const configPath = join(rootDir, ".markdownlintrc");
|
const configPath = join(rootDir, ".markdownlintrc");
|
||||||
return lintTestRepo(t, globPatterns, configPath);
|
const ignoreRes = [
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-apple-touch-icons\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-axe\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-compat-api\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-compat-api\/docs\/html\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-doctype\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-highest-available-document-mode\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-http-compression\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-meta-viewport\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-minified-js\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-no-p3p\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-performance-budget\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-strict-transport-security\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint-x-content-type-options\/README\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint\/docs\/about\/GOVERNANCE.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint\/docs\/contributor-guide\/getting-started\/architecture\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint\/docs\/contributor-guide\/getting-started\/development-environment\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint\/docs\/contributor-guide\/how-to\/hint\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint\/docs\/user-guide\/development-flow-integration\/local-server\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint\/docs\/user-guide\/index\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/hint\/docs\/user-guide\/troubleshoot\/summary\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/test-repos\/webhintio-hint\/packages\/parser-html\/README\.md: \d+: MD053\/.*$\r?\n?/gm
|
||||||
|
];
|
||||||
|
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("https://github.com/webpack/webpack.js.org", (t) => {
|
test("https://github.com/webpack/webpack.js.org", (t) => {
|
||||||
const rootDir = "./test-repos/webpack-webpack-js-org";
|
const rootDir = "./test-repos/webpack-webpack-js-org";
|
||||||
const globPatterns = [ join(rootDir, "**/*.md") ];
|
const globPatterns = [ join(rootDir, "**/*.md") ];
|
||||||
const configPath = join(rootDir, ".markdownlint.json");
|
const configPath = join(rootDir, ".markdownlint.json");
|
||||||
return lintTestRepo(t, globPatterns, configPath);
|
const ignoreRes = [ /^test-repos\/webpack-webpack-js-org\/README\.md: \d+: MD053\/.*$\r?\n?/gm ];
|
||||||
|
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Optional repositories (very large)
|
// Optional repositories (very large)
|
||||||
|
|
@ -200,7 +246,19 @@ if (existsSync(dotnetDocsDir)) {
|
||||||
const rootDir = dotnetDocsDir;
|
const rootDir = dotnetDocsDir;
|
||||||
const globPatterns = [ join(rootDir, "**/*.md") ];
|
const globPatterns = [ join(rootDir, "**/*.md") ];
|
||||||
const configPath = join(rootDir, ".markdownlint.json");
|
const configPath = join(rootDir, ".markdownlint.json");
|
||||||
const ignoreRes = [ /^[^:]+: \d+: MD051\/.*$\r?\n?/gm ];
|
const ignoreRes = [
|
||||||
|
/^[^:]+: \d+: MD051\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/dotnet-docs\/docs\/core\/diagnostics\/sos-debugging-extension\.md: \d+: MD052\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/dotnet-docs\/docs\/core\/install\/macos\.md: \d+: MD053\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/dotnet-docs\/docs\/framework\/data\/adonet\/dataset-datatable-dataview\/diffgrams\.md: \d+: MD052\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/dotnet-docs\/docs\/framework\/tools\/al-exe-assembly-linker\.md: \d+: MD052\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/dotnet-docs\/docs\/framework\/tools\/sos-dll-sos-debugging-extension\.md: \d+: MD052\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/dotnet-docs\/docs\/fsharp\/language-reference\/compiler-options\.md: \d+: MD052\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/dotnet-docs\/docs\/fundamentals\/code-analysis\/quality-rules\/ca2241\.md: \d+: MD052\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/dotnet-docs\/docs\/standard\/base-types\/composite-formatting\.md: \d+: MD052\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/dotnet-docs\/docs\/standard\/base-types\/standard-timespan-format-strings\.md: \d+: MD052\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/dotnet-docs\/docs\/standard\/native-interop\/tutorial-comwrappers\.md: \d+: MD053\/.*$\r?\n?/gm
|
||||||
|
];
|
||||||
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -211,7 +269,10 @@ if (existsSync(v8v8DevDir)) {
|
||||||
const rootDir = v8v8DevDir;
|
const rootDir = v8v8DevDir;
|
||||||
const globPatterns = [ join(rootDir, "src/**/*.md") ];
|
const globPatterns = [ join(rootDir, "src/**/*.md") ];
|
||||||
const configPath = join(rootDir, ".markdownlint.json");
|
const configPath = join(rootDir, ".markdownlint.json");
|
||||||
const ignoreRes = [ /^[^:]+: \d+: (MD049|MD051)\/.*$\r?\n?/gm ];
|
const ignoreRes = [
|
||||||
|
/^[^:]+: \d+: (MD049|MD051)\/.*$\r?\n?/gm,
|
||||||
|
/^test-repos\/v8-v8-dev\/src\/blog\/oilpan-library\.md: \d+: MD053\/.*$\r?\n?/gm
|
||||||
|
];
|
||||||
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
return lintTestRepo(t, globPatterns, configPath, ignoreRes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -488,11 +488,13 @@ test.cb("styleAll", (t) => {
|
||||||
"MD042": [ 81 ],
|
"MD042": [ 81 ],
|
||||||
"MD045": [ 85 ],
|
"MD045": [ 85 ],
|
||||||
"MD046": [ 49, 73, 77 ],
|
"MD046": [ 49, 73, 77 ],
|
||||||
"MD047": [ 98 ],
|
"MD047": [ 101 ],
|
||||||
"MD048": [ 77 ],
|
"MD048": [ 77 ],
|
||||||
"MD049": [ 90 ],
|
"MD049": [ 90 ],
|
||||||
"MD050": [ 94 ],
|
"MD050": [ 94 ],
|
||||||
"MD051": [ 96 ]
|
"MD051": [ 96 ],
|
||||||
|
"MD052": [ 98 ],
|
||||||
|
"MD053": [ 99 ]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
@ -534,11 +536,13 @@ test.cb("styleRelaxed", (t) => {
|
||||||
"MD042": [ 81 ],
|
"MD042": [ 81 ],
|
||||||
"MD045": [ 85 ],
|
"MD045": [ 85 ],
|
||||||
"MD046": [ 49, 73, 77 ],
|
"MD046": [ 49, 73, 77 ],
|
||||||
"MD047": [ 98 ],
|
"MD047": [ 101 ],
|
||||||
"MD048": [ 77 ],
|
"MD048": [ 77 ],
|
||||||
"MD049": [ 90 ],
|
"MD049": [ 90 ],
|
||||||
"MD050": [ 94 ],
|
"MD050": [ 94 ],
|
||||||
"MD051": [ 96 ]
|
"MD051": [ 96 ],
|
||||||
|
"MD052": [ 98 ],
|
||||||
|
"MD053": [ 99 ]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
@ -840,7 +844,7 @@ test.cb("customFileSystemAsync", (t) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test.cb("readme", (t) => {
|
test.cb("readme", (t) => {
|
||||||
t.plan(121);
|
t.plan(125);
|
||||||
const tagToRules = {};
|
const tagToRules = {};
|
||||||
rules.forEach(function forRule(rule) {
|
rules.forEach(function forRule(rule) {
|
||||||
rule.tags.forEach(function forTag(tag) {
|
rule.tags.forEach(function forTag(tag) {
|
||||||
|
|
@ -916,7 +920,7 @@ test.cb("readme", (t) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test.cb("rules", (t) => {
|
test.cb("rules", (t) => {
|
||||||
t.plan(359);
|
t.plan(373);
|
||||||
fs.readFile("doc/Rules.md", "utf8",
|
fs.readFile("doc/Rules.md", "utf8",
|
||||||
(err, contents) => {
|
(err, contents) => {
|
||||||
t.falsy(err);
|
t.falsy(err);
|
||||||
|
|
@ -1093,7 +1097,7 @@ test("validateConfigExampleJson", async(t) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("allBuiltInRulesHaveValidUrl", (t) => {
|
test("allBuiltInRulesHaveValidUrl", (t) => {
|
||||||
t.plan(141);
|
t.plan(147);
|
||||||
rules.forEach(function forRule(rule) {
|
rules.forEach(function forRule(rule) {
|
||||||
t.truthy(rule.information);
|
t.truthy(rule.information);
|
||||||
t.true(Object.getPrototypeOf(rule.information) === URL.prototype);
|
t.true(Object.getPrototypeOf(rule.information) === URL.prototype);
|
||||||
|
|
|
||||||
164
test/reference-links-and-images.md
Normal file
164
test/reference-links-and-images.md
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
# Reference Links and Images
|
||||||
|
|
||||||
|
## Valid Links
|
||||||
|
|
||||||
|
Full reference link: [text][label]
|
||||||
|
|
||||||
|
Collapsed reference link: [label][]
|
||||||
|
|
||||||
|
Shortcut reference link: [label]
|
||||||
|
|
||||||
|
Same line: [text][label] [label][] [label]
|
||||||
|
|
||||||
|
Mixed case: [TEXT][LABEL] [LABEL][] [LABEL]
|
||||||
|
|
||||||
|
With spaces: [text][label with spaces] [text][ label with spaces ]
|
||||||
|
|
||||||
|
With nested brackets: [t[ex]t][label]
|
||||||
|
|
||||||
|
With inline content: [*text*][label]
|
||||||
|
|
||||||
|
With inline code span: [`code`][label]
|
||||||
|
|
||||||
|
Shortcut inline code span: [`code`]
|
||||||
|
|
||||||
|
Multi-line full text: [multi
|
||||||
|
line][multi line full text]
|
||||||
|
|
||||||
|
Multi-line full label: [text][multi
|
||||||
|
line full label]
|
||||||
|
|
||||||
|
Multi-line collapsed label: [multi
|
||||||
|
line collapsed label][]
|
||||||
|
|
||||||
|
Multi-line shortcut label: [multi line
|
||||||
|
shortcut label]
|
||||||
|
|
||||||
|
Dedicated line:
|
||||||
|
[text][label]
|
||||||
|
|
||||||
|
Dedicated line with trailing colon:
|
||||||
|
[text][label]:
|
||||||
|
|
||||||
|
Shortcut ending in colon: [colon]:
|
||||||
|
|
||||||
|
Use of multi-line label: [multi-line-label][]
|
||||||
|
|
||||||
|
Standard link: [text](https://example.com/standard)
|
||||||
|
|
||||||
|
## Invalid Links
|
||||||
|
|
||||||
|
Missing label: [text][missing] {MD052}
|
||||||
|
|
||||||
|
Mixed valid/invalid: [text][label] [text][missing] {MD052}
|
||||||
|
|
||||||
|
Missing multi-line label {MD052}: [text][missing
|
||||||
|
label]
|
||||||
|
|
||||||
|
## Non-Links
|
||||||
|
|
||||||
|
Space: [text] [wrong]
|
||||||
|
|
||||||
|
Empty: [text][ ]
|
||||||
|
|
||||||
|
Code span: `[text][wrong]`
|
||||||
|
|
||||||
|
Escaped left text: \[text][wrong]
|
||||||
|
|
||||||
|
Escaped right text: [text\][wrong]
|
||||||
|
|
||||||
|
Escaped left label: [text]\[wrong]
|
||||||
|
|
||||||
|
Escaped right label: [text][wrong\]
|
||||||
|
|
||||||
|
## Valid Images
|
||||||
|
|
||||||
|
Full style: ![text][image]
|
||||||
|
|
||||||
|
Collapsed style: ![image][]
|
||||||
|
|
||||||
|
Shortcut style: ![image]
|
||||||
|
|
||||||
|
Image in link: [![text][image]][label] [![image][]][label] [![image]][label]
|
||||||
|
|
||||||
|
## Invalid Images
|
||||||
|
|
||||||
|
Image only: ![text][missing] {MD052}
|
||||||
|
|
||||||
|
Image in link: [![text][missing]][label] {MD052}
|
||||||
|
|
||||||
|
## Non-Images
|
||||||
|
|
||||||
|
Escaped left text: !\[text][wrong]
|
||||||
|
|
||||||
|
Escaped right text: ![text\][wrong]
|
||||||
|
|
||||||
|
Escaped left label: ![text]\[wrong]
|
||||||
|
|
||||||
|
Escaped right label: ![text][wrong\]
|
||||||
|
|
||||||
|
## Valid Footnotes
|
||||||
|
|
||||||
|
Footnote[^1]
|
||||||
|
|
||||||
|
## Invalid Footnotes
|
||||||
|
|
||||||
|
Missing[^2]
|
||||||
|
|
||||||
|
## Valid Labels
|
||||||
|
|
||||||
|
[label]: https://example.com/label
|
||||||
|
[ label with spaces ]: https://example.com/label-with-spaces
|
||||||
|
[image]:https://example.com/image
|
||||||
|
[`code`]: https://example.com/code
|
||||||
|
[^1]: https://example.com/footnote
|
||||||
|
[multi line full text]: https://example.com/multi-line-full-text
|
||||||
|
[multi line full label]: https://example.com/multi-line-full-label
|
||||||
|
[multi line collapsed label]: https://example.com/multi-line-collapsed-label
|
||||||
|
[multi line shortcut label]: https://example.com/multi-line-shortcut-label
|
||||||
|
[colon]: https://example.com/colon
|
||||||
|
[multi-line-label]:
|
||||||
|
https://example.com/multi-line-label
|
||||||
|
|
||||||
|
## Invalid Labels
|
||||||
|
|
||||||
|
Duplicate:
|
||||||
|
[label]: {MD053}
|
||||||
|
|
||||||
|
Unused:
|
||||||
|
[unused]: {MD053}
|
||||||
|
|
||||||
|
Unused footnote:
|
||||||
|
[^3]: {MD053}
|
||||||
|
|
||||||
|
[Duplicate unused multi-line label {MD053}]:
|
||||||
|
https://example.com/duplicate-unused-multi-line-label
|
||||||
|
|
||||||
|
[Duplicate unused multi-line label {MD053}]:
|
||||||
|
https://example.com/duplicate-unused-multi-line-label
|
||||||
|
|
||||||
|
\[Escaped left]: text
|
||||||
|
|
||||||
|
[Escaped right\]: text
|
||||||
|
|
||||||
|
## Valid Links and Images after Labels
|
||||||
|
|
||||||
|
Link and image: [text][label] [![text][image]][label]
|
||||||
|
|
||||||
|
## More Invalid Links and Images after Labels
|
||||||
|
|
||||||
|
Bad link with image [![text][image]][missing] {MD052}
|
||||||
|
|
||||||
|
## Shortcut One-Way Handling
|
||||||
|
|
||||||
|
Validates the label: [shortcut]
|
||||||
|
|
||||||
|
[shortcut]: https://example.com/shortcut
|
||||||
|
|
||||||
|
Not flagged due to ambiguity: [ignored]
|
||||||
|
|
||||||
|
## Open Bracket Pairs
|
||||||
|
|
||||||
|
Unmatched [ in text
|
||||||
|
|
||||||
|
Hidden reference: [hidden][] {MD052}
|
||||||
|
|
@ -5750,7 +5750,7 @@ Generated by [AVA](https://avajs.dev).
|
||||||
insertText: `␊
|
insertText: `␊
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
lineNumber: 98,
|
lineNumber: 101,
|
||||||
ruleDescription: 'Files should end with a single newline character',
|
ruleDescription: 'Files should end with a single newline character',
|
||||||
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md047',
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md047',
|
||||||
ruleNames: [
|
ruleNames: [
|
||||||
|
|
@ -5827,6 +5827,40 @@ Generated by [AVA](https://avajs.dev).
|
||||||
'link-fragments',
|
'link-fragments',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[Missing link][label]',
|
||||||
|
errorDetail: 'Missing link or image reference definition: "label"',
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
21,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 98,
|
||||||
|
ruleDescription: 'Reference links and images should use a label that is defined',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md052',
|
||||||
|
ruleNames: [
|
||||||
|
'MD052',
|
||||||
|
'reference-links-images',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[unused]: link-destination {MD...',
|
||||||
|
errorDetail: 'Unused link or image reference definition: "unused"',
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
34,
|
||||||
|
],
|
||||||
|
fixInfo: {
|
||||||
|
deleteCount: -1,
|
||||||
|
},
|
||||||
|
lineNumber: 99,
|
||||||
|
ruleDescription: 'Link and image reference definitions should be needed',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md053',
|
||||||
|
ruleNames: [
|
||||||
|
'MD053',
|
||||||
|
'link-image-reference-definitions',
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
fixed: `## Heading 1 {MD002} {MD041}␊
|
fixed: `## Heading 1 {MD002} {MD041}␊
|
||||||
␊
|
␊
|
||||||
|
|
@ -5927,6 +5961,8 @@ Generated by [AVA](https://avajs.dev).
|
||||||
␊
|
␊
|
||||||
[Missing link fragment](#missing) {MD051}␊
|
[Missing link fragment](#missing) {MD051}␊
|
||||||
␊
|
␊
|
||||||
|
[Missing link][label] {MD052}␊
|
||||||
|
␊
|
||||||
EOF {MD047}␊
|
EOF {MD047}␊
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
@ -27459,6 +27495,22 @@ Generated by [AVA](https://avajs.dev).
|
||||||
'strong-style',
|
'strong-style',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[Link to broken label][notlabel]',
|
||||||
|
errorDetail: 'Missing link or image reference definition: "notlabel"',
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
32,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 27,
|
||||||
|
ruleDescription: 'Reference links and images should use a label that is defined',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md052',
|
||||||
|
ruleNames: [
|
||||||
|
'MD052',
|
||||||
|
'reference-links-images',
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
fixed: `# Long Lines␊
|
fixed: `# Long Lines␊
|
||||||
␊
|
␊
|
||||||
|
|
@ -27486,7 +27538,7 @@ Generated by [AVA](https://avajs.dev).
|
||||||
␊
|
␊
|
||||||
[label]: https://example.org "Title for a link reference that is itself long enough to violate the rule"␊
|
[label]: https://example.org "Title for a link reference that is itself long enough to violate the rule"␊
|
||||||
␊
|
␊
|
||||||
[Link to broken label][notlabel]␊
|
[Link to broken label][notlabel] {MD052}␊
|
||||||
␊
|
␊
|
||||||
[notlabel\\]: notlink "Invalid syntax for a link label because the right bracket is backslash-escaped {MD013}"␊
|
[notlabel\\]: notlink "Invalid syntax for a link label because the right bracket is backslash-escaped {MD013}"␊
|
||||||
␊
|
␊
|
||||||
|
|
@ -33027,6 +33079,375 @@ Generated by [AVA](https://avajs.dev).
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## reference-links-and-images.md
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
{
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
errorContext: '[text][missing]',
|
||||||
|
errorDetail: 'Missing link or image reference definition: "missing"',
|
||||||
|
errorRange: [
|
||||||
|
16,
|
||||||
|
15,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 51,
|
||||||
|
ruleDescription: 'Reference links and images should use a label that is defined',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md052',
|
||||||
|
ruleNames: [
|
||||||
|
'MD052',
|
||||||
|
'reference-links-images',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[text][missing]',
|
||||||
|
errorDetail: 'Missing link or image reference definition: "missing"',
|
||||||
|
errorRange: [
|
||||||
|
36,
|
||||||
|
15,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 53,
|
||||||
|
ruleDescription: 'Reference links and images should use a label that is defined',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md052',
|
||||||
|
ruleNames: [
|
||||||
|
'MD052',
|
||||||
|
'reference-links-images',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[text][missing',
|
||||||
|
errorDetail: 'Missing link or image reference definition: "missing label"',
|
||||||
|
errorRange: [
|
||||||
|
35,
|
||||||
|
14,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 55,
|
||||||
|
ruleDescription: 'Reference links and images should use a label that is defined',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md052',
|
||||||
|
ruleNames: [
|
||||||
|
'MD052',
|
||||||
|
'reference-links-images',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '![text][missing]',
|
||||||
|
errorDetail: 'Missing link or image reference definition: "missing"',
|
||||||
|
errorRange: [
|
||||||
|
13,
|
||||||
|
16,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 86,
|
||||||
|
ruleDescription: 'Reference links and images should use a label that is defined',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md052',
|
||||||
|
ruleNames: [
|
||||||
|
'MD052',
|
||||||
|
'reference-links-images',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '![text][missing]',
|
||||||
|
errorDetail: 'Missing link or image reference definition: "missing"',
|
||||||
|
errorRange: [
|
||||||
|
17,
|
||||||
|
16,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 88,
|
||||||
|
ruleDescription: 'Reference links and images should use a label that is defined',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md052',
|
||||||
|
ruleNames: [
|
||||||
|
'MD052',
|
||||||
|
'reference-links-images',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[![text][image]][missing]',
|
||||||
|
errorDetail: 'Missing link or image reference definition: "missing"',
|
||||||
|
errorRange: [
|
||||||
|
21,
|
||||||
|
25,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 150,
|
||||||
|
ruleDescription: 'Reference links and images should use a label that is defined',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md052',
|
||||||
|
ruleNames: [
|
||||||
|
'MD052',
|
||||||
|
'reference-links-images',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[hidden][]',
|
||||||
|
errorDetail: 'Missing link or image reference definition: "hidden"',
|
||||||
|
errorRange: [
|
||||||
|
19,
|
||||||
|
10,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 164,
|
||||||
|
ruleDescription: 'Reference links and images should use a label that is defined',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md052',
|
||||||
|
ruleNames: [
|
||||||
|
'MD052',
|
||||||
|
'reference-links-images',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[label]: {MD053}',
|
||||||
|
errorDetail: 'Duplicate link or image reference definition: "label"',
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
16,
|
||||||
|
],
|
||||||
|
fixInfo: {
|
||||||
|
deleteCount: -1,
|
||||||
|
},
|
||||||
|
lineNumber: 126,
|
||||||
|
ruleDescription: 'Link and image reference definitions should be needed',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md053',
|
||||||
|
ruleNames: [
|
||||||
|
'MD053',
|
||||||
|
'link-image-reference-definitions',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[unused]: {MD053}',
|
||||||
|
errorDetail: 'Unused link or image reference definition: "unused"',
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
17,
|
||||||
|
],
|
||||||
|
fixInfo: {
|
||||||
|
deleteCount: -1,
|
||||||
|
},
|
||||||
|
lineNumber: 129,
|
||||||
|
ruleDescription: 'Link and image reference definitions should be needed',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md053',
|
||||||
|
ruleNames: [
|
||||||
|
'MD053',
|
||||||
|
'link-image-reference-definitions',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[^3]: {MD053}',
|
||||||
|
errorDetail: 'Unused link or image reference definition: "^3"',
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
13,
|
||||||
|
],
|
||||||
|
fixInfo: {
|
||||||
|
deleteCount: -1,
|
||||||
|
},
|
||||||
|
lineNumber: 132,
|
||||||
|
ruleDescription: 'Link and image reference definitions should be needed',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md053',
|
||||||
|
ruleNames: [
|
||||||
|
'MD053',
|
||||||
|
'link-image-reference-definitions',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[Duplicate unused multi-line l...',
|
||||||
|
errorDetail: 'Unused link or image reference definition: "duplicate unused multi-line label {md053}"',
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
44,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 134,
|
||||||
|
ruleDescription: 'Link and image reference definitions should be needed',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md053',
|
||||||
|
ruleNames: [
|
||||||
|
'MD053',
|
||||||
|
'link-image-reference-definitions',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorContext: '[Duplicate unused multi-line l...',
|
||||||
|
errorDetail: 'Duplicate link or image reference definition: "duplicate unused multi-line label {md053}"',
|
||||||
|
errorRange: [
|
||||||
|
1,
|
||||||
|
44,
|
||||||
|
],
|
||||||
|
fixInfo: null,
|
||||||
|
lineNumber: 137,
|
||||||
|
ruleDescription: 'Link and image reference definitions should be needed',
|
||||||
|
ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md053',
|
||||||
|
ruleNames: [
|
||||||
|
'MD053',
|
||||||
|
'link-image-reference-definitions',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fixed: `# Reference Links and Images␊
|
||||||
|
␊
|
||||||
|
## Valid Links␊
|
||||||
|
␊
|
||||||
|
Full reference link: [text][label]␊
|
||||||
|
␊
|
||||||
|
Collapsed reference link: [label][]␊
|
||||||
|
␊
|
||||||
|
Shortcut reference link: [label]␊
|
||||||
|
␊
|
||||||
|
Same line: [text][label] [label][] [label]␊
|
||||||
|
␊
|
||||||
|
Mixed case: [TEXT][LABEL] [LABEL][] [LABEL]␊
|
||||||
|
␊
|
||||||
|
With spaces: [text][label with spaces] [text][ label with spaces ]␊
|
||||||
|
␊
|
||||||
|
With nested brackets: [t[ex]t][label]␊
|
||||||
|
␊
|
||||||
|
With inline content: [*text*][label]␊
|
||||||
|
␊
|
||||||
|
With inline code span: [\`code\`][label]␊
|
||||||
|
␊
|
||||||
|
Shortcut inline code span: [\`code\`]␊
|
||||||
|
␊
|
||||||
|
Multi-line full text: [multi␊
|
||||||
|
line][multi line full text]␊
|
||||||
|
␊
|
||||||
|
Multi-line full label: [text][multi␊
|
||||||
|
line full label]␊
|
||||||
|
␊
|
||||||
|
Multi-line collapsed label: [multi␊
|
||||||
|
line collapsed label][]␊
|
||||||
|
␊
|
||||||
|
Multi-line shortcut label: [multi line␊
|
||||||
|
shortcut label]␊
|
||||||
|
␊
|
||||||
|
Dedicated line:␊
|
||||||
|
[text][label]␊
|
||||||
|
␊
|
||||||
|
Dedicated line with trailing colon:␊
|
||||||
|
[text][label]:␊
|
||||||
|
␊
|
||||||
|
Shortcut ending in colon: [colon]:␊
|
||||||
|
␊
|
||||||
|
Use of multi-line label: [multi-line-label][]␊
|
||||||
|
␊
|
||||||
|
Standard link: [text](https://example.com/standard)␊
|
||||||
|
␊
|
||||||
|
## Invalid Links␊
|
||||||
|
␊
|
||||||
|
Missing label: [text][missing] {MD052}␊
|
||||||
|
␊
|
||||||
|
Mixed valid/invalid: [text][label] [text][missing] {MD052}␊
|
||||||
|
␊
|
||||||
|
Missing multi-line label {MD052}: [text][missing␊
|
||||||
|
label]␊
|
||||||
|
␊
|
||||||
|
## Non-Links␊
|
||||||
|
␊
|
||||||
|
Space: [text] [wrong]␊
|
||||||
|
␊
|
||||||
|
Empty: [text][ ]␊
|
||||||
|
␊
|
||||||
|
Code span: \`[text][wrong]\`␊
|
||||||
|
␊
|
||||||
|
Escaped left text: \\[text][wrong]␊
|
||||||
|
␊
|
||||||
|
Escaped right text: [text\\][wrong]␊
|
||||||
|
␊
|
||||||
|
Escaped left label: [text]\\[wrong]␊
|
||||||
|
␊
|
||||||
|
Escaped right label: [text][wrong\\]␊
|
||||||
|
␊
|
||||||
|
## Valid Images␊
|
||||||
|
␊
|
||||||
|
Full style: ![text][image]␊
|
||||||
|
␊
|
||||||
|
Collapsed style: ![image][]␊
|
||||||
|
␊
|
||||||
|
Shortcut style: ![image]␊
|
||||||
|
␊
|
||||||
|
Image in link: [![text][image]][label] [![image][]][label] [![image]][label]␊
|
||||||
|
␊
|
||||||
|
## Invalid Images␊
|
||||||
|
␊
|
||||||
|
Image only: ![text][missing] {MD052}␊
|
||||||
|
␊
|
||||||
|
Image in link: [![text][missing]][label] {MD052}␊
|
||||||
|
␊
|
||||||
|
## Non-Images␊
|
||||||
|
␊
|
||||||
|
Escaped left text: !\\[text][wrong]␊
|
||||||
|
␊
|
||||||
|
Escaped right text: ![text\\][wrong]␊
|
||||||
|
␊
|
||||||
|
Escaped left label: ![text]\\[wrong]␊
|
||||||
|
␊
|
||||||
|
Escaped right label: ![text][wrong\\]␊
|
||||||
|
␊
|
||||||
|
## Valid Footnotes␊
|
||||||
|
␊
|
||||||
|
Footnote[^1]␊
|
||||||
|
␊
|
||||||
|
## Invalid Footnotes␊
|
||||||
|
␊
|
||||||
|
Missing[^2]␊
|
||||||
|
␊
|
||||||
|
## Valid Labels␊
|
||||||
|
␊
|
||||||
|
[label]: https://example.com/label␊
|
||||||
|
[ label with spaces ]: https://example.com/label-with-spaces␊
|
||||||
|
[image]:https://example.com/image␊
|
||||||
|
[\`code\`]: https://example.com/code␊
|
||||||
|
[^1]: https://example.com/footnote␊
|
||||||
|
[multi line full text]: https://example.com/multi-line-full-text␊
|
||||||
|
[multi line full label]: https://example.com/multi-line-full-label␊
|
||||||
|
[multi line collapsed label]: https://example.com/multi-line-collapsed-label␊
|
||||||
|
[multi line shortcut label]: https://example.com/multi-line-shortcut-label␊
|
||||||
|
[colon]: https://example.com/colon␊
|
||||||
|
[multi-line-label]:␊
|
||||||
|
https://example.com/multi-line-label␊
|
||||||
|
␊
|
||||||
|
## Invalid Labels␊
|
||||||
|
␊
|
||||||
|
Duplicate:␊
|
||||||
|
␊
|
||||||
|
Unused:␊
|
||||||
|
␊
|
||||||
|
Unused footnote:␊
|
||||||
|
␊
|
||||||
|
[Duplicate unused multi-line label {MD053}]:␊
|
||||||
|
https://example.com/duplicate-unused-multi-line-label␊
|
||||||
|
␊
|
||||||
|
[Duplicate unused multi-line label {MD053}]:␊
|
||||||
|
https://example.com/duplicate-unused-multi-line-label␊
|
||||||
|
␊
|
||||||
|
\\[Escaped left]: text␊
|
||||||
|
␊
|
||||||
|
[Escaped right\\]: text␊
|
||||||
|
␊
|
||||||
|
## Valid Links and Images after Labels␊
|
||||||
|
␊
|
||||||
|
Link and image: [text][label] [![text][image]][label]␊
|
||||||
|
␊
|
||||||
|
## More Invalid Links and Images after Labels␊
|
||||||
|
␊
|
||||||
|
Bad link with image [![text][image]][missing] {MD052}␊
|
||||||
|
␊
|
||||||
|
## Shortcut One-Way Handling␊
|
||||||
|
␊
|
||||||
|
Validates the label: [shortcut]␊
|
||||||
|
␊
|
||||||
|
[shortcut]: https://example.com/shortcut␊
|
||||||
|
␊
|
||||||
|
Not flagged due to ambiguity: [ignored]␊
|
||||||
|
␊
|
||||||
|
## Open Bracket Pairs␊
|
||||||
|
␊
|
||||||
|
Unmatched [ in text␊
|
||||||
|
␊
|
||||||
|
Hidden reference: [hidden][] {MD052}␊
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
## required-headings-all-optional-at-least-one.md
|
## required-headings-all-optional-at-least-one.md
|
||||||
|
|
||||||
> Snapshot 1
|
> Snapshot 1
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue