Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 12 additions & 35 deletions src/rules/no-invalid-label-refs.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// Imports
//-----------------------------------------------------------------------------

import { findOffsets, illegalShorthandTailPattern } from "../util.js";
import { illegalShorthandTailPattern } from "../util.js";

//-----------------------------------------------------------------------------
// Type Definitions
Expand All @@ -17,33 +17,30 @@ import { findOffsets, illegalShorthandTailPattern } from "../util.js";
* @import { Position } from "unist";
* @import { Text } from "mdast";
* @import { MarkdownRuleDefinition } from "../types.js";
* @import { MarkdownSourceCode } from "../index.js";
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was motivated by #366 (comment).

The Parameters<MarkdownRuleDefinition['create']>[0]['sourceCode'] expression was needed due to a collision between rollup and tools/dedupe-types at the time, but since @eslint/markdown no longer uses rollup, it's now safe to use ../index.js.

* @typedef {"invalidLabelRef"} NoInvalidLabelRefsMessageIds
* @typedef {[]} NoInvalidLabelRefsOptions
* @typedef {MarkdownRuleDefinition<{ RuleOptions: NoInvalidLabelRefsOptions, MessageIds: NoInvalidLabelRefsMessageIds }>} NoInvalidLabelRefsRuleDefinition
* @typedef {Parameters<MarkdownRuleDefinition['create']>[0]['sourceCode']} SourceCode
*/

//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------

// matches i.e., [foo][bar]
/** matches i.e., `[foo][bar]` */
const labelPattern = /\]\[([^\]]+)\]/u;

/**
* Finds missing references in a node.
* @param {Text} node The node to check.
* @param {SourceCode} sourceCode The Markdown source code object.
* @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;
const offset = node.position.start.offset;
const nodeStartLine = node.position.start.line;
const nodeStartColumn = node.position.start.column;

/*
* This loop works by searching the string inside the node for the next
Expand All @@ -66,16 +63,16 @@ function findInvalidLabelReferences(node, sourceCode) {
}

/*
* Calculate the match index relative to just the node and
* to the entire document text.
* Adjust `labelPattern` match index to the full source code.
*/
const nodeMatchIndex = startIndex + match.index;
const docMatchIndex = offset + nodeMatchIndex;
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("[", docMatchIndex);
const lastOpenBracketIndex = docText.lastIndexOf("[", startOffset);

if (lastOpenBracketIndex === -1) {
startIndex += match.index + match[0].length;
Expand All @@ -87,34 +84,14 @@ function findInvalidLabelReferences(node, sourceCode) {
* take that into account when calculating the line and column offsets.
*/
const label = docText
.slice(lastOpenBracketIndex, docMatchIndex + match[0].length)
.slice(lastOpenBracketIndex, endOffset)
.match(/!?\[([^\]]+)\]/u)[1];

// find location of [ in the document text
const { lineOffset: startLineOffset, columnOffset: startColumnOffset } =
findOffsets(nodeText, nodeMatchIndex + 1);

// find location of [ in the document text
const { lineOffset: endLineOffset, columnOffset: endColumnOffset } =
findOffsets(nodeText, nodeMatchIndex + match[0].length);

const startLine = nodeStartLine + startLineOffset;
const startColumn = nodeStartColumn + startColumnOffset;
const endLine = nodeStartLine + endLineOffset;
const endColumn =
(endLine === startLine ? nodeStartColumn : 1) + endColumnOffset;

invalid.push({
label: label.trim(),
position: {
start: {
line: startLine,
column: startColumn,
},
end: {
line: endLine,
column: endColumn,
},
start: sourceCode.getLocFromIndex(startOffset + 1),
end: sourceCode.getLocFromIndex(endOffset),
},
});

Expand Down
13 changes: 13 additions & 0 deletions tests/rules/no-invalid-label-refs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,5 +206,18 @@ ruleTester.run("no-invalid-label-refs", rule, {
},
],
},
{
code: "[*eslint*][ ]",
errors: [
{
messageId: "invalidLabelRef",
data: { label: "*eslint*" },
line: 1,
column: 11,
endLine: 1,
endColumn: 14,
},
],
},
],
});
Loading