Skip to content

Commit e56fbdb

Browse files
authored
feat: add svelte/no-trailing-spaces rule (#240)
* feat: add `svelte/no-inner-declarations` rule * Create curly-tigers-destroy.md * chore: update doc
1 parent 9789614 commit e56fbdb

19 files changed

+618
-0
lines changed

.changeset/curly-tigers-destroy.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-svelte": minor
3+
---
4+
5+
feat: add `svelte/no-trailing-spaces` rule

docs/rules.md

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ These rules extend the rules provided by ESLint itself to work well in Svelte:
7979
| Rule ID | Description | |
8080
|:--------|:------------|:---|
8181
| [svelte/no-inner-declarations](./rules/no-inner-declarations.md) | disallow variable or `function` declarations in nested blocks | :star: |
82+
| [svelte/no-trailing-spaces](./rules/no-trailing-spaces.md) | disallow trailing whitespace at the end of lines | :wrench: |
8283

8384
## System
8485

docs/rules/no-trailing-spaces.md

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "svelte/no-trailing-spaces"
5+
description: "disallow trailing whitespace at the end of lines"
6+
---
7+
8+
# svelte/no-trailing-spaces
9+
10+
> disallow trailing whitespace at the end of lines
11+
12+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
13+
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
14+
15+
## :book: Rule Details
16+
17+
This rule extends the base ESLint's [no-trailing-spaces] rule. The [no-trailing-spaces] rule does not understand HTML comments and will report trailing whitespace in HTML comments when using `ignoreComments` option.
18+
This rule supports HTML comments generated by [svelte-eslint-parser].
19+
20+
[svelte-eslint-parser]: https://github.com/ota-meshi/svelte-eslint-parser
21+
22+
<ESLintCodeBlock fix>
23+
24+
<!-- prettier-ignore-start -->
25+
<!--eslint-skip-->
26+
27+
```svelte
28+
<script>
29+
/* eslint svelte/no-trailing-spaces: "error" */
30+
31+
/* ✓ GOOD */
32+
var foo = 0;
33+
/* ✗ BAD */
34+
var foo = 0;
35+
</script>
36+
37+
<!-- ✓ GOOD -->
38+
<div>
39+
Text
40+
</div>
41+
42+
<!-- ✗ BAD -->
43+
<div>
44+
Text
45+
</div>
46+
```
47+
48+
<!-- prettier-ignore-end -->
49+
50+
</ESLintCodeBlock>
51+
52+
## :wrench: Options
53+
54+
```jsonc
55+
{
56+
"no-trailing-spaces": "off", // Don't need ESLint's no-trailing-spaces rule, so turn it off.
57+
"svelte/no-trailing-spaces": [
58+
"error",
59+
{
60+
"skipBlankLines": false,
61+
"ignoreComments": false
62+
}
63+
]
64+
}
65+
```
66+
67+
- `skipBlankLines` ... If `true`, allows trailing whitespace on empty lines.
68+
- `ignoreComments` ... If `true`, allows trailing whitespace in comments.
69+
70+
Same as [no-trailing-spaces] rule option. See [here](https://eslint.org/docs/rules/no-trailing-spaces#options) for details.
71+
72+
## :couple: Related rules
73+
74+
- [no-trailing-spaces]
75+
76+
[no-trailing-spaces]: https://eslint.org/docs/rules/no-trailing-spaces
77+
78+
## :mag: Implementation
79+
80+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/no-trailing-spaces.ts)
81+
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/no-trailing-spaces.ts)
82+
83+
<sup>Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/no-trailing-spaces)</sup>

src/configs/prettier.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export = {
1414
"svelte/max-attributes-per-line": "off",
1515
"svelte/mustache-spacing": "off",
1616
"svelte/no-spaces-around-equal-signs-in-attribute": "off",
17+
"svelte/no-trailing-spaces": "off",
1718
"svelte/shorthand-attribute": "off",
1819
"svelte/shorthand-directive": "off",
1920
},

src/rules/no-trailing-spaces.ts

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import type { AST } from "svelte-eslint-parser"
2+
import { createRule } from "../utils"
3+
4+
export default createRule("no-trailing-spaces", {
5+
meta: {
6+
type: "layout",
7+
docs: {
8+
description: "disallow trailing whitespace at the end of lines",
9+
category: "Extension Rules",
10+
recommended: false,
11+
extensionRule: "no-trailing-spaces",
12+
conflictWithPrettier: true,
13+
},
14+
fixable: "whitespace",
15+
schema: [
16+
{
17+
type: "object",
18+
properties: {
19+
skipBlankLines: { type: "boolean" },
20+
ignoreComments: { type: "boolean" },
21+
},
22+
additionalProperties: false,
23+
},
24+
],
25+
messages: {
26+
trailingSpace: "Trailing spaces not allowed.",
27+
},
28+
},
29+
create(context) {
30+
const options:
31+
| { skipBlankLines?: boolean; ignoreComments?: boolean }
32+
| undefined = context.options[0]
33+
const skipBlankLines = options?.skipBlankLines || false
34+
const ignoreComments = options?.ignoreComments || false
35+
36+
const sourceCode = context.getSourceCode()
37+
38+
const ignoreLineNumbers = new Set<number>()
39+
if (ignoreComments) {
40+
for (const { type, loc } of sourceCode.getAllComments()) {
41+
const endLine = type === "Block" ? loc.end.line - 1 : loc.end.line
42+
for (let i = loc.start.line; i <= endLine; i++) {
43+
ignoreLineNumbers.add(i)
44+
}
45+
}
46+
}
47+
48+
/**
49+
* Reports a given location.
50+
*/
51+
function report(loc: AST.SourceLocation) {
52+
context.report({
53+
loc,
54+
messageId: "trailingSpace",
55+
fix(fixer) {
56+
return fixer.removeRange([
57+
sourceCode.getIndexFromLoc(loc.start),
58+
sourceCode.getIndexFromLoc(loc.end),
59+
])
60+
},
61+
})
62+
}
63+
64+
/**
65+
* Collects the location of the given node as the ignore line numbers.
66+
*/
67+
function collectIgnoreLineNumbers({ loc }: { loc: AST.SourceLocation }) {
68+
const endLine = loc.end.line - 1
69+
for (let i = loc.start.line; i <= endLine; i++) {
70+
ignoreLineNumbers.add(i)
71+
}
72+
}
73+
74+
return {
75+
TemplateElement: collectIgnoreLineNumbers,
76+
...(ignoreComments
77+
? {
78+
SvelteHTMLComment: collectIgnoreLineNumbers,
79+
}
80+
: {}),
81+
"Program:exit"() {
82+
const lines = sourceCode.lines
83+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
84+
const line = lines[lineIndex]
85+
if (skipBlankLines && !line.trim()) {
86+
continue
87+
}
88+
const lineNumber = lineIndex + 1
89+
if (ignoreLineNumbers.has(lineNumber)) {
90+
continue
91+
}
92+
const trimmed = line.trimEnd()
93+
if (trimmed === line) {
94+
continue
95+
}
96+
report({
97+
start: { line: lineNumber, column: trimmed.length },
98+
end: { line: lineNumber, column: line.length },
99+
})
100+
}
101+
},
102+
}
103+
},
104+
})

src/utils/rules.ts

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import noShorthandStylePropertyOverrides from "../rules/no-shorthand-style-prope
2323
import noSpacesAroundEqualSignsInAttribute from "../rules/no-spaces-around-equal-signs-in-attribute"
2424
import noStoreAsync from "../rules/no-store-async"
2525
import noTargetBlank from "../rules/no-target-blank"
26+
import noTrailingSpaces from "../rules/no-trailing-spaces"
2627
import noUnknownStyleDirectiveProperty from "../rules/no-unknown-style-directive-property"
2728
import noUnusedSvelteIgnore from "../rules/no-unused-svelte-ignore"
2829
import noUselessMustaches from "../rules/no-useless-mustaches"
@@ -62,6 +63,7 @@ export const rules = [
6263
noSpacesAroundEqualSignsInAttribute,
6364
noStoreAsync,
6465
noTargetBlank,
66+
noTrailingSpaces,
6567
noUnknownStyleDirectiveProperty,
6668
noUnusedSvelteIgnore,
6769
noUselessMustaches,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"ignoreComments": true
5+
}
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
- message: Trailing spaces not allowed.
2+
line: 5
3+
column: 2
4+
suggestions: null
5+
- message: Trailing spaces not allowed.
6+
line: 9
7+
column: 6
8+
suggestions: null
9+
- message: Trailing spaces not allowed.
10+
line: 14
11+
column: 1
12+
suggestions: null
13+
- message: Trailing spaces not allowed.
14+
line: 23
15+
column: 12
16+
suggestions: null
17+
- message: Trailing spaces not allowed.
18+
line: 26
19+
column: 7
20+
suggestions: null
21+
- message: Trailing spaces not allowed.
22+
line: 27
23+
column: 7
24+
suggestions: null
25+
- message: Trailing spaces not allowed.
26+
line: 30
27+
column: 7
28+
suggestions: null
29+
- message: Trailing spaces not allowed.
30+
line: 33
31+
column: 7
32+
suggestions: null
33+
- message: Trailing spaces not allowed.
34+
line: 34
35+
column: 23
36+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<!-- prettier-ignore -->
2+
<script>
3+
const str = `
4+
5+
`
6+
// line comment
7+
/**
8+
* block comment
9+
*/
10+
/**
11+
* block comment2
12+
*/ const a = 42
13+
// empty line
14+
15+
// empty line
16+
</script>
17+
18+
<!--
19+
HTML comment
20+
-->
21+
22+
<!-- prettier-ignore -->
23+
<span> Text
24+
</span>
25+
<!-- prettier-ignore -->
26+
<span>
27+
Text
28+
</span>
29+
<!-- prettier-ignore -->
30+
<span>
31+
Text </span>
32+
<!-- prettier-ignore -->
33+
<span>
34+
<span>Text </span>
35+
</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<!-- prettier-ignore -->
2+
<script>
3+
const str = `
4+
5+
`
6+
// line comment
7+
/**
8+
* block comment
9+
*/
10+
/**
11+
* block comment2
12+
*/ const a = 42
13+
// empty line
14+
15+
// empty line
16+
</script>
17+
18+
<!--
19+
HTML comment
20+
-->
21+
22+
<!-- prettier-ignore -->
23+
<span> Text
24+
</span>
25+
<!-- prettier-ignore -->
26+
<span>
27+
Text
28+
</span>
29+
<!-- prettier-ignore -->
30+
<span>
31+
Text </span>
32+
<!-- prettier-ignore -->
33+
<span>
34+
<span>Text </span>
35+
</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"skipBlankLines": true
5+
}
6+
]
7+
}

0 commit comments

Comments
 (0)