Skip to content

Commit b6f8298

Browse files
committed
correctly detect empty footnote definitions with only HTML comments
1 parent 34a962b commit b6f8298

File tree

3 files changed

+84
-5
lines changed

3 files changed

+84
-5
lines changed

docs/rules/no-empty-definitions.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ Disallow empty definitions.
44

55
## Background
66

7-
Markdown allows you to specify a label as a placeholder for a URL in both links and images using square brackets, such as:
7+
Markdown allows you to specify a label as a placeholder for a URL in both links and images, or as a footnote reference, using square brackets. For example:
88

99
```markdown
1010
[ESLint][eslint]
1111

1212
[eslint]: https://eslint.org
13+
14+
[ESLint][^eslint]
15+
16+
[^eslint]: Find and fix problmes in your JavaScript code
1317
```
1418

1519
Definitions with an empty URL or only an empty fragment (`#`), as well as footnote definitions with no content, are usually mistakes and do not provide useful information.
@@ -58,7 +62,7 @@ Examples of **correct** code for this rule with `checkFootnoteDefinitions: false
5862

5963
## When Not to Use It
6064

61-
If you aren't concerned with empty definitions or empty footnote definitions, you can safely disable this rule.
65+
If you aren't concerned with empty definitions, you can safely disable this rule.
6266

6367
## Prior Art
6468

src/rules/no-empty-definitions.js

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,29 @@
88
//-----------------------------------------------------------------------------
99

1010
/**
11-
* @typedef {import("../types.ts").MarkdownRuleDefinition<{ RuleOptions: [{ checkFootnoteDefinitions?: boolean; }] }>}
12-
* NoEmptyDefinitionsRuleDefinition
11+
* @import { MarkdownRuleDefinition } from "../types.js";
12+
* @typedef {"emptyDefinition" | "emptyFootnoteDefinition"} NoEmptyDefinitionsMessageIds
13+
* @typedef {[{ checkFootnoteDefinitions?: boolean }]} NoEmptyDefinitionsOptions
14+
* @typedef {MarkdownRuleDefinition<{ RuleOptions: NoEmptyDefinitionsOptions, MessageIds: NoEmptyDefinitionsMessageIds }>} NoEmptyDefinitionsRuleDefinition
1315
*/
1416

17+
//-----------------------------------------------------------------------------
18+
// Helpers
19+
//-----------------------------------------------------------------------------
20+
21+
const htmlCommentPattern = /<!--[\s\S]*?-->/gu;
22+
23+
/**
24+
* Checks if a string contains only HTML comments.
25+
* @param {string} value The input string to check.
26+
* @returns {boolean} True if the string contains only HTML comments, false otherwise.
27+
*/
28+
function isOnlyComments(value) {
29+
const withoutComments = value.replace(htmlCommentPattern, "");
30+
31+
return withoutComments.trim().length === 0;
32+
}
33+
1534
//-----------------------------------------------------------------------------
1635
// Rule Definition
1736
//-----------------------------------------------------------------------------
@@ -62,7 +81,15 @@ export default {
6281
},
6382

6483
footnoteDefinition(node) {
65-
if (checkFootnoteDefinitions && node.children.length === 0) {
84+
if (
85+
checkFootnoteDefinitions &&
86+
(node.children.length === 0 ||
87+
node.children.every(
88+
child =>
89+
child.type === "html" &&
90+
isOnlyComments(child.value),
91+
))
92+
) {
6693
context.report({
6794
loc: node.position,
6895
messageId: "emptyFootnoteDefinition",

tests/rules/no-empty-definitions.test.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ ruleTester.run("no-empty-definitions", rule, {
3939
"[\\^note]:",
4040
"[^note\\]:",
4141
"[^note]\\:",
42+
"[^foo]: <span></span> <!-- comment -->",
43+
"[^foo]: content <!-- comment -->",
44+
"[^foo]: <!-- comment --> content",
45+
"[^foo]: <!-- comment --> content <!-- comment -->",
46+
dedent`
47+
[^foo]: <!-- comm
48+
ent --> content <!-- comment -->
49+
`,
4250
{
4351
code: "[^note]:",
4452
options: [{ checkFootnoteDefinitions: false }],
@@ -178,5 +186,45 @@ ruleTester.run("no-empty-definitions", rule, {
178186
},
179187
],
180188
},
189+
{
190+
code: "[^foo]: <!-- comment -->",
191+
errors: [
192+
{
193+
messageId: "emptyFootnoteDefinition",
194+
line: 1,
195+
column: 1,
196+
endLine: 1,
197+
endColumn: 25,
198+
},
199+
],
200+
},
201+
{
202+
code: dedent`
203+
[^foo]: <!-- comment
204+
-->`,
205+
errors: [
206+
{
207+
messageId: "emptyFootnoteDefinition",
208+
line: 1,
209+
column: 1,
210+
endLine: 2,
211+
endColumn: 8,
212+
},
213+
],
214+
},
215+
{
216+
code: dedent`
217+
[^foo]: <!-- comment -->
218+
<!-- another comment -->`,
219+
errors: [
220+
{
221+
messageId: "emptyFootnoteDefinition",
222+
line: 1,
223+
column: 1,
224+
endLine: 2,
225+
endColumn: 29,
226+
},
227+
],
228+
},
181229
],
182230
});

0 commit comments

Comments
 (0)