Skip to content

Commit 5aef967

Browse files
committed
simplify with ESQuery selector
1 parent 22ff7fc commit 5aef967

File tree

2 files changed

+232
-38
lines changed

2 files changed

+232
-38
lines changed

src/rules/no-reference-like-urls.js

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { findOffsets } from "../util.js";
1515
//-----------------------------------------------------------------------------
1616

1717
/**
18+
* @import { SourceRange } from "@eslint/core"
1819
* @import { Heading, Node, Paragraph, TableCell } from "mdast";
1920
* @import { MarkdownRuleDefinition } from "../types.js";
2021
* @typedef {"referenceLikeUrl"} NoReferenceLikeUrlMessageIds
@@ -28,50 +29,16 @@ import { findOffsets } from "../util.js";
2829

2930
/** Pattern to match both inline links: `[text](url)` and images: `![alt](url)`, with optional title */
3031
const linkOrImagePattern =
31-
/(?<!(?<!\\)\\)(?<imageBang>!)?\[(?<label>(?:\\.|[^()\\]|\([\s\S]*\))*?)\]\((?<destination>(?:<[^>]*>)|(?:[^ \t)]+))(?:[ \t]+(?<title>"[^"]*"|'[^']*'|\([^)]*\)))?\)(?!\()/gu;
32+
/(?<!(?<!\\)\\)(?<imageBang>!)?\[(?<label>(?:\\.|[^()\\]|\([\s\S]*\))*?)\]\((?<destination>[ \t]*(?:\r\n|\n|\r)?[ \t]*(?:<[^>]*>|[^ \t)]+))(?:[ \t]*(?:\r\n|\n|\r)?[ \t]*(?<title>"[^"]*"|'[^']*'|\([^)]*\)))?[ \t]*(?:\r\n|\n|\r)?[ \t]*\)(?!\()/gu;
3233

3334
/**
3435
* Checks if a given index is within any skip range.
3536
* @param {number} index The index to check
36-
* @param {Array<{startOffset: number, endOffset: number}>} skipRanges The skip ranges
37+
* @param {Array<SourceRange>} skipRanges The skip ranges
3738
* @returns {boolean} True if index is in a skip range
3839
*/
3940
function isInSkipRange(index, skipRanges) {
40-
return skipRanges.some(
41-
range => range.startOffset <= index && index < range.endOffset,
42-
);
43-
}
44-
45-
/**
46-
* Finds ranges of inline code and HTML nodes within a given node
47-
* @param {Heading | Paragraph | TableCell} node The node to search
48-
* @returns {Array<{startOffset: number, endOffset: number}>} Array of skip ranges
49-
*/
50-
function findSkipRanges(node) {
51-
/** @type {Array<{startOffset: number, endOffset: number}>} */
52-
const skipRanges = [];
53-
54-
/**
55-
* Recursively traverses the AST to find inline code and HTML nodes.
56-
* @param {Node} currentNode The current node being traversed
57-
* @returns {void}
58-
*/
59-
function traverse(currentNode) {
60-
if (currentNode.type === "inlineCode" || currentNode.type === "html") {
61-
skipRanges.push({
62-
startOffset: currentNode.position.start.offset,
63-
endOffset: currentNode.position.end.offset,
64-
});
65-
return;
66-
}
67-
68-
if ("children" in currentNode && Array.isArray(currentNode.children)) {
69-
currentNode.children.forEach(traverse);
70-
}
71-
}
72-
73-
traverse(node);
74-
return skipRanges;
41+
return skipRanges.some(range => range[0] <= index && index < range[1]);
7542
}
7643

7744
//-----------------------------------------------------------------------------
@@ -100,6 +67,8 @@ export default {
10067

10168
create(context) {
10269
const { sourceCode } = context;
70+
/** @type {Array<SourceRange>} */
71+
const skipRanges = [];
10372
/** @type {Set<string>} */
10473
const definitionIdentifiers = new Set();
10574
/** @type {Array<Heading | Paragraph | TableCell>} */
@@ -122,10 +91,15 @@ export default {
12291
relevantNodes.push(node);
12392
},
12493

94+
":matches(heading, paragraph, tableCell) :matches(html, inlineCode)"(
95+
node,
96+
) {
97+
skipRanges.push(sourceCode.getRange(node));
98+
},
99+
125100
"root:exit"() {
126101
for (const node of relevantNodes) {
127102
const text = sourceCode.getText(node);
128-
const skipRanges = findSkipRanges(node);
129103

130104
let match;
131105
while ((match = linkOrImagePattern.exec(text)) !== null) {

tests/rules/no-reference-like-urls.test.js

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,226 @@ ruleTester.run("no-reference-like-urls", rule, {
157157
},
158158
],
159159
invalid: [
160+
{
161+
code: dedent`
162+
[Mercury]( mercury)
163+
164+
[mercury]: https://example.com/mercury
165+
`,
166+
output: dedent`
167+
[Mercury][ mercury]
168+
169+
[mercury]: https://example.com/mercury
170+
`,
171+
errors: [
172+
{
173+
messageId: "referenceLikeUrl",
174+
data: { type: "link", prefix: "" },
175+
line: 1,
176+
column: 1,
177+
endLine: 1,
178+
endColumn: 21,
179+
},
180+
],
181+
},
182+
{
183+
code: dedent`
184+
[Mercury](\tmercury)
185+
186+
[mercury]: https://example.com/mercury
187+
`.replace(/\\t/gu, "\t"),
188+
output: dedent`
189+
[Mercury][\tmercury]
190+
191+
[mercury]: https://example.com/mercury
192+
`.replace(/\\t/gu, "\t"),
193+
errors: [
194+
{
195+
messageId: "referenceLikeUrl",
196+
data: { type: "link", prefix: "" },
197+
line: 1,
198+
column: 1,
199+
endLine: 1,
200+
endColumn: 20,
201+
},
202+
],
203+
},
204+
{
205+
code: dedent`
206+
[Mercury](\nmercury)
207+
208+
[mercury]: https://example.com/mercury
209+
`,
210+
output: dedent`
211+
[Mercury][\nmercury]
212+
213+
[mercury]: https://example.com/mercury
214+
`,
215+
errors: [
216+
{
217+
messageId: "referenceLikeUrl",
218+
data: { type: "link", prefix: "" },
219+
line: 1,
220+
column: 1,
221+
endLine: 2,
222+
endColumn: 9,
223+
},
224+
],
225+
},
226+
{
227+
code: dedent`
228+
[Mercury](\r\nmercury)
229+
230+
[mercury]: https://example.com/mercury
231+
`.replace(/\\r/gu, "\r"),
232+
output: dedent`
233+
[Mercury][\r\nmercury]
234+
235+
[mercury]: https://example.com/mercury
236+
`.replace(/\\r/gu, "\r"),
237+
errors: [
238+
{
239+
messageId: "referenceLikeUrl",
240+
data: { type: "link", prefix: "" },
241+
line: 1,
242+
column: 1,
243+
endLine: 2,
244+
endColumn: 9,
245+
},
246+
],
247+
},
248+
{
249+
code: dedent`
250+
[Mercury](\rmercury)
251+
252+
[mercury]: https://example.com/mercury
253+
`.replace(/\\r/gu, "\r"),
254+
output: dedent`
255+
[Mercury][\rmercury]
256+
257+
[mercury]: https://example.com/mercury
258+
`.replace(/\\r/gu, "\r"),
259+
errors: [
260+
{
261+
messageId: "referenceLikeUrl",
262+
data: { type: "link", prefix: "" },
263+
line: 1,
264+
column: 1,
265+
endLine: 1,
266+
endColumn: 20,
267+
},
268+
],
269+
},
270+
{
271+
code: dedent`
272+
![Mercury](\nmercury)
273+
274+
[mercury]: https://example.com/mercury.jpg
275+
`,
276+
output: dedent`
277+
![Mercury][\nmercury]
278+
279+
[mercury]: https://example.com/mercury.jpg
280+
`,
281+
errors: [
282+
{
283+
messageId: "referenceLikeUrl",
284+
data: { type: "image", prefix: "!" },
285+
line: 1,
286+
column: 1,
287+
endLine: 2,
288+
endColumn: 9,
289+
},
290+
],
291+
},
292+
{
293+
code: dedent`
294+
![Mercury](\r\nmercury)
295+
296+
[mercury]: https://example.com/mercury.jpg
297+
`.replace(/\\r/gu, "\r"),
298+
output: dedent`
299+
![Mercury][\r\nmercury]
300+
301+
[mercury]: https://example.com/mercury.jpg
302+
`.replace(/\\r/gu, "\r"),
303+
errors: [
304+
{
305+
messageId: "referenceLikeUrl",
306+
data: { type: "image", prefix: "!" },
307+
line: 1,
308+
column: 1,
309+
endLine: 2,
310+
endColumn: 9,
311+
},
312+
],
313+
},
314+
{
315+
code: dedent`
316+
![Mercury](\rmercury)
317+
318+
[mercury]: https://example.com/mercury.jpg
319+
`.replace(/\\r/gu, "\r"),
320+
output: dedent`
321+
![Mercury][\rmercury]
322+
323+
[mercury]: https://example.com/mercury.jpg
324+
`.replace(/\\r/gu, "\r"),
325+
errors: [
326+
{
327+
messageId: "referenceLikeUrl",
328+
data: { type: "image", prefix: "!" },
329+
line: 1,
330+
column: 1,
331+
endLine: 1,
332+
endColumn: 21,
333+
},
334+
],
335+
},
336+
{
337+
code: dedent`
338+
![Mercury]( mercury)
339+
340+
[mercury]: https://example.com/mercury.jpg
341+
`,
342+
output: dedent`
343+
![Mercury][ mercury]
344+
345+
[mercury]: https://example.com/mercury.jpg
346+
`,
347+
errors: [
348+
{
349+
messageId: "referenceLikeUrl",
350+
data: { type: "image", prefix: "!" },
351+
line: 1,
352+
column: 1,
353+
endLine: 1,
354+
endColumn: 22,
355+
},
356+
],
357+
},
358+
{
359+
code: dedent`
360+
![Mercury](\tmercury)
361+
362+
[mercury]: https://example.com/mercury.jpg
363+
`.replace(/\\t/gu, "\t"),
364+
output: dedent`
365+
![Mercury][\tmercury]
366+
367+
[mercury]: https://example.com/mercury.jpg
368+
`.replace(/\\t/gu, "\t"),
369+
errors: [
370+
{
371+
messageId: "referenceLikeUrl",
372+
data: { type: "image", prefix: "!" },
373+
line: 1,
374+
column: 1,
375+
endLine: 1,
376+
endColumn: 21,
377+
},
378+
],
379+
},
160380
{
161381
code: dedent`
162382
[Mercury](mercury)

0 commit comments

Comments
 (0)