Skip to content

Commit f6d6b76

Browse files
authored
feat: add allowLabels option to no-missing-label-refs (#513)
* feat: add ignoreLabels option to no-missing-label-refs * rename ignoreLabels -> allowLabels * add GFM task list test case
1 parent 3067607 commit f6d6b76

File tree

3 files changed

+176
-3
lines changed

3 files changed

+176
-3
lines changed

docs/rules/no-missing-label-refs.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ Examples of **incorrect** code for this rule:
3030
[eslint]
3131
```
3232

33+
## Options
34+
35+
The following options are available on this rule:
36+
37+
* `allowLabels: Array<string>` - labels to allow when checking for missing label references. (default: `[]`)
38+
39+
Examples of **correct** code when configured as `"no-missing-label-refs": ["error", { allowLabels: ["eslint"] }]`:
40+
41+
```markdown
42+
<!-- eslint markdown/no-missing-label-refs: ["error", { allowLabels: ["eslint"] }] -->
43+
44+
[ESLint][eslint]
45+
46+
[eslint][]
47+
48+
[eslint]
49+
```
50+
3351
## When Not to Use It
3452

3553
If you aren't concerned with missing label references, you can safely disable this rule.

src/rules/no-missing-label-refs.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { findOffsets, illegalShorthandTailPattern } from "../util.js";
1818
* @import { Text } from "mdast";
1919
* @import { MarkdownRuleDefinition } from "../types.js";
2020
* @typedef {"notFound"} NoMissingLabelRefsMessageIds
21-
* @typedef {[]} NoMissingLabelRefsOptions
21+
* @typedef {[{ allowLabels?: string[] }]} NoMissingLabelRefsOptions
2222
* @typedef {MarkdownRuleDefinition<{ RuleOptions: NoMissingLabelRefsOptions, MessageIds: NoMissingLabelRefsMessageIds }>} NoMissingLabelRefsRuleDefinition
2323
*/
2424

@@ -119,13 +119,36 @@ export default {
119119
url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-missing-label-refs.md",
120120
},
121121

122+
schema: [
123+
{
124+
type: "object",
125+
properties: {
126+
allowLabels: {
127+
type: "array",
128+
items: {
129+
type: "string",
130+
},
131+
uniqueItems: true,
132+
},
133+
},
134+
additionalProperties: false,
135+
},
136+
],
137+
138+
defaultOptions: [
139+
{
140+
allowLabels: [],
141+
},
142+
],
143+
122144
messages: {
123145
notFound: "Label reference '{{label}}' not found.",
124146
},
125147
},
126148

127149
create(context) {
128150
const { sourceCode } = context;
151+
const allowLabels = new Set(context.options[0].allowLabels);
129152

130153
/** @type {Array<{label:string,position:Position}>} */
131154
let allMissingReferences = [];
@@ -144,9 +167,16 @@ export default {
144167
},
145168

146169
text(node) {
147-
allMissingReferences.push(
148-
...findMissingReferences(node, sourceCode.getText(node)),
170+
const missingReferences = findMissingReferences(
171+
node,
172+
sourceCode.getText(node),
149173
);
174+
175+
for (const missingReference of missingReferences) {
176+
if (!allowLabels.has(missingReference.label)) {
177+
allMissingReferences.push(missingReference);
178+
}
179+
}
150180
},
151181

152182
definition(node) {

tests/rules/no-missing-label-refs.test.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import rule from "../../src/rules/no-missing-label-refs.js";
1111
import markdown from "../../src/index.js";
1212
import { Linter, RuleTester } from "eslint";
13+
import dedent from "dedent";
1314

1415
//------------------------------------------------------------------------------
1516
// Tests
@@ -89,6 +90,46 @@ ruleTester.run("no-missing-label-refs", rule, {
8990
`[escaped${"\\".repeat(3)}][escaped${"\\".repeat(3)}]`,
9091
`[escaped${"\\".repeat(5)}][escaped${"\\".repeat(5)}]`,
9192
`[escaped${"\\".repeat(7)}][escaped${"\\".repeat(7)}]`,
93+
{
94+
code: "[foo][bar]",
95+
options: [{ allowLabels: ["bar"] }],
96+
},
97+
{
98+
code: "![foo][bar]",
99+
options: [{ allowLabels: ["bar"] }],
100+
},
101+
{
102+
code: "[foo][]",
103+
options: [{ allowLabels: ["foo"] }],
104+
},
105+
{
106+
code: "![foo][]",
107+
options: [{ allowLabels: ["foo"] }],
108+
},
109+
{
110+
code: "[foo]",
111+
options: [{ allowLabels: ["foo"] }],
112+
},
113+
{
114+
code: "![foo]",
115+
options: [{ allowLabels: ["foo"] }],
116+
},
117+
{
118+
code: "[foo]\n[bar]",
119+
options: [{ allowLabels: ["foo", "bar"] }],
120+
},
121+
{
122+
code: "[Foo][]\n[Bar][]",
123+
options: [{ allowLabels: ["Foo", "Bar"] }],
124+
},
125+
{
126+
code: dedent`
127+
- [x] Checked
128+
- [-] In progress
129+
`,
130+
language: "markdown/gfm",
131+
options: [{ allowLabels: ["-"] }],
132+
},
92133
],
93134
invalid: [
94135
{
@@ -546,6 +587,90 @@ ruleTester.run("no-missing-label-refs", rule, {
546587
},
547588
],
548589
},
590+
{
591+
code: "[foo][bar]",
592+
options: [{ allowLabels: ["baz"] }],
593+
errors: [
594+
{
595+
messageId: "notFound",
596+
data: { label: "bar" },
597+
line: 1,
598+
column: 7,
599+
endLine: 1,
600+
endColumn: 10,
601+
},
602+
],
603+
},
604+
{
605+
code: "![foo][bar]",
606+
options: [{ allowLabels: ["foo"] }],
607+
errors: [
608+
{
609+
messageId: "notFound",
610+
data: { label: "bar" },
611+
line: 1,
612+
column: 8,
613+
endLine: 1,
614+
endColumn: 11,
615+
},
616+
],
617+
},
618+
{
619+
code: "[foo][]",
620+
options: [{ allowLabels: ["bar"] }],
621+
errors: [
622+
{
623+
messageId: "notFound",
624+
data: { label: "foo" },
625+
line: 1,
626+
column: 2,
627+
endLine: 1,
628+
endColumn: 5,
629+
},
630+
],
631+
},
632+
{
633+
code: "[foo]\n[bar]",
634+
options: [{ allowLabels: ["foo"] }],
635+
errors: [
636+
{
637+
messageId: "notFound",
638+
data: { label: "bar" },
639+
line: 2,
640+
column: 2,
641+
endLine: 2,
642+
endColumn: 5,
643+
},
644+
],
645+
},
646+
{
647+
code: "[Foo][]",
648+
options: [{ allowLabels: ["foo"] }],
649+
errors: [
650+
{
651+
messageId: "notFound",
652+
data: { label: "Foo" },
653+
line: 1,
654+
column: 2,
655+
endLine: 1,
656+
endColumn: 5,
657+
},
658+
],
659+
},
660+
{
661+
code: "[Foo][foo]\n[Bar][]",
662+
options: [{ allowLabels: ["Bar"] }],
663+
errors: [
664+
{
665+
messageId: "notFound",
666+
data: { label: "foo" },
667+
line: 1,
668+
column: 7,
669+
endLine: 1,
670+
endColumn: 10,
671+
},
672+
],
673+
},
549674
],
550675
});
551676

0 commit comments

Comments
 (0)