Skip to content

Commit ff28fd3

Browse files
authored
feat: added the no-inline-styles rule (#608)
1 parent 318b9bf commit ff28fd3

17 files changed

+188
-0
lines changed

.changeset/happy-monkeys-grin.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-svelte': minor
3+
---
4+
5+
feat: added the no-inline-styles rule

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ These rules relate to better ways of doing things to help you avoid problems:
342342
| [svelte/no-at-debug-tags](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/) | disallow the use of `{@debug}` | :star: |
343343
| [svelte/no-ignored-unsubscribe](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-ignored-unsubscribe/) | disallow ignoring the unsubscribe method returned by the `subscribe()` on Svelte stores. | |
344344
| [svelte/no-immutable-reactive-statements](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-immutable-reactive-statements/) | disallow reactive statements that don't reference reactive values. | |
345+
| [svelte/no-inline-styles](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-inline-styles/) | disallow attributes and directives that produce inline styles | |
345346
| [svelte/no-reactive-functions](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-functions/) | it's not necessary to define functions in reactive statements | :bulb: |
346347
| [svelte/no-reactive-literals](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-literals/) | don't assign literal values in reactive statements | :bulb: |
347348
| [svelte/no-unused-class-name](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-class-name/) | disallow the use of a class in the template without a corresponding style | |

docs/rules.md

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ These rules relate to better ways of doing things to help you avoid problems:
5555
| [svelte/no-at-debug-tags](./rules/no-at-debug-tags.md) | disallow the use of `{@debug}` | :star: |
5656
| [svelte/no-ignored-unsubscribe](./rules/no-ignored-unsubscribe.md) | disallow ignoring the unsubscribe method returned by the `subscribe()` on Svelte stores. | |
5757
| [svelte/no-immutable-reactive-statements](./rules/no-immutable-reactive-statements.md) | disallow reactive statements that don't reference reactive values. | |
58+
| [svelte/no-inline-styles](./rules/no-inline-styles.md) | disallow attributes and directives that produce inline styles | |
5859
| [svelte/no-reactive-functions](./rules/no-reactive-functions.md) | it's not necessary to define functions in reactive statements | :bulb: |
5960
| [svelte/no-reactive-literals](./rules/no-reactive-literals.md) | don't assign literal values in reactive statements | :bulb: |
6061
| [svelte/no-unused-class-name](./rules/no-unused-class-name.md) | disallow the use of a class in the template without a corresponding style | |

docs/rules/no-inline-styles.md

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
pageClass: 'rule-details'
3+
sidebarDepth: 0
4+
title: 'svelte/no-inline-styles'
5+
description: 'disallow attributes and directives that produce inline styles'
6+
---
7+
8+
# svelte/no-inline-styles
9+
10+
> disallow attributes and directives that produce inline styles
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+
14+
## :book: Rule Details
15+
16+
This rule reports all attributes and directives that would compile to inline styles. This is mainly useful when adding Content Security Policy to your app, as having inline styles requires the `style-src: 'unsafe-inline'` directive, which is generally discouraged and unsafe.
17+
18+
<ESLintCodeBlock>
19+
20+
<!--eslint-skip-->
21+
22+
```svelte
23+
<script>
24+
/* eslint svelte/no-inline-styles: "error" */
25+
26+
import { fade } from 'svelte/transition';
27+
28+
export let classTwo;
29+
export let blockDisplay;
30+
</script>
31+
32+
<!-- ✓ GOOD -->
33+
<span class="one">Hello World!</span>
34+
35+
<span class:two={classTwo}>Hello World!</span>
36+
37+
<!-- ✗ BAD -->
38+
<span style="display: block;">Hello World!</span>
39+
40+
<span style:display={blockDisplay ? 'block' : 'inline'}>Hello World!</span>
41+
42+
<span transition:fade>Hello World!</span>
43+
```
44+
45+
</ESLintCodeBlock>
46+
47+
## :wrench: Options
48+
49+
```json
50+
{
51+
"svelte/no-inline-styles": [
52+
"error",
53+
{
54+
"allowTransitions": false
55+
}
56+
]
57+
}
58+
```
59+
60+
- `allowTransitions` ... Most svelte transitions (including the built-in ones) use inline styles. However, it is theoretically possible to only use transitions that don't (see this [issue](https://github.com/sveltejs/svelte/issues/6662) about removing inline styles from built-in transitions). This option allows transitions to be used in such cases. Default `false`.
61+
62+
## :books: Further Reading
63+
64+
- [CSP documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
65+
66+
## :mag: Implementation
67+
68+
- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/src/rules/no-inline-styles.ts)
69+
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/tests/src/rules/no-inline-styles.ts)

src/rules/no-inline-styles.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { createRule } from '../utils';
2+
3+
export default createRule('no-inline-styles', {
4+
meta: {
5+
docs: {
6+
description: 'disallow attributes and directives that produce inline styles',
7+
category: 'Best Practices',
8+
recommended: false
9+
},
10+
schema: [
11+
{
12+
type: 'object',
13+
properties: {
14+
allowTransitions: {
15+
type: 'boolean'
16+
}
17+
},
18+
additionalProperties: false
19+
}
20+
],
21+
messages: {
22+
hasStyleAttribute: 'Found disallowed style attribute.',
23+
hasStyleDirective: 'Found disallowed style directive.',
24+
hasTransition: 'Found disallowed transition.'
25+
},
26+
type: 'suggestion'
27+
},
28+
create(context) {
29+
const allowTransitions: boolean = context.options[0]?.allowTransitions ?? false;
30+
return {
31+
SvelteElement(node) {
32+
if (node.kind !== 'html') {
33+
return;
34+
}
35+
for (const attribute of node.startTag.attributes) {
36+
if (attribute.type === 'SvelteStyleDirective') {
37+
context.report({ loc: attribute.loc, messageId: 'hasStyleDirective' });
38+
}
39+
if (attribute.type === 'SvelteAttribute' && attribute.key.name === 'style') {
40+
context.report({ loc: attribute.loc, messageId: 'hasStyleAttribute' });
41+
}
42+
if (
43+
!allowTransitions &&
44+
attribute.type === 'SvelteDirective' &&
45+
attribute.kind === 'Transition'
46+
) {
47+
context.report({ loc: attribute.loc, messageId: 'hasTransition' });
48+
}
49+
}
50+
}
51+
};
52+
}
53+
});

src/utils/rules.ts

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import noExportLoadInSvelteModuleInKitPages from '../rules/no-export-load-in-sve
2929
import noExtraReactiveCurlies from '../rules/no-extra-reactive-curlies';
3030
import noIgnoredUnsubscribe from '../rules/no-ignored-unsubscribe';
3131
import noImmutableReactiveStatements from '../rules/no-immutable-reactive-statements';
32+
import noInlineStyles from '../rules/no-inline-styles';
3233
import noInnerDeclarations from '../rules/no-inner-declarations';
3334
import noNotFunctionHandler from '../rules/no-not-function-handler';
3435
import noObjectInTextMustaches from '../rules/no-object-in-text-mustaches';
@@ -91,6 +92,7 @@ export const rules = [
9192
noExtraReactiveCurlies,
9293
noIgnoredUnsubscribe,
9394
noImmutableReactiveStatements,
95+
noInlineStyles,
9496
noInnerDeclarations,
9597
noNotFunctionHandler,
9698
noObjectInTextMustaches,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: Found disallowed style attribute.
2+
line: 1
3+
column: 7
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<span style="display: block;">Hello World!</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- message: Found disallowed style directive.
2+
line: 5
3+
column: 7
4+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
export let block;
3+
</script>
4+
5+
<span style:display={block ? 'block' : 'inline-block'}>Hello World!</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
- message: Found disallowed transition.
2+
line: 5
3+
column: 7
4+
suggestions: null
5+
- message: Found disallowed transition.
6+
line: 7
7+
column: 7
8+
suggestions: null
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
import { fade, fly } from 'svelte/transition';
3+
</script>
4+
5+
<span transition:fade>Hello World!</span>
6+
7+
<span transition:fly={{ y: 200, duration: 2000 }}>Hello World!</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"options": [
3+
{
4+
"allowTransitions": true
5+
}
6+
]
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
import { fade, fly } from 'svelte/transition';
3+
</script>
4+
5+
<span transition:fade>Hello World!</span>
6+
7+
<span transition:fly={{ y: 200, duration: 2000 }}>Hello World!</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<span class="my-class">Hello World!</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<span class:one={true}>Hello World!</span>

tests/src/rules/no-inline-styles.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { RuleTester } from 'eslint';
2+
import rule from '../../../src/rules/no-inline-styles';
3+
import { loadTestCases } from '../../utils/utils';
4+
5+
const tester = new RuleTester({
6+
parserOptions: {
7+
ecmaVersion: 2020,
8+
sourceType: 'module'
9+
}
10+
});
11+
12+
tester.run('no-inline-styles', rule as any, loadTestCases('no-inline-styles'));

0 commit comments

Comments
 (0)