first commit
This commit is contained in:
+117
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @fileoverview Rule to prevent non-complaint link references.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
//-----------------------------------------------------------------------------
|
||||
// Imports
|
||||
//-----------------------------------------------------------------------------
|
||||
import { illegalShorthandTailPattern } from "../util.js";
|
||||
//-----------------------------------------------------------------------------
|
||||
// Type Definitions
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* @import { Position } from "unist";
|
||||
* @import { Text } from "mdast";
|
||||
* @import { MarkdownRuleDefinition } from "../types.js";
|
||||
* @import { MarkdownSourceCode } from "../language/markdown-source-code.js";
|
||||
* @typedef {"invalidLabelRef"} NoInvalidLabelRefsMessageIds
|
||||
* @typedef {[]} NoInvalidLabelRefsOptions
|
||||
* @typedef {MarkdownRuleDefinition<{ RuleOptions: NoInvalidLabelRefsOptions, MessageIds: NoInvalidLabelRefsMessageIds }>} NoInvalidLabelRefsRuleDefinition
|
||||
*/
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
/** matches i.e., `[foo][bar]` */
|
||||
const labelPattern = /\]\[([^\]]+)\]/u;
|
||||
/**
|
||||
* Finds missing references in a node.
|
||||
* @param {Text} node The node to check.
|
||||
* @param {MarkdownSourceCode} sourceCode The Markdown source code object.
|
||||
* @returns {Array<{label:string,position:Position}>} The missing references.
|
||||
*/
|
||||
function findInvalidLabelReferences(node, sourceCode) {
|
||||
const nodeText = sourceCode.getText(node);
|
||||
const docText = sourceCode.text;
|
||||
const invalid = [];
|
||||
let startIndex = 0;
|
||||
/*
|
||||
* This loop works by searching the string inside the node for the next
|
||||
* label reference. If it finds one, it checks to see if there is any
|
||||
* white space between the [ and ]. If there is, it reports an error.
|
||||
* It then moves the start index to the end of the label reference and
|
||||
* continues searching the text until the end of the text is found.
|
||||
*/
|
||||
while (startIndex < nodeText.length) {
|
||||
const value = nodeText.slice(startIndex);
|
||||
const match = value.match(labelPattern);
|
||||
if (!match) {
|
||||
break;
|
||||
}
|
||||
if (!illegalShorthandTailPattern.test(match[0])) {
|
||||
startIndex += match.index + match[0].length;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Adjust `labelPattern` match index to the full source code.
|
||||
*/
|
||||
const startOffset = startIndex + match.index + node.position.start.offset;
|
||||
const endOffset = startOffset + match[0].length;
|
||||
/*
|
||||
* Search the entire document text to find the preceding open bracket.
|
||||
*/
|
||||
const lastOpenBracketIndex = docText.lastIndexOf("[", startOffset);
|
||||
if (lastOpenBracketIndex === -1) {
|
||||
startIndex += match.index + match[0].length;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Note: `label` can contain leading and trailing newlines, so we need to
|
||||
* take that into account when calculating the line and column offsets.
|
||||
*/
|
||||
const label = docText
|
||||
.slice(lastOpenBracketIndex, endOffset)
|
||||
.match(/!?\[([^\]]+)\]/u)[1];
|
||||
invalid.push({
|
||||
label: label.trim(),
|
||||
position: {
|
||||
start: sourceCode.getLocFromIndex(startOffset + 1),
|
||||
end: sourceCode.getLocFromIndex(endOffset),
|
||||
},
|
||||
});
|
||||
startIndex += match.index + match[0].length;
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//-----------------------------------------------------------------------------
|
||||
export default /** @satisfies {NoInvalidLabelRefsRuleDefinition} */ ({
|
||||
meta: {
|
||||
type: "problem",
|
||||
docs: {
|
||||
recommended: true,
|
||||
description: "Disallow invalid label references",
|
||||
url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-invalid-label-refs.md",
|
||||
},
|
||||
messages: {
|
||||
invalidLabelRef: "Label reference '{{label}}' is invalid due to white space between [ and ].",
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
const { sourceCode } = context;
|
||||
return {
|
||||
text(node) {
|
||||
const invalidReferences = findInvalidLabelReferences(node, sourceCode);
|
||||
for (const invalidReference of invalidReferences) {
|
||||
context.report({
|
||||
loc: invalidReference.position,
|
||||
messageId: "invalidLabelRef",
|
||||
data: {
|
||||
label: invalidReference.label,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user