Skip to content

feat: added the no-inline-styles rule #608

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
merged 9 commits into from
Nov 6, 2023
5 changes: 5 additions & 0 deletions .changeset/happy-monkeys-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-svelte': minor
---

feat: added the no-inline-styles rule
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ These rules relate to better ways of doing things to help you avoid problems:
| [svelte/no-at-debug-tags](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/) | disallow the use of `{@debug}` | :star: |
| [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. | |
| [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. | |
| [svelte/no-inline-styles](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-inline-styles/) | disallow attributes and directives that produce inline styles | |
| [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: |
| [svelte/no-reactive-literals](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-literals/) | don't assign literal values in reactive statements | :bulb: |
| [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 | |
Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ These rules relate to better ways of doing things to help you avoid problems:
| [svelte/no-at-debug-tags](./rules/no-at-debug-tags.md) | disallow the use of `{@debug}` | :star: |
| [svelte/no-ignored-unsubscribe](./rules/no-ignored-unsubscribe.md) | disallow ignoring the unsubscribe method returned by the `subscribe()` on Svelte stores. | |
| [svelte/no-immutable-reactive-statements](./rules/no-immutable-reactive-statements.md) | disallow reactive statements that don't reference reactive values. | |
| [svelte/no-inline-styles](./rules/no-inline-styles.md) | disallow attributes and directives that produce inline styles | |
| [svelte/no-reactive-functions](./rules/no-reactive-functions.md) | it's not necessary to define functions in reactive statements | :bulb: |
| [svelte/no-reactive-literals](./rules/no-reactive-literals.md) | don't assign literal values in reactive statements | :bulb: |
| [svelte/no-unused-class-name](./rules/no-unused-class-name.md) | disallow the use of a class in the template without a corresponding style | |
Expand Down
69 changes: 69 additions & 0 deletions docs/rules/no-inline-styles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
pageClass: 'rule-details'
sidebarDepth: 0
title: 'svelte/no-inline-styles'
description: 'disallow attributes and directives that produce inline styles'
---

# svelte/no-inline-styles

> disallow attributes and directives that produce inline styles

- :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 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.

<ESLintCodeBlock>

<!--eslint-skip-->

```svelte
<script>
/* eslint svelte/no-inline-styles: "error" */

import { fade } from 'svelte/transition';

export let classTwo;
export let blockDisplay;
</script>

<!-- ✓ GOOD -->
<span class="one">Hello World!</span>

<span class:two={classTwo}>Hello World!</span>

<!-- ✗ BAD -->
<span style="display: block;">Hello World!</span>

<span style:display={blockDisplay ? 'block' : 'inline'}>Hello World!</span>

<span transition:fade>Hello World!</span>
```

</ESLintCodeBlock>

## :wrench: Options

```json
{
"svelte/no-inline-styles": [
"error",
{
"allowTransitions": false
}
]
}
```

-

## :books: Further Reading

- [CSP documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)

## :mag: Implementation

- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/src/rules/no-inline-styles.ts)
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/tests/src/rules/no-inline-styles.ts)
53 changes: 53 additions & 0 deletions src/rules/no-inline-styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { createRule } from '../utils';

export default createRule('no-inline-styles', {
meta: {
docs: {
description: 'disallow attributes and directives that produce inline styles',
category: 'Best Practices',
recommended: false
},
schema: [
{
type: 'object',
properties: {
allowTransitions: {
type: 'boolean'
}
},
additionalProperties: false
}
],
messages: {
hasStyleAttribute: 'Found disallowed style attribute.',
hasStyleDirective: 'Found disallowed style directive.',
hasTransition: 'Found disallowed transition.'
},
type: 'suggestion'
},
create(context) {
const allowTransitions: boolean = context.options[0]?.allowTransitions ?? false;
return {
SvelteElement(node) {
if (node.kind !== 'html') {
return;
}
for (const attribute of node.startTag.attributes) {
if (attribute.type === 'SvelteStyleDirective') {
context.report({ loc: attribute.loc, messageId: 'hasStyleDirective' });
}
if (attribute.type === 'SvelteAttribute' && attribute.key.name === 'style') {
context.report({ loc: attribute.loc, messageId: 'hasStyleAttribute' });
}
if (
!allowTransitions &&
attribute.type === 'SvelteDirective' &&
attribute.kind === 'Transition'
) {
context.report({ loc: attribute.loc, messageId: 'hasTransition' });
}
}
}
};
}
});
2 changes: 2 additions & 0 deletions src/utils/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import noExportLoadInSvelteModuleInKitPages from '../rules/no-export-load-in-sve
import noExtraReactiveCurlies from '../rules/no-extra-reactive-curlies';
import noIgnoredUnsubscribe from '../rules/no-ignored-unsubscribe';
import noImmutableReactiveStatements from '../rules/no-immutable-reactive-statements';
import noInlineStyles from '../rules/no-inline-styles';
import noInnerDeclarations from '../rules/no-inner-declarations';
import noNotFunctionHandler from '../rules/no-not-function-handler';
import noObjectInTextMustaches from '../rules/no-object-in-text-mustaches';
Expand Down Expand Up @@ -91,6 +92,7 @@ export const rules = [
noExtraReactiveCurlies,
noIgnoredUnsubscribe,
noImmutableReactiveStatements,
noInlineStyles,
noInnerDeclarations,
noNotFunctionHandler,
noObjectInTextMustaches,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: Found disallowed style attribute.
line: 1
column: 7
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<span style="display: block;">Hello World!</span>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: Found disallowed style directive.
line: 5
column: 7
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
export let block;
</script>

<span style:display={block ? 'block' : 'inline-block'}>Hello World!</span>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- message: Found disallowed transition.
line: 5
column: 7
suggestions: null
- message: Found disallowed transition.
line: 7
column: 7
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
import { fade, fly } from 'svelte/transition';
</script>

<span transition:fade>Hello World!</span>

<span transition:fly={{ y: 200, duration: 2000 }}>Hello World!</span>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"options": [
{
"allowTransitions": true
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
import { fade, fly } from 'svelte/transition';
</script>

<span transition:fade>Hello World!</span>

<span transition:fly={{ y: 200, duration: 2000 }}>Hello World!</span>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<span class="my-class">Hello World!</span>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<span class:one={true}>Hello World!</span>
12 changes: 12 additions & 0 deletions tests/src/rules/no-inline-styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { RuleTester } from 'eslint';
import rule from '../../../src/rules/no-inline-styles';
import { loadTestCases } from '../../utils/utils';

const tester = new RuleTester({
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module'
}
});

tester.run('no-inline-styles', rule as any, loadTestCases('no-inline-styles'));