-
-
Notifications
You must be signed in to change notification settings - Fork 48
feat: Added the no-unused-class-name rule using parser services #489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ota-meshi
merged 37 commits into
sveltejs:main
from
marekdedic:no-unused-class-name-parser-services
Jun 19, 2023
Merged
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
660d537
test(no-unused-class-name): Added rule tests
marekdedic 7753853
Merge branch 'main' into no-unused-class-name
marekdedic 3647bc1
test(no-unused-class-name): added fixtures
marekdedic 214ee0d
feat(no-unused-class-name): added the implementation of the rule
marekdedic f12bc23
feat(no-unused-class-name): added support for nested rules
marekdedic 64e5a9a
feat(no-unused-class-name): added proper lint issue message
marekdedic c018c05
docs(no-unused-class-name): added docs
marekdedic d7053db
test(no-unused-class-name): renamed fixture
marekdedic ea8a7b5
feat(no-unused-class-name): added support for multiple classes on one…
marekdedic cde0a7f
test(no-unused-class-name): added test for SASS support
marekdedic 57ba04b
test(no-unused-class-name): removed test for deprecated SASS syntax
marekdedic 61e153e
feat(no-unused-class-name): handling unavailable style AST
marekdedic 09431e9
chore(no-unused-class-name): extracted findClassesInAttribute
marekdedic efb0115
test(no-unused-class-name): ignoring invalid style language test case…
marekdedic 281c287
test(no-unused-class-name): added tests for various selectors
marekdedic 8c63f78
chore(no-unused-class-name): switched from for cycle to flatMap
marekdedic 60e84ca
chore(no-unused-class-name): removed unneeded type assertions
marekdedic 612a2dc
feat(no-unused-class-name): added support for class directives
marekdedic d6e7c62
chore(no-unused-class-name): using renamed SvelteStyle- node types
marekdedic c1d0fa3
chore(no-unused-class-name): added a changeset
marekdedic e627577
chore(no-unused-class-name): update to reflect parser types
marekdedic e0334b5
chore(no-unused-class-name): using type narrowing on ESLintCompatible…
marekdedic d3a5fd0
Merge branch 'main' into no-unused-class-name
marekdedic 426ca67
chore(no-unused-class-name): removed yarn.lock
marekdedic 45a2835
feat(no-ununsed-class-name): Using style AST from parser services
marekdedic 0aeff3e
fix(no-ununsed-class-name): not reporting errors on invalid lang
marekdedic f594c9c
chore(no-unused-class-name): updated the rule to use the StyleContext…
marekdedic c31afeb
Merge branch 'main' into no-unused-class-name-parser-services
marekdedic 66e17c2
chore(no-unused-class-name): updated the rule to use the new StyleCon…
marekdedic a69787f
test(no-unused-class-name): added a test with an invalid style language
marekdedic d8ed1a6
test(no-unused-class-name): added a test with pseudo-classes
marekdedic 8476b47
fix(no-unused-class-name): added support for pseudo-classes
marekdedic 8aa5b9a
fix(no-unused-class-name): added support for class attributes with mu…
marekdedic 2614df0
chore(no-unused-class-name): removed premature 'since' comment
marekdedic 3541a67
fix(no-unused-class-name): fixed eslint error on fixture
marekdedic ee2739c
fix(no-unused-class-name): fixed eslint error on multiline class names
marekdedic b7fb1b0
fix(no-unused-class-name): fixed test fixture line offset
marekdedic File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"eslint-plugin-svelte": minor | ||
--- | ||
|
||
feat: added the no-unused-class-name rule |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
--- | ||
pageClass: "rule-details" | ||
sidebarDepth: 0 | ||
title: "svelte/no-unused-class-name" | ||
description: "disallow the use of a class in the template without a corresponding style" | ||
--- | ||
|
||
# svelte/no-unused-class-name | ||
|
||
> disallow the use of a class in the template without a corresponding style | ||
|
||
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge> | ||
|
||
## :book: Rule Details | ||
|
||
This rule is aimed at reducing unused classes in the HTML template. While `svelte-check` will produce the `css-unused-selector` if your `<style>` block includes any classes that aren't used in the template, this rule works the other way around - it reports cases wehre the template contains classes that aren't referred to in the `<style>` block. | ||
|
||
<ESLintCodeBlock> | ||
|
||
<!--eslint-skip--> | ||
|
||
```svelte | ||
<script lang="ts"> | ||
/* eslint svelte/no-unused-class-name: "error" */ | ||
</scrip> | ||
|
||
<!-- ✓ GOOD --> | ||
<div class="first-class">Hello</div> | ||
<div class="second-class">Hello</div> | ||
<div class="third-class fourth-class">Hello</div> | ||
|
||
<!-- ✗ BAD --> | ||
<div class="fifth-class">Hello</div> | ||
<div class="sixth-class first-class">Hello</div> | ||
|
||
<style> | ||
.first-class { | ||
color: red; | ||
} | ||
|
||
.second-class, | ||
.third-class { | ||
color: blue; | ||
} | ||
|
||
.fourth-class { | ||
color: green; | ||
} | ||
</style> | ||
``` | ||
|
||
</ESLintCodeBlock> | ||
|
||
## :wrench: Options | ||
|
||
Nothing. | ||
|
||
## :mag: Implementation | ||
|
||
- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/src/rules/no-unused-class-name.ts) | ||
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/tests/src/rules/no-unused-class-name.ts) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import { createRule } from "../utils" | ||
import type { | ||
SourceLocation, | ||
SvelteAttribute, | ||
SvelteDirective, | ||
SvelteShorthandAttribute, | ||
SvelteSpecialDirective, | ||
SvelteSpreadAttribute, | ||
SvelteStyleDirective, | ||
} from "svelte-eslint-parser/lib/ast" | ||
import type { AnyNode } from "postcss" | ||
import { | ||
default as selectorParser, | ||
type Node as SelectorNode, | ||
} from "postcss-selector-parser" | ||
|
||
export default createRule("no-unused-class-name", { | ||
meta: { | ||
docs: { | ||
description: | ||
"disallow the use of a class in the template without a corresponding style", | ||
category: "Best Practices", | ||
recommended: false, | ||
}, | ||
schema: [], | ||
messages: {}, | ||
type: "suggestion", | ||
}, | ||
create(context) { | ||
const classesUsedInTemplate: Record<string, SourceLocation> = {} | ||
|
||
return { | ||
SvelteElement(node) { | ||
if (node.kind !== "html") { | ||
return | ||
} | ||
const classes = node.startTag.attributes.flatMap(findClassesInAttribute) | ||
for (const className of classes) { | ||
classesUsedInTemplate[className] = node.startTag.loc | ||
} | ||
}, | ||
"Program:exit"() { | ||
const styleContext = context.parserServices.getStyleContext() | ||
if (["parse-error", "unknown-lang"].includes(styleContext.status)) { | ||
return | ||
} | ||
const classesUsedInStyle = | ||
styleContext.sourceAst != null | ||
? findClassesInPostCSSNode(styleContext.sourceAst) | ||
: [] | ||
for (const className in classesUsedInTemplate) { | ||
if (!classesUsedInStyle.includes(className)) { | ||
context.report({ | ||
loc: classesUsedInTemplate[className], | ||
message: `Unused class "${className}".`, | ||
}) | ||
} | ||
} | ||
}, | ||
} | ||
}, | ||
}) | ||
|
||
/** | ||
* Extract all class names used in a HTML element attribute. | ||
*/ | ||
function findClassesInAttribute( | ||
attribute: | ||
| SvelteAttribute | ||
| SvelteShorthandAttribute | ||
| SvelteSpreadAttribute | ||
| SvelteDirective | ||
| SvelteStyleDirective | ||
| SvelteSpecialDirective, | ||
): string[] { | ||
if (attribute.type === "SvelteAttribute" && attribute.key.name === "class") { | ||
return attribute.value.flatMap((value) => | ||
value.type === "SvelteLiteral" ? value.value.trim().split(/\s+/u) : [], | ||
) | ||
} | ||
if (attribute.type === "SvelteDirective" && attribute.kind === "Class") { | ||
return [attribute.key.name.name] | ||
} | ||
return [] | ||
} | ||
|
||
/** | ||
* Extract all class names used in a PostCSS node. | ||
*/ | ||
function findClassesInPostCSSNode(node: AnyNode): string[] { | ||
if (node.type === "rule") { | ||
let classes = node.nodes.flatMap(findClassesInPostCSSNode) | ||
const processor = selectorParser() | ||
classes = classes.concat( | ||
findClassesInSelector(processor.astSync(node.selector)), | ||
) | ||
return classes | ||
} | ||
if (node.type === "root" || node.type === "atrule") { | ||
return node.nodes.flatMap(findClassesInPostCSSNode) | ||
} | ||
return [] | ||
} | ||
|
||
/** | ||
* Extract all class names used in a PostCSS selector. | ||
*/ | ||
function findClassesInSelector(node: SelectorNode): string[] { | ||
if (node.type === "class") { | ||
return [node.value] | ||
} | ||
if ( | ||
node.type === "pseudo" || | ||
node.type === "root" || | ||
node.type === "selector" | ||
) { | ||
return node.nodes.flatMap(findClassesInSelector) | ||
} | ||
return [] | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
tests/fixtures/rules/no-unused-class-name/invalid/class-directive01-errors.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
- message: Unused class "first". | ||
line: 1 | ||
column: 1 | ||
suggestions: null | ||
- message: Unused class "second". | ||
line: 3 | ||
column: 1 | ||
suggestions: null |
3 changes: 3 additions & 0 deletions
3
tests/fixtures/rules/no-unused-class-name/invalid/class-directive01-input.svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<div class:first={true}>Hello</div> | ||
|
||
<span class:second={false}>World!</span> |
12 changes: 12 additions & 0 deletions
12
tests/fixtures/rules/no-unused-class-name/invalid/multiline-class-names01-errors.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
- message: Unused class "div-class-two". | ||
line: 2 | ||
column: 1 | ||
suggestions: null | ||
- message: Unused class "span-class-two". | ||
line: 4 | ||
column: 1 | ||
suggestions: null | ||
- message: Unused class "span-class-three". | ||
line: 4 | ||
column: 1 | ||
suggestions: null |
19 changes: 19 additions & 0 deletions
19
tests/fixtures/rules/no-unused-class-name/invalid/multiline-class-names01-input.svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<!-- eslint-disable prettier/prettier --> | ||
<div class="div-class div-class-two">Hello</div> | ||
|
||
<span | ||
class=" | ||
span-class | ||
span-class-two | ||
span-class-three | ||
">World!</span> | ||
|
||
<style> | ||
.div-class { | ||
color: red; | ||
} | ||
|
||
.span-class { | ||
font-weight: bold; | ||
} | ||
</style> |
12 changes: 12 additions & 0 deletions
12
tests/fixtures/rules/no-unused-class-name/invalid/multiple-class-names01-errors.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
- message: Unused class "div-class-two". | ||
line: 1 | ||
column: 1 | ||
suggestions: null | ||
- message: Unused class "span-class-two". | ||
line: 3 | ||
column: 1 | ||
suggestions: null | ||
- message: Unused class "span-class-three". | ||
line: 3 | ||
column: 1 | ||
suggestions: null |
13 changes: 13 additions & 0 deletions
13
tests/fixtures/rules/no-unused-class-name/invalid/multiple-class-names01-input.svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<div class="div-class div-class-two">Hello</div> | ||
|
||
<span class="span-class span-class-two span-class-three">World!</span> | ||
|
||
<style> | ||
.div-class { | ||
color: red; | ||
} | ||
|
||
.span-class { | ||
font-weight: bold; | ||
} | ||
</style> |
8 changes: 8 additions & 0 deletions
8
tests/fixtures/rules/no-unused-class-name/invalid/same-name-id01-errors.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
- message: Unused class "div-class". | ||
line: 1 | ||
column: 1 | ||
suggestions: null | ||
- message: Unused class "span-class". | ||
line: 3 | ||
column: 1 | ||
suggestions: null |
13 changes: 13 additions & 0 deletions
13
tests/fixtures/rules/no-unused-class-name/invalid/same-name-id01-input.svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<div class="div-class">Hello</div> | ||
|
||
<span class="span-class">World!</span> | ||
|
||
<style> | ||
#div-class { | ||
color: red; | ||
} | ||
|
||
#span-class { | ||
font-weight: bold; | ||
} | ||
</style> |
8 changes: 8 additions & 0 deletions
8
tests/fixtures/rules/no-unused-class-name/invalid/unused-class-name01-errors.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
- message: Unused class "div-class". | ||
line: 1 | ||
column: 1 | ||
suggestions: null | ||
- message: Unused class "span-class". | ||
line: 3 | ||
column: 1 | ||
suggestions: null |
3 changes: 3 additions & 0 deletions
3
tests/fixtures/rules/no-unused-class-name/invalid/unused-class-name01-input.svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<div class="div-class">Hello</div> | ||
|
||
<span class="span-class">World!</span> |
8 changes: 8 additions & 0 deletions
8
tests/fixtures/rules/no-unused-class-name/invalid/used-unrelated-class-name01-errors.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
- message: Unused class "div-class". | ||
line: 1 | ||
column: 1 | ||
suggestions: null | ||
- message: Unused class "span-class". | ||
line: 3 | ||
column: 1 | ||
suggestions: null |
9 changes: 9 additions & 0 deletions
9
tests/fixtures/rules/no-unused-class-name/invalid/used-unrelated-class-name01-input.svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<div class="div-class">Hello</div> | ||
|
||
<span class="span-class">World!</span> | ||
|
||
<style> | ||
.unrelated-class { | ||
color: red; | ||
} | ||
</style> |
9 changes: 9 additions & 0 deletions
9
tests/fixtures/rules/no-unused-class-name/valid/adjacent-sibling-combinator01-input.svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<div class="div-class">Hello</div> | ||
|
||
<span class="span-class">World!</span> | ||
|
||
<style> | ||
.div-class + .span-class { | ||
color: red; | ||
} | ||
</style> |
9 changes: 9 additions & 0 deletions
9
tests/fixtures/rules/no-unused-class-name/valid/child-combinator01-input.svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<div class="container"> | ||
<div class="div-class">Hello</div> | ||
</div> | ||
|
||
<style> | ||
.container > .div-class { | ||
color: red; | ||
} | ||
</style> |
9 changes: 9 additions & 0 deletions
9
tests/fixtures/rules/no-unused-class-name/valid/descendant-combinator01-input.svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<div class="container"> | ||
<div class="div-class">Hello</div> | ||
</div> | ||
|
||
<style> | ||
.container .div-class { | ||
color: red; | ||
} | ||
</style> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.