Skip to content

Commit 4ae07d9

Browse files
authored
fix: false positive for containing element in svelte/no-unused-svelte-ignore (#420)
1 parent 6d3e449 commit 4ae07d9

30 files changed

+487
-17
lines changed

.changeset/heavy-cycles-explain.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-svelte": patch
3+
---
4+
5+
fix: false positive for containing element in `svelte/no-unused-svelte-ignore`

src/shared/svelte-compile-warns/extract-leading-comments.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function extractLeadingComments(
1717
}
1818
const astToken = token as AST.Token
1919
if (astToken.type === "HTMLText") {
20-
return Boolean(astToken.value.trim())
20+
return false
2121
}
2222
return astToken.type !== "HTMLComment"
2323
},

src/shared/svelte-compile-warns/index.ts

+65-16
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { AST } from "svelte-eslint-parser"
22
import * as compiler from "svelte/compiler"
33
import type { SourceMapMappings } from "@jridgewell/sourcemap-codec"
44
import { decode } from "@jridgewell/sourcemap-codec"
5-
import type { RuleContext } from "../../types"
5+
import type { ASTNodeWithParent, RuleContext } from "../../types"
66
import { LinesAndColumns } from "../../utils/lines-and-columns"
77
import type { TransformResult } from "./transform/types"
88
import {
@@ -21,6 +21,18 @@ import { getLangValue } from "../../utils/ast-utils"
2121
import path from "path"
2222
import fs from "fs"
2323

24+
type WarningTargetNode =
25+
| (AST.SvelteProgram & ASTNodeWithParent)
26+
| (AST.SvelteElement & ASTNodeWithParent)
27+
| (AST.SvelteStyleElement & ASTNodeWithParent)
28+
| (AST.SvelteScriptElement["body"][number] & ASTNodeWithParent)
29+
type IgnoreTargetNode =
30+
| WarningTargetNode
31+
| (AST.SvelteIfBlock & ASTNodeWithParent)
32+
| (AST.SvelteKeyBlock & ASTNodeWithParent)
33+
| (AST.SvelteEachBlock & ASTNodeWithParent)
34+
| (AST.SvelteAwaitBlock & ASTNodeWithParent)
35+
2436
const STYLE_TRANSFORMS: Record<
2537
string,
2638
typeof transformWithPostCSS | undefined
@@ -477,21 +489,22 @@ function processIgnore(
477489
if (!warning.code) {
478490
continue
479491
}
480-
const node = getWarningNode(warning)
481-
if (!node) {
482-
continue
483-
}
484-
for (const comment of extractLeadingComments(context, node).reverse()) {
485-
const ignoreItem = ignoreComments.find(
486-
(item) => item.token === comment && item.code === warning.code,
487-
)
488-
if (ignoreItem) {
489-
unusedIgnores.delete(ignoreItem)
490-
remainingWarning.delete(warning)
491-
break
492+
let node: IgnoreTargetNode | null = getWarningNode(warning)
493+
while (node) {
494+
for (const comment of extractLeadingComments(context, node).reverse()) {
495+
const ignoreItem = ignoreComments.find(
496+
(item) => item.token === comment && item.code === warning.code,
497+
)
498+
if (ignoreItem) {
499+
unusedIgnores.delete(ignoreItem)
500+
remainingWarning.delete(warning)
501+
break
502+
}
492503
}
504+
node = getIgnoreParent(node)
493505
}
494506
}
507+
495508
// Stripped styles are ignored from compilation and cannot determine css errors.
496509
for (const node of stripStyleElements) {
497510
for (const comment of extractLeadingComments(context, node).reverse()) {
@@ -509,8 +522,42 @@ function processIgnore(
509522
unusedIgnores: [...unusedIgnores],
510523
}
511524

525+
/** Get ignore target parent node */
526+
function getIgnoreParent(node: IgnoreTargetNode): IgnoreTargetNode | null {
527+
if (
528+
node.type !== "SvelteElement" &&
529+
node.type !== "SvelteIfBlock" &&
530+
node.type !== "SvelteKeyBlock" &&
531+
node.type !== "SvelteEachBlock" &&
532+
node.type !== "SvelteAwaitBlock"
533+
) {
534+
return null
535+
}
536+
const parent = node.parent
537+
if (parent.type === "SvelteElseBlock") {
538+
return parent.parent // SvelteIfBlock or SvelteEachBlock
539+
}
540+
if (
541+
parent.type === "SvelteAwaitPendingBlock" ||
542+
parent.type === "SvelteAwaitThenBlock" ||
543+
parent.type === "SvelteAwaitCatchBlock"
544+
) {
545+
return parent.parent // SvelteAwaitBlock
546+
}
547+
if (
548+
parent.type !== "SvelteElement" &&
549+
parent.type !== "SvelteIfBlock" &&
550+
parent.type !== "SvelteKeyBlock" &&
551+
parent.type !== "SvelteEachBlock"
552+
// && parent.type !== "SvelteAwaitBlock"
553+
) {
554+
return null
555+
}
556+
return parent
557+
}
558+
512559
/** Get warning node */
513-
function getWarningNode(warning: Warning) {
560+
function getWarningNode(warning: Warning): WarningTargetNode | null {
514561
const indexes = getWarningIndexes(warning)
515562
if (indexes.start != null) {
516563
const node = getWarningTargetNodeFromIndex(indexes.start)
@@ -534,7 +581,9 @@ function processIgnore(
534581
/**
535582
* Get warning target node from the given index
536583
*/
537-
function getWarningTargetNodeFromIndex(index: number) {
584+
function getWarningTargetNodeFromIndex(
585+
index: number,
586+
): WarningTargetNode | null {
538587
let targetNode = sourceCode.getNodeByRangeIndex(index)
539588
while (targetNode) {
540589
if (
@@ -548,7 +597,7 @@ function processIgnore(
548597
targetNode.parent.type === "Program" ||
549598
targetNode.parent.type === "SvelteScriptElement"
550599
) {
551-
return targetNode
600+
return targetNode as WarningTargetNode
552601
}
553602
} else {
554603
return null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
- message: svelte-ignore comment is used, but not warned
2+
line: 4
3+
column: 24
4+
suggestions: null
5+
- message: svelte-ignore comment is used, but not warned
6+
line: 4
7+
column: 58
8+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div>
2+
{#if true}
3+
A
4+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
5+
{:else}
6+
<label tabindex="0">Click</label>
7+
<ul tabindex="0" />
8+
{/if}
9+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
- message: svelte-ignore comment is used, but not warned
2+
line: 4
3+
column: 24
4+
suggestions: null
5+
- message: svelte-ignore comment is used, but not warned
6+
line: 4
7+
column: 58
8+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div>
2+
{#each [] as e}
3+
A
4+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
5+
{:else}
6+
<label tabindex="0">Click</label>
7+
<ul tabindex="0" />
8+
{/each}
9+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
- message: svelte-ignore comment is used, but not warned
2+
line: 3
3+
column: 24
4+
suggestions: null
5+
- message: svelte-ignore comment is used, but not warned
6+
line: 3
7+
column: 58
8+
suggestions: null
9+
- message: svelte-ignore comment is used, but not warned
10+
line: 7
11+
column: 24
12+
suggestions: null
13+
- message: svelte-ignore comment is used, but not warned
14+
line: 7
15+
column: 58
16+
suggestions: null
17+
- message: svelte-ignore comment is used, but not warned
18+
line: 15
19+
column: 24
20+
suggestions: null
21+
- message: svelte-ignore comment is used, but not warned
22+
line: 15
23+
column: 58
24+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<div>
2+
{#await Promise.resolve(42)}
3+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
4+
{:then name}
5+
<label tabindex="0">Click</label>
6+
<ul tabindex="0" />
7+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
8+
{:catch name}
9+
<label tabindex="0">Click</label>
10+
<ul tabindex="0" />
11+
{/await}
12+
</div>
13+
<div>
14+
{#await Promise.resolve(42)}
15+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
16+
{:then name}
17+
<label tabindex="0">Click</label>
18+
<ul tabindex="0" />
19+
{/await}
20+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
2+
<div class="dropdown">
3+
<label tabindex="0">Click</label>
4+
<ul tabindex="0" />
5+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
2+
TEXT
3+
<div class="dropdown">
4+
<label tabindex="0">Click</label>
5+
<ul tabindex="0" />
6+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
2+
<!-- comment -->
3+
<div class="dropdown">
4+
<label tabindex="0">Click</label>
5+
<ul tabindex="0" />
6+
</div>
7+
<!-- svelte-ignore a11y-label-has-associated-control -->
8+
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
9+
<div class="dropdown">
10+
<label tabindex="0">Click</label>
11+
<ul tabindex="0" />
12+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<div>
2+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
3+
{#if true}
4+
<label tabindex="0">Click</label>
5+
<ul tabindex="0" />
6+
{/if}
7+
</div>
8+
<div>
9+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
10+
{#if true}
11+
A
12+
{:else}
13+
<div />
14+
<label tabindex="0">Click</label>
15+
<ul tabindex="0" />
16+
{/if}
17+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div>
2+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
3+
{#key 42}
4+
<label tabindex="0">Click</label>
5+
<ul tabindex="0" />
6+
{/key}
7+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<div>
2+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
3+
{#each [] as e}
4+
<label tabindex="0">Click</label>
5+
<ul tabindex="0" />
6+
{/each}
7+
</div>
8+
<div>
9+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
10+
{#each [] as e}
11+
A
12+
{:else}
13+
<div />
14+
<label tabindex="0">Click</label>
15+
<ul tabindex="0" />
16+
{/each}
17+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<div>
2+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
3+
{#await Promise.resolve(42)}
4+
<label tabindex="0">Click</label>
5+
<ul tabindex="0" />
6+
{/await}
7+
</div>
8+
<div>
9+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
10+
{#await Promise.resolve(42)}
11+
<label tabindex="0">Click</label>
12+
<ul tabindex="0" />
13+
{:then name}
14+
<label tabindex="0">Click</label>
15+
<ul tabindex="0" />
16+
{:catch name}
17+
<label tabindex="0">Click</label>
18+
<ul tabindex="0" />
19+
{/await}
20+
</div>
21+
<div>
22+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
23+
{#await Promise.resolve(42)}
24+
<label tabindex="0">Click</label>
25+
<ul tabindex="0" />
26+
{:then name}
27+
<label tabindex="0">Click</label>
28+
<ul tabindex="0" />
29+
{/await}
30+
</div>
31+
<div>
32+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
33+
{#await Promise.resolve(42) then n}
34+
<label tabindex="0">Click</label>
35+
<ul tabindex="0" />
36+
{/await}
37+
</div>
38+
<div>
39+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
40+
{#await Promise.resolve(42) catch n}
41+
<label tabindex="0">Click</label>
42+
<ul tabindex="0" />
43+
{/await}
44+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
- message: "A11y: noninteractive element cannot have nonnegative tabIndex
2+
value(a11y-no-noninteractive-tabindex)"
3+
line: 6
4+
column: 5
5+
suggestions: null
6+
- message: "A11y: A form label must be associated with a
7+
control.(a11y-label-has-associated-control)"
8+
line: 6
9+
column: 5
10+
suggestions: null
11+
- message: "A11y: noninteractive element cannot have nonnegative tabIndex
12+
value(a11y-no-noninteractive-tabindex)"
13+
line: 7
14+
column: 5
15+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div>
2+
{#if true}
3+
A
4+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
5+
{:else}
6+
<label tabindex="0">Click</label>
7+
<ul tabindex="0" />
8+
{/if}
9+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
- message: "A11y: noninteractive element cannot have nonnegative tabIndex
2+
value(a11y-no-noninteractive-tabindex)"
3+
line: 6
4+
column: 5
5+
suggestions: null
6+
- message: "A11y: A form label must be associated with a
7+
control.(a11y-label-has-associated-control)"
8+
line: 6
9+
column: 5
10+
suggestions: null
11+
- message: "A11y: noninteractive element cannot have nonnegative tabIndex
12+
value(a11y-no-noninteractive-tabindex)"
13+
line: 7
14+
column: 5
15+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div>
2+
{#each [] as e}
3+
A
4+
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
5+
{:else}
6+
<label tabindex="0">Click</label>
7+
<ul tabindex="0" />
8+
{/each}
9+
</div>

0 commit comments

Comments
 (0)