Skip to content

Commit 1ea1396

Browse files
armano2michalsnik
authored andcommitted
Add no-shared-component-data rule. (#84)
* Add `no-shared-component-data` rule. * Remove redundant code & merge with master
1 parent 5ebdf71 commit 1ea1396

9 files changed

+230
-10
lines changed
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Enforces component's data property to be a function (no-shared-component-data)
2+
3+
When using the data property on a component (i.e. anywhere except on `new Vue`), the value must be a function that returns an object.
4+
5+
## :book: Rule Details
6+
7+
When the value of `data` is an object, it’s shared across all instances of a component.
8+
9+
:-1: Examples of **incorrect** code for this rule:
10+
11+
```js
12+
Vue.component('some-comp', {
13+
data: {
14+
foo: 'bar'
15+
}
16+
})
17+
```
18+
19+
:+1: Examples of **correct** code for this rule:
20+
21+
```js
22+
Vue.component('some-comp', {
23+
data: function () {
24+
return {
25+
foo: 'bar'
26+
}
27+
}
28+
})
29+
```
30+
31+
## :wrench: Options
32+
33+
Nothing.

lib/rules/name-property-casing.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function create (context) {
5555
// Public
5656
// ----------------------------------------------------------------------
5757

58-
return utils.executeOnVueComponent(context, (obj) => {
58+
return utils.executeOnVue(context, (obj) => {
5959
const node = obj.properties
6060
.filter(item => (
6161
item.type === 'Property' &&

lib/rules/no-async-in-computed-properties.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ function create (context) {
114114
})
115115
}
116116
},
117-
utils.executeOnVueComponent(context, (obj) => {
117+
utils.executeOnVue(context, (obj) => {
118118
const computedProperties = utils.getComputedProperties(obj)
119119

120120
computedProperties.forEach(cp => {

lib/rules/no-shared-component-data.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @fileoverview Enforces component's data property to be a function.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
9+
function create (context) {
10+
// ----------------------------------------------------------------------
11+
// Public
12+
// ----------------------------------------------------------------------
13+
14+
return utils.executeOnVueComponent(context, (obj) => {
15+
obj.properties
16+
.filter(p =>
17+
p.type === 'Property' &&
18+
p.key.type === 'Identifier' &&
19+
p.key.name === 'data' &&
20+
p.value.type !== 'FunctionExpression' &&
21+
p.value.type !== 'Identifier'
22+
)
23+
.forEach(cp => {
24+
context.report({
25+
node: cp.value,
26+
message: '`data` property in component must be a function'
27+
})
28+
})
29+
})
30+
}
31+
32+
// ------------------------------------------------------------------------------
33+
// Rule Definition
34+
// ------------------------------------------------------------------------------
35+
36+
module.exports = {
37+
meta: {
38+
docs: {
39+
description: "Enforces component's data property to be a function.",
40+
category: 'Possible Errors',
41+
recommended: false
42+
},
43+
fixable: null, // or "code" or "whitespace"
44+
schema: [
45+
// fill in your schema
46+
]
47+
},
48+
49+
create
50+
}

lib/rules/no-side-effects-in-computed-properties.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function create (context) {
3333
}
3434
}
3535
},
36-
utils.executeOnVueComponent(context, (obj) => {
36+
utils.executeOnVue(context, (obj) => {
3737
const computedProperties = utils.getComputedProperties(obj)
3838

3939
computedProperties.forEach(cp => {

lib/rules/order-in-components.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function create (context) {
8080
const extendedOrder = order.map(property => groups[property] || property)
8181
const orderMap = getOrderMap(extendedOrder)
8282

83-
return utils.executeOnVueComponent(context, (obj) => {
83+
return utils.executeOnVue(context, (obj) => {
8484
checkOrder(obj.properties, orderMap, context)
8585
})
8686
}

lib/rules/return-in-computed-property.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function create (context) {
6161
}
6262
}
6363
},
64-
utils.executeOnVueComponent(context, properties => {
64+
utils.executeOnVue(context, properties => {
6565
const computedProperties = utils.getComputedProperties(properties)
6666

6767
computedProperties.forEach(cp => {

lib/utils/index.js

+19-5
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,25 @@ module.exports = {
363363
node.arguments[0].type === 'ObjectExpression'
364364
},
365365

366+
executeOnVue (context, cb) {
367+
return Object.assign(
368+
this.executeOnVueComponent(context, cb),
369+
this.executeOnVueInstance(context, cb)
370+
)
371+
},
372+
373+
executeOnVueInstance (context, cb) {
374+
const _this = this
375+
376+
return {
377+
'NewExpression:exit' (node) {
378+
// new Vue({})
379+
if (!_this.isVueInstance(node)) return
380+
cb(node.arguments[0])
381+
}
382+
}
383+
},
384+
366385
executeOnVueComponent (context, cb) {
367386
const filePath = context.getFilename()
368387
const _this = this
@@ -377,11 +396,6 @@ module.exports = {
377396
// Vue.component('xxx', {}) || component('xxx', {})
378397
if (!_this.isVueComponent(node)) return
379398
cb(node.arguments.slice(-1)[0])
380-
},
381-
'NewExpression:exit' (node) {
382-
// new Vue({})
383-
if (!_this.isVueInstance(node)) return
384-
cb(node.arguments[0])
385399
}
386400
}
387401
}
+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* @fileoverview Enforces component's data property to be a function.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const rule = require('../../../lib/rules/no-shared-component-data')
12+
13+
const RuleTester = require('eslint').RuleTester
14+
15+
// ------------------------------------------------------------------------------
16+
// Tests
17+
// ------------------------------------------------------------------------------
18+
19+
const ruleTester = new RuleTester()
20+
ruleTester.run('no-shared-component-data', rule, {
21+
22+
valid: [
23+
{
24+
filename: 'test.js',
25+
code: `
26+
new Vue({
27+
data: function () {
28+
return {
29+
foo: 'bar'
30+
}
31+
}
32+
})
33+
`
34+
},
35+
{
36+
filename: 'test.js',
37+
code: `
38+
new Vue({
39+
data: {
40+
foo: 'bar'
41+
}
42+
})
43+
`
44+
},
45+
{
46+
filename: 'test.js',
47+
code: `
48+
Vue.component('some-comp', {
49+
data: function () {
50+
return {
51+
foo: 'bar'
52+
}
53+
}
54+
})
55+
`,
56+
parserOptions: { ecmaVersion: 6 }
57+
},
58+
{
59+
filename: 'test.vue',
60+
code: `
61+
export default {
62+
data: function () {
63+
return {
64+
foo: 'bar'
65+
}
66+
}
67+
}
68+
`,
69+
parserOptions: { ecmaVersion: 6, sourceType: 'module' }
70+
},
71+
{
72+
filename: 'test.vue',
73+
code: `
74+
export default {
75+
...foo
76+
}
77+
`,
78+
parserOptions: { ecmaVersion: 6, sourceType: 'module', ecmaFeatures: { experimentalObjectRestSpread: true }}
79+
},
80+
{
81+
filename: 'test.vue',
82+
code: `
83+
export default {
84+
data
85+
}
86+
`,
87+
parserOptions: { ecmaVersion: 6, sourceType: 'module' }
88+
}
89+
],
90+
91+
invalid: [
92+
{
93+
filename: 'test.js',
94+
code: `
95+
Vue.component('some-comp', {
96+
data: {
97+
foo: 'bar'
98+
}
99+
})
100+
`,
101+
parserOptions: { ecmaVersion: 6 },
102+
errors: [{
103+
message: '`data` property in component must be a function',
104+
line: 3
105+
}]
106+
},
107+
{
108+
filename: 'test.vue',
109+
code: `
110+
export default {
111+
data: {
112+
foo: 'bar'
113+
}
114+
}
115+
`,
116+
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
117+
errors: [{
118+
message: '`data` property in component must be a function',
119+
line: 3
120+
}]
121+
}
122+
]
123+
})

0 commit comments

Comments
 (0)