Skip to content

Commit 8a86226

Browse files
authored
Add no-non-optimized-style-attributes rule (#129)
1 parent 5f7a741 commit 8a86226

File tree

13 files changed

+287
-0
lines changed

13 files changed

+287
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ These rules relate to better ways of doing things to help you avoid problems:
268268
|:--------|:------------|:---|
269269
| [@ota-meshi/svelte/button-has-type](https://ota-meshi.github.io/eslint-plugin-svelte/rules/button-has-type/) | disallow usage of button without an explicit type attribute | |
270270
| [@ota-meshi/svelte/no-at-debug-tags](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/) | disallow the use of `{@debug}` | :star: |
271+
| [@ota-meshi/svelte/no-non-optimized-style-attributes](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-non-optimized-style-attributes/) | disallow style attributes that cannot be optimized | |
271272
| [@ota-meshi/svelte/no-unused-svelte-ignore](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/) | disallow unused svelte-ignore comments | :star: |
272273
| [@ota-meshi/svelte/no-useless-mustaches](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :wrench: |
273274

docs/rules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ These rules relate to better ways of doing things to help you avoid problems:
4141
|:--------|:------------|:---|
4242
| [@ota-meshi/svelte/button-has-type](./rules/button-has-type.md) | disallow usage of button without an explicit type attribute | |
4343
| [@ota-meshi/svelte/no-at-debug-tags](./rules/no-at-debug-tags.md) | disallow the use of `{@debug}` | :star: |
44+
| [@ota-meshi/svelte/no-non-optimized-style-attributes](./rules/no-non-optimized-style-attributes.md) | disallow style attributes that cannot be optimized | |
4445
| [@ota-meshi/svelte/no-unused-svelte-ignore](./rules/no-unused-svelte-ignore.md) | disallow unused svelte-ignore comments | :star: |
4546
| [@ota-meshi/svelte/no-useless-mustaches](./rules/no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :wrench: |
4647

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
---
2+
pageClass: "rule-details"
3+
sidebarDepth: 0
4+
title: "@ota-meshi/svelte/no-non-optimized-style-attributes"
5+
description: "disallow style attributes that cannot be optimized"
6+
---
7+
8+
# @ota-meshi/svelte/no-non-optimized-style-attributes
9+
10+
> disallow style attributes that cannot be optimized
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 `style` attributes written in a format that cannot be optimized.
17+
18+
Svelte parses the content written in the style attribute and tries to optimize it. (See [https://github.com/sveltejs/svelte/pull/810](https://github.com/sveltejs/svelte/pull/810))
19+
If Svelte can be successfully optimized, Svelte can minimize the number of re-renders.
20+
21+
e.g.
22+
23+
template:
24+
25+
```html
26+
<div
27+
style="
28+
font-size: 12px;
29+
color: {color};
30+
transform: translate({x}px, {y}px);
31+
"
32+
/>
33+
```
34+
35+
compiled:
36+
37+
```js
38+
div.style.setProperty("font-size", "12px") // font-size style is not updated once it is initially set.
39+
div.style.setProperty("color", color) // color style is updated only when color variable is updated.
40+
div.style.setProperty("transform", `translate(${x}px, ${y}px)`) // transform style is updated only when x, or y variables is updated.
41+
```
42+
43+
However, if the optimization fails, it will be re-rendered triggered by the update of all variables described in the style attribute.
44+
45+
e.g.
46+
47+
template:
48+
49+
```html
50+
<script>
51+
$: transformStyle = `transform: translate(${x}px, ${y}px)`
52+
</script>
53+
54+
<div
55+
style="
56+
font-size: 12px;
57+
color: {color};
58+
{transformStyle}
59+
"
60+
/>
61+
```
62+
63+
compiled:
64+
65+
```js
66+
// If any of variables color, x, or y are updated, all styles will be updated.
67+
div.setAttribute(
68+
"style",
69+
`font-size: 12px; color: ${color}; ${/* transformStyle */ ctx[0]}`,
70+
)
71+
```
72+
73+
Examples:
74+
75+
<ESLintCodeBlock>
76+
77+
<!--eslint-skip-->
78+
79+
```svelte
80+
<script>
81+
/* eslint @ota-meshi/svelte/no-non-optimized-style-attributes: "error" */
82+
let color = "blue"
83+
let x = 12,
84+
y = 12
85+
</script>
86+
87+
<!-- ✓ GOOD -->
88+
<div
89+
style="font-size: 12px; color: {color}; transform: translate({x}px, {y}px);"
90+
/>
91+
<div style={pointerEvents === false ? "pointer-events:none;" : ""} />
92+
93+
<!-- ✗ BAD -->
94+
<div style="font-size: 12px; color: {color}; {transformStyle}" />
95+
<div style:pointer-events={pointerEvents ? null : "none"} />
96+
97+
<div style="font-size: 12px; /* comment */ color: {color};" />
98+
<div style="font-size: 12px; {key}: red;" />
99+
```
100+
101+
</ESLintCodeBlock>
102+
103+
## :wrench: Options
104+
105+
Nothing.
106+
107+
## :mag: Implementation
108+
109+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/no-non-optimized-style-attributes.ts)
110+
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/no-non-optimized-style-attributes.ts)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { createRule } from "../utils"
2+
import { parseStyleAttributeValue } from "../utils/css-utils"
3+
4+
export default createRule("no-non-optimized-style-attributes", {
5+
meta: {
6+
docs: {
7+
description: "disallow style attributes that cannot be optimized",
8+
category: "Best Practices",
9+
recommended: false,
10+
},
11+
schema: [],
12+
messages: {
13+
shorthand:
14+
"It cannot be optimized because style attribute is specified using shorthand.",
15+
comment: "It cannot be optimized because contains comments.",
16+
interpolationKey:
17+
"It cannot be optimized because property of style declaration contain interpolation.",
18+
complex: "It cannot be optimized because too complex.",
19+
},
20+
type: "suggestion",
21+
},
22+
create(context) {
23+
return {
24+
SvelteShorthandAttribute(node) {
25+
if (node.key.name !== "style") {
26+
return
27+
}
28+
29+
context.report({
30+
node,
31+
messageId: "shorthand",
32+
})
33+
},
34+
SvelteAttribute(node) {
35+
if (node.key.name !== "style") {
36+
return
37+
}
38+
const root = parseStyleAttributeValue(node, context)
39+
if (!root) {
40+
return
41+
}
42+
43+
for (const child of root.nodes) {
44+
if (child.type === "decl") {
45+
if (child.unsafe) {
46+
context.report({
47+
node,
48+
loc: child.loc,
49+
messageId: "complex",
50+
})
51+
} else if (child.prop.name.includes("{")) {
52+
context.report({
53+
node,
54+
loc: child.prop.loc,
55+
messageId: "interpolationKey",
56+
})
57+
}
58+
} else if (child.type === "comment") {
59+
context.report({
60+
node,
61+
loc: child.loc,
62+
messageId: "comment",
63+
})
64+
} else {
65+
context.report({
66+
node,
67+
loc: child.loc,
68+
messageId: "complex",
69+
})
70+
}
71+
}
72+
},
73+
}
74+
},
75+
})

src/utils/rules.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import noDupeElseIfBlocks from "../rules/no-dupe-else-if-blocks"
1313
import noDupeStyleProperties from "../rules/no-dupe-style-properties"
1414
import noDynamicSlotName from "../rules/no-dynamic-slot-name"
1515
import noInnerDeclarations from "../rules/no-inner-declarations"
16+
import noNonOptimizedStyleAttributes from "../rules/no-non-optimized-style-attributes"
1617
import noNotFunctionHandler from "../rules/no-not-function-handler"
1718
import noObjectInTextMustaches from "../rules/no-object-in-text-mustaches"
1819
import noShorthandStylePropertyOverrides from "../rules/no-shorthand-style-property-overrides"
@@ -43,6 +44,7 @@ export const rules = [
4344
noDupeStyleProperties,
4445
noDynamicSlotName,
4546
noInnerDeclarations,
47+
noNonOptimizedStyleAttributes,
4648
noNotFunctionHandler,
4749
noObjectInTextMustaches,
4850
noShorthandStylePropertyOverrides,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[
2+
{
3+
"message": "It cannot be optimized because contains comments.",
4+
"line": 5,
5+
"column": 30
6+
}
7+
]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let color = "blue"
3+
</script>
4+
5+
<div style="font-size: 12px; /* comment */ color: {color};" />
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[
2+
{
3+
"message": "It cannot be optimized because property of style declaration contain interpolation.",
4+
"line": 6,
5+
"column": 30
6+
}
7+
]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
let color = "blue"
3+
let key = "color"
4+
</script>
5+
6+
<div style="font-size: 12px; {key}: {color};" />
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"message": "It cannot be optimized because too complex.",
4+
"line": 14,
5+
"column": 12
6+
},
7+
{
8+
"message": "It cannot be optimized because too complex.",
9+
"line": 17,
10+
"column": 46
11+
},
12+
{
13+
"message": "It cannot be optimized because contains comments.",
14+
"line": 20,
15+
"column": 30
16+
},
17+
{
18+
"message": "It cannot be optimized because property of style declaration contain interpolation.",
19+
"line": 21,
20+
"column": 30
21+
}
22+
]

0 commit comments

Comments
 (0)