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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 65 additions & 16 deletions
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
Lines changed: 8 additions & 0 deletions
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
Lines changed: 9 additions & 0 deletions
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>
Lines changed: 8 additions & 0 deletions
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
Lines changed: 9 additions & 0 deletions
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>
Lines changed: 24 additions & 0 deletions
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
Lines changed: 20 additions & 0 deletions
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>
Lines changed: 5 additions & 0 deletions
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>

0 commit comments

Comments
 (0)