Skip to content

Commit 34fb5da

Browse files
committed
Prepare example tests for no-side-effects-in-computed-properties rule,
Move component detection logic to utils
1 parent 6b175fd commit 34fb5da

File tree

4 files changed

+232
-110
lines changed

4 files changed

+232
-110
lines changed

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

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,28 @@
22
* @fileoverview Don't introduce side effects in computed properties
33
* @author Michał Sajnóg
44
*/
5-
"use strict";
5+
'use strict'
66

7-
//------------------------------------------------------------------------------
7+
const utils = require('../utils')
8+
9+
function create (context) {
10+
return utils.executeOnVueComponent(context, (properties) => {
11+
})
12+
}
13+
14+
// ------------------------------------------------------------------------------
815
// Rule Definition
9-
//------------------------------------------------------------------------------
16+
// ------------------------------------------------------------------------------
1017

1118
module.exports = {
12-
meta: {
13-
docs: {
14-
description: "Don't introduce side effects in computed properties",
15-
category: "Fill me in",
16-
recommended: false
17-
},
18-
fixable: null, // or "code" or "whitespace"
19-
schema: [
20-
// fill in your schema
21-
]
19+
create,
20+
meta: {
21+
docs: {
22+
description: 'Don\'t introduce side effects in computed properties',
23+
category: 'Best Practices',
24+
recommended: false
2225
},
23-
24-
create: function(context) {
25-
26-
// variables should be defined here
27-
28-
//----------------------------------------------------------------------
29-
// Helpers
30-
//----------------------------------------------------------------------
31-
32-
// any helper functions should go here or else delete this section
33-
34-
//----------------------------------------------------------------------
35-
// Public
36-
//----------------------------------------------------------------------
37-
38-
return {
39-
40-
// give me methods
41-
42-
};
43-
}
44-
};
26+
fixable: null,
27+
schema: []
28+
}
29+
}

lib/rules/order-in-components.js

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
'use strict'
66

7+
const utils = require('../utils')
8+
79
const defaultOrder = [
810
['name', 'delimiters', 'functional', 'model'],
911
['components', 'directives', 'filters'],
@@ -36,38 +38,6 @@ const groups = {
3638
]
3739
}
3840

39-
function isComponentFile (node, path) {
40-
const isVueFile = path.endsWith('.vue') || path.endsWith('.jsx')
41-
return isVueFile && node.declaration.type === 'ObjectExpression'
42-
}
43-
44-
function isVueComponent (node) {
45-
const callee = node.callee
46-
47-
const isFullVueComponent = node.type === 'CallExpression' &&
48-
callee.type === 'MemberExpression' &&
49-
callee.object.type === 'Identifier' &&
50-
callee.object.name === 'Vue' &&
51-
callee.property.type === 'Identifier' &&
52-
callee.property.name === 'component' &&
53-
node.arguments.length &&
54-
node.arguments.slice(-1)[0].type === 'ObjectExpression'
55-
56-
const isDestructedVueComponent = callee.type === 'Identifier' &&
57-
callee.name === 'component'
58-
59-
return isFullVueComponent || isDestructedVueComponent
60-
}
61-
62-
function isVueInstance (node) {
63-
const callee = node.callee
64-
return node.type === 'NewExpression' &&
65-
callee.type === 'Identifier' &&
66-
callee.name === 'Vue' &&
67-
node.arguments.length &&
68-
node.arguments[0].type === 'ObjectExpression'
69-
}
70-
7141
function getOrderMap (order) {
7242
const orderMap = new Map()
7343

@@ -106,28 +76,13 @@ function checkOrder (propertiesNodes, orderMap, context) {
10676
function create (context) {
10777
const options = context.options[0] || {}
10878
const order = options.order || defaultOrder
109-
const filePath = context.getFilename()
11079

11180
const extendedOrder = order.map(property => groups[property] || property)
11281
const orderMap = getOrderMap(extendedOrder)
11382

114-
return {
115-
ExportDefaultDeclaration (node) {
116-
// export default {} in .vue || .jsx
117-
if (!isComponentFile(node, filePath)) return
118-
checkOrder(node.declaration.properties, orderMap, context)
119-
},
120-
CallExpression (node) {
121-
// Vue.component('xxx', {}) || component('xxx', {})
122-
if (!isVueComponent(node)) return
123-
checkOrder(node.arguments.slice(-1)[0].properties, orderMap, context)
124-
},
125-
NewExpression (node) {
126-
// new Vue({})
127-
if (!isVueInstance(node)) return
128-
checkOrder(node.arguments[0].properties, orderMap, context)
129-
}
130-
}
83+
return utils.executeOnVueComponent(context, (properties) => {
84+
checkOrder(properties, orderMap, context)
85+
})
13186
}
13287

13388
// ------------------------------------------------------------------------------

lib/utils/index.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,5 +241,82 @@ module.exports = {
241241
assert(typeof name === 'string')
242242

243243
return VOID_ELEMENT_NAMES.has(name.toLowerCase())
244+
},
245+
246+
/**
247+
* Check whether the given node is a Vue component based
248+
* on the filename and default export type
249+
* export default {} in .vue || .jsx
250+
* @param {ASTNode} node Node to check
251+
* @param {string} path File name with extension
252+
* @returns {boolean}
253+
*/
254+
isVueComponentFile (node, path) {
255+
const isVueFile = path.endsWith('.vue') || path.endsWith('.jsx')
256+
return isVueFile &&
257+
node.type === 'ExportDefaultDeclaration' &&
258+
node.declaration.type === 'ObjectExpression'
259+
},
260+
261+
/**
262+
* Check whether given node is Vue component
263+
* Vue.component('xxx', {}) || component('xxx', {})
264+
* @param {ASTNode} node Node to check
265+
* @returns {boolean}
266+
*/
267+
isVueComponent (node) {
268+
const callee = node.callee
269+
270+
const isFullVueComponent = node.type === 'CallExpression' &&
271+
callee.type === 'MemberExpression' &&
272+
callee.object.type === 'Identifier' &&
273+
callee.object.name === 'Vue' &&
274+
callee.property.type === 'Identifier' &&
275+
callee.property.name === 'component' &&
276+
node.arguments.length &&
277+
node.arguments.slice(-1)[0].type === 'ObjectExpression'
278+
279+
const isDestructedVueComponent = callee.type === 'Identifier' &&
280+
callee.name === 'component'
281+
282+
return isFullVueComponent || isDestructedVueComponent
283+
},
284+
285+
/**
286+
* Check whether given node is new Vue instance
287+
* new Vue({})
288+
* @param {ASTNode} node Node to check
289+
* @returns {boolean}
290+
*/
291+
isVueInstance (node) {
292+
const callee = node.callee
293+
return node.type === 'NewExpression' &&
294+
callee.type === 'Identifier' &&
295+
callee.name === 'Vue' &&
296+
node.arguments.length &&
297+
node.arguments[0].type === 'ObjectExpression'
298+
},
299+
300+
executeOnVueComponent (context, cb) {
301+
const filePath = context.getFilename()
302+
const _this = this
303+
304+
return {
305+
ExportDefaultDeclaration (node) {
306+
// export default {} in .vue || .jsx
307+
if (!_this.isVueComponentFile(node, filePath)) return
308+
cb(node.declaration.properties)
309+
},
310+
CallExpression (node) {
311+
// Vue.component('xxx', {}) || component('xxx', {})
312+
if (!_this.isVueComponent(node)) return
313+
cb(node.arguments.slice(-1)[0].properties)
314+
},
315+
NewExpression (node) {
316+
// new Vue({})
317+
if (!_this.isVueInstance(node)) return
318+
cb(node.arguments[0].properties)
319+
}
320+
}
244321
}
245322
}

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

Lines changed: 130 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,141 @@
22
* @fileoverview Don't introduce side effects in computed properties
33
* @author Michał Sajnóg
44
*/
5-
"use strict";
5+
'use strict'
66

7-
//------------------------------------------------------------------------------
7+
// ------------------------------------------------------------------------------
88
// Requirements
9-
//------------------------------------------------------------------------------
9+
// ------------------------------------------------------------------------------
1010

11-
var rule = require("../../../lib/rules/no-side-effects-in-computed-properties"),
11+
const rule = require('../../../lib/rules/no-side-effects-in-computed-properties')
12+
const { RuleTester } = require('eslint')
1213

13-
RuleTester = require("eslint").RuleTester;
14+
const parserOptions = {
15+
ecmaVersion: 6,
16+
sourceType: 'module',
17+
ecmaFeatures: { experimentalObjectRestSpread: true }
18+
}
1419

15-
16-
//------------------------------------------------------------------------------
20+
// ------------------------------------------------------------------------------
1721
// Tests
18-
//------------------------------------------------------------------------------
19-
20-
var ruleTester = new RuleTester();
21-
ruleTester.run("no-side-effects-in-computed-properties", rule, {
22-
23-
valid: [
22+
// ------------------------------------------------------------------------------
2423

25-
// give me some code that won't trigger a warning
26-
],
27-
28-
invalid: [
29-
{
30-
code: "",
31-
errors: [{
32-
message: "Fill me in.",
33-
type: "Me too"
34-
}]
24+
const ruleTester = new RuleTester()
25+
ruleTester.run('no-side-effects-in-computed-properties', rule, {
26+
valid: [
27+
{
28+
code: `Vue.component('test', {
29+
computed: {
30+
test1() {
31+
return this.firstName + ' ' + this.lastName
32+
},
33+
test2() {
34+
return this.something.slice(0).reverse()
35+
},
36+
test3() {
37+
const example = this.something * 2
38+
return example + 'test'
39+
},
40+
test4() {
41+
return {
42+
...this.something,
43+
test: 'example'
44+
}
45+
},
46+
test5: {
47+
get() {
48+
return this.firstName + ' ' + this.lastName
49+
},
50+
set(newValue) {
51+
const names = newValue.split(' ')
52+
this.firstName = names[0]
53+
this.lastName = names[names.length - 1]
54+
}
55+
},
56+
test6: {
57+
get() {
58+
return this.something.slice(0).reverse()
59+
}
60+
},
61+
test7: {
62+
get() {
63+
const example = this.something * 2
64+
return example + 'test'
65+
}
66+
},
67+
test8: {
68+
get() {
69+
return {
70+
...this.something,
71+
test: 'example'
72+
}
73+
}
74+
}
75+
}
76+
})`,
77+
parserOptions
78+
}
79+
],
80+
invalid: [
81+
{
82+
code: `Vue.component('test', {
83+
computed: {
84+
test1() {
85+
this.firstName = 'lorem'
86+
return this.firstName + ' ' + this.lastName
87+
},
88+
test2() {
89+
return this.something.reverse()
90+
},
91+
test3() {
92+
const test = this.another.something.push('example')
93+
return 'something'
94+
},
95+
test4: {
96+
get() {
97+
this.firstName = 'lorem'
98+
return this.firstName + ' ' + this.lastName
99+
}
100+
},
101+
test5: {
102+
get() {
103+
return this.something.reverse()
104+
}
105+
},
106+
test6: {
107+
get() {
108+
const test = this.another.something.push('example')
109+
return 'something'
110+
},
111+
set(newValue) {
112+
this.something = newValue
113+
}
114+
},
35115
}
36-
]
37-
});
116+
})`,
117+
parserOptions,
118+
errors: [{
119+
line: 4,
120+
message: 'Unexpected side effect in "test1" computed property'
121+
}, {
122+
line: 8,
123+
message: 'Unexpected side effect in "test2" computed property'
124+
}, {
125+
line: 11,
126+
message: 'Unexpected side effect in "test3" computed property'
127+
}, {
128+
line: 15,
129+
message: 'Unexpected side effect in "test4" computed property'
130+
}, {
131+
line: 21,
132+
message: 'Unexpected side effect in "test5" computed property'
133+
}, {
134+
line: 26,
135+
message: 'Unexpected side effect in "test6" computed property'
136+
}, {
137+
line: 31,
138+
message: 'Unexpected side effect in "test7" computed property'
139+
}]
140+
}
141+
]
142+
})

0 commit comments

Comments
 (0)