Skip to content

Commit 603a6e1

Browse files
authored
⭐️New: Add vue/no-static-inline-styles rule (#843)
* ⭐️New: Add vue/no-static-inline-styles rule * Change the test name to no-static-inline-styles
1 parent 0c80259 commit 603a6e1

File tree

5 files changed

+500
-0
lines changed

5 files changed

+500
-0
lines changed

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ For example:
159159
| [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns | |
160160
| [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions | |
161161
| [vue/no-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax | |
162+
| [vue/no-static-inline-styles](./no-static-inline-styles.md) | disallow static inline `style` attributes | |
162163
| [vue/no-unsupported-features](./no-unsupported-features.md) | disallow unsupported Vue.js syntax on the specified version | :wrench: |
163164
| [vue/object-curly-spacing](./object-curly-spacing.md) | enforce consistent spacing inside braces | :wrench: |
164165
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | |

docs/rules/no-static-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: vue/no-static-inline-styles
5+
description: disallow static inline `style` attributes
6+
---
7+
# vue/no-static-inline-styles
8+
> disallow static inline `style` attributes
9+
10+
## :book: Rule Details
11+
12+
This rule reports static inline `style` bindings and `style` attributes.
13+
The styles reported in this rule mean that we recommend separating them into `<style>` tag.
14+
15+
<eslint-code-block :rules="{'vue/no-static-inline-styles': ['error']}">
16+
17+
```vue
18+
<template>
19+
<!-- ✓ GOOD -->
20+
<div :style="styleObject"></div>
21+
<div :style="{ backgroundImage: 'url('+img+')' }"></div>
22+
23+
<!-- ✗ BAD -->
24+
<div style="color: pink;"></div>
25+
<div :style="{ color: 'pink' }"></div>
26+
<div :style="[ { color: 'pink' }, { 'font-size': '85%' } ]"></div>
27+
<div :style="{ backgroundImage, color: 'pink' }"></div>
28+
</template>
29+
```
30+
31+
</eslint-code-block>
32+
33+
## :wrench: Options
34+
35+
```json
36+
{
37+
"vue/no-static-inline-styles": ["error", {
38+
"allowBinding": false
39+
}]
40+
}
41+
```
42+
43+
- allowBinding ... if `true`, allow binding static inline `style`. default `false`.
44+
45+
### `"allowBinding": true`
46+
47+
<eslint-code-block :rules="{'vue/no-static-inline-styles': ['error', {'allowBinding': true}]}">
48+
49+
```vue
50+
<template>
51+
<!-- ✓ GOOD -->
52+
<div :style="{ transform: 'scale(0.5)' }"></div>
53+
<div :style="[ { transform: 'scale(0.5)' }, { 'user-select': 'none' } ]"></div>
54+
55+
<!-- ✗ BAD -->
56+
<div style="transform: scale(0.5);"></div>
57+
</template>
58+
```
59+
60+
</eslint-code-block>
61+
62+
## :books: Further reading
63+
64+
- [Guide - Binding Inline Styles](https://vuejs.org/v2/guide/class-and-style.html#Binding-Inline-Styles)
65+
66+
## :mag: Implementation
67+
68+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-static-inline-styles.js)
69+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-static-inline-styles.js)

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ module.exports = {
5151
'no-shared-component-data': require('./rules/no-shared-component-data'),
5252
'no-side-effects-in-computed-properties': require('./rules/no-side-effects-in-computed-properties'),
5353
'no-spaces-around-equal-signs-in-attribute': require('./rules/no-spaces-around-equal-signs-in-attribute'),
54+
'no-static-inline-styles': require('./rules/no-static-inline-styles'),
5455
'no-template-key': require('./rules/no-template-key'),
5556
'no-template-shadow': require('./rules/no-template-shadow'),
5657
'no-textarea-mustache': require('./rules/no-textarea-mustache'),

lib/rules/no-static-inline-styles.js

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
module.exports = {
9+
meta: {
10+
type: 'suggestion',
11+
docs: {
12+
description: 'disallow static inline `style` attributes',
13+
category: undefined,
14+
url: 'https://eslint.vuejs.org/rules/no-static-inline-styles.html'
15+
},
16+
fixable: null,
17+
schema: [
18+
{
19+
type: 'object',
20+
properties: {
21+
allowBinding: {
22+
type: 'boolean'
23+
}
24+
},
25+
additionalProperties: false
26+
}
27+
],
28+
messages: {
29+
forbiddenStaticInlineStyle: 'Static inline `style` are forbidden.',
30+
forbiddenStyleAttr: '`style` attributes are forbidden.'
31+
}
32+
},
33+
create (context) {
34+
/**
35+
* Checks whether if the given property node is a static value.
36+
* @param {AssignmentProperty} prop property node to check
37+
* @returns {boolean} `true` if the given property node is a static value.
38+
*/
39+
function isStaticValue (prop) {
40+
return (
41+
!prop.computed &&
42+
prop.value.type === 'Literal' &&
43+
(prop.key.type === 'Identifier' || prop.key.type === 'Literal')
44+
)
45+
}
46+
47+
/**
48+
* Gets the static properties of a given expression node.
49+
* - If `SpreadElement` or computed property exists, it gets only the static properties before it.
50+
* `:style="{ color: 'red', display: 'flex', ...spread, width: '16px' }"`
51+
* ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
52+
* - If non-static object exists, it gets only the static properties up to that object.
53+
* `:style="[ { color: 'red' }, { display: 'flex', color, width: '16px' }, { height: '16px' } ]"`
54+
* ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
55+
* - If all properties are static properties, it returns one root node.
56+
* `:style="[ { color: 'red' }, { display: 'flex', width: '16px' } ]"`
57+
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
58+
* @param {VAttribute} node `:style` node to check
59+
* @returns {AssignmentProperty[] | [VAttribute]} the static properties.
60+
*/
61+
function getReportNodes (node) {
62+
const { value } = node
63+
if (!value) {
64+
return []
65+
}
66+
const { expression } = value
67+
if (!expression) {
68+
return []
69+
}
70+
71+
let elements
72+
if (expression.type === 'ObjectExpression') {
73+
elements = [expression]
74+
} else if (expression.type === 'ArrayExpression') {
75+
elements = expression.elements
76+
} else {
77+
return []
78+
}
79+
const staticProperties = []
80+
for (const element of elements) {
81+
if (!element) {
82+
continue
83+
}
84+
if (element.type !== 'ObjectExpression') {
85+
return staticProperties
86+
}
87+
88+
let isAllStatic = true
89+
for (const prop of element.properties) {
90+
if (prop.type === 'SpreadElement' || prop.computed) {
91+
// If `SpreadElement` or computed property exists, it gets only the static properties before it.
92+
return staticProperties
93+
}
94+
if (isStaticValue(prop)) {
95+
staticProperties.push(prop)
96+
} else {
97+
isAllStatic = false
98+
}
99+
}
100+
if (!isAllStatic) {
101+
// If non-static object exists, it gets only the static properties up to that object.
102+
return staticProperties
103+
}
104+
}
105+
// If all properties are static properties, it returns one root node.
106+
return [node]
107+
}
108+
109+
/**
110+
* Reports if the value is static.
111+
* @param {VAttribute} node `:style` node to check
112+
*/
113+
function verifyVBindStyle (node) {
114+
for (const n of getReportNodes(node)) {
115+
context.report({
116+
node: n,
117+
messageId: 'forbiddenStaticInlineStyle'
118+
})
119+
}
120+
}
121+
122+
const visitor = {
123+
"VAttribute[directive=false][key.name='style']" (node) {
124+
context.report({
125+
node,
126+
messageId: 'forbiddenStyleAttr'
127+
})
128+
}
129+
}
130+
if (!context.options[0] || !context.options[0].allowBinding) {
131+
visitor[
132+
"VAttribute[directive=true][key.name.name='bind'][key.argument.name='style']"
133+
] = verifyVBindStyle
134+
}
135+
136+
return utils.defineTemplateBodyVisitor(context, visitor)
137+
}
138+
}

0 commit comments

Comments
 (0)