Skip to content

Commit b7e6ff1

Browse files
armano2michalsnik
authored andcommitted
Add return-in-computed-property rule. (#78)
* Add `return-in-computed-property` rule. * Add unit test proposed by @chrisvfritz * Fix issue with treatUndefinedAsUnspecified & add missing unit test
1 parent 7904536 commit b7e6ff1

File tree

3 files changed

+354
-0
lines changed

3 files changed

+354
-0
lines changed
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Enforces that a return statement is present in computed property (return-in-computed-property)
2+
3+
## :book: Rule Details
4+
5+
This rule enforces that a `return` statement is present in `computed` properties.
6+
7+
:-1: Examples of **incorrect** code for this rule:
8+
9+
```js
10+
export default {
11+
computed: {
12+
foo () {
13+
},
14+
bar: function () {
15+
}
16+
}
17+
}
18+
```
19+
20+
:+1: Examples of **correct** code for this rule:
21+
22+
```js
23+
export default {
24+
computed: {
25+
foo () {
26+
return true
27+
},
28+
bar: function () {
29+
return false
30+
}
31+
}
32+
}
33+
```
34+
35+
## :wrench: Options
36+
37+
This rule has an object option:
38+
- `"treatUndefinedAsUnspecified"`: `true` (default) disallows implicitly returning undefined with a `return;` statement.
39+
40+
```
41+
vue/return-in-computed-property: [2, {
42+
treatUndefinedAsUnspecified: true
43+
}]
44+
```
+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* @fileoverview Enforces that a return statement is present in computed property (return-in-computed-property)
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
9+
function create (context) {
10+
const options = context.options[0] || {}
11+
const treatUndefinedAsUnspecified = !(options.treatUndefinedAsUnspecified === false)
12+
13+
let funcInfo = {
14+
funcInfo: null,
15+
codePath: null,
16+
hasReturn: false,
17+
hasReturnValue: false,
18+
node: null
19+
}
20+
const forbiddenNodes = []
21+
22+
// ----------------------------------------------------------------------
23+
// Helpers
24+
// ----------------------------------------------------------------------
25+
function isValidReturn () {
26+
if (!funcInfo.hasReturn) {
27+
return false
28+
}
29+
return !treatUndefinedAsUnspecified || funcInfo.hasReturnValue
30+
}
31+
32+
// ----------------------------------------------------------------------
33+
// Public
34+
// ----------------------------------------------------------------------
35+
36+
return Object.assign({},
37+
{
38+
onCodePathStart (codePath, node) {
39+
funcInfo = {
40+
codePath,
41+
funcInfo: funcInfo,
42+
hasReturn: false,
43+
hasReturnValue: false,
44+
node
45+
}
46+
},
47+
onCodePathEnd () {
48+
funcInfo = funcInfo.funcInfo
49+
},
50+
ReturnStatement (node) {
51+
funcInfo.hasReturn = true
52+
funcInfo.hasReturnValue = Boolean(node.argument)
53+
},
54+
'FunctionExpression:exit' (node) {
55+
if (!isValidReturn()) {
56+
forbiddenNodes.push({
57+
hasReturn: funcInfo.hasReturn,
58+
node: funcInfo.node,
59+
type: 'return'
60+
})
61+
}
62+
}
63+
},
64+
utils.executeOnVueComponent(context, properties => {
65+
const computedProperties = utils.getComputedProperties(properties)
66+
67+
computedProperties.forEach(cp => {
68+
forbiddenNodes.forEach(el => {
69+
if (
70+
cp.value &&
71+
el.node.loc.start.line >= cp.value.loc.start.line &&
72+
el.node.loc.end.line <= cp.value.loc.end.line
73+
) {
74+
context.report({
75+
node: el.node,
76+
message: 'Expected to return a value in "{{name}}" computed property.',
77+
data: {
78+
name: cp.key
79+
}
80+
})
81+
}
82+
})
83+
})
84+
})
85+
)
86+
}
87+
88+
// ------------------------------------------------------------------------------
89+
// Rule Definition
90+
// ------------------------------------------------------------------------------
91+
92+
module.exports = {
93+
meta: {
94+
docs: {
95+
description: 'Enforces that a return statement is present in computed property.',
96+
category: 'Possible Errors',
97+
recommended: false
98+
},
99+
fixable: null, // or "code" or "whitespace"
100+
schema: [
101+
{
102+
type: 'object',
103+
properties: {
104+
treatUndefinedAsUnspecified: {
105+
type: 'boolean'
106+
}
107+
},
108+
additionalProperties: false
109+
}
110+
]
111+
},
112+
113+
create
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/**
2+
* @fileoverview Enforces that a return statement is present in computed property (return-in-computed-property)
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const rule = require('../../../lib/rules/return-in-computed-property')
12+
13+
const RuleTester = require('eslint').RuleTester
14+
15+
// ------------------------------------------------------------------------------
16+
// Tests
17+
// ------------------------------------------------------------------------------
18+
19+
const ruleTester = new RuleTester()
20+
ruleTester.run('return-in-computed-property', rule, {
21+
22+
valid: [
23+
{
24+
filename: 'test.vue',
25+
code: `
26+
export default {
27+
computed: {
28+
foo () {
29+
return true
30+
},
31+
bar: function () {
32+
return false
33+
},
34+
bar3: {
35+
set () {
36+
return true
37+
},
38+
get () {
39+
return true
40+
}
41+
}
42+
}
43+
}
44+
`,
45+
parserOptions: { ecmaVersion: 8, sourceType: 'module' }
46+
},
47+
{
48+
filename: 'test.vue',
49+
code: `
50+
export default {
51+
computed: {
52+
foo: {
53+
get () {
54+
return
55+
}
56+
}
57+
}
58+
}
59+
`,
60+
parserOptions: { ecmaVersion: 8, sourceType: 'module' },
61+
options: [{ treatUndefinedAsUnspecified: false }]
62+
}
63+
],
64+
65+
invalid: [
66+
{
67+
filename: 'test.vue',
68+
code: `
69+
export default {
70+
computed: {
71+
foo () {
72+
}
73+
}
74+
}
75+
`,
76+
parserOptions: { ecmaVersion: 8, sourceType: 'module' },
77+
errors: [{
78+
message: 'Expected to return a value in "foo" computed property.',
79+
line: 4
80+
}]
81+
},
82+
{
83+
filename: 'test.vue',
84+
code: `
85+
export default {
86+
computed: {
87+
foo: function () {
88+
}
89+
}
90+
}
91+
`,
92+
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
93+
errors: [{
94+
message: 'Expected to return a value in "foo" computed property.',
95+
line: 4
96+
}]
97+
},
98+
{
99+
filename: 'test.vue',
100+
code: `
101+
export default {
102+
computed: {
103+
foo: function () {
104+
if (a) {
105+
return
106+
}
107+
}
108+
}
109+
}
110+
`,
111+
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
112+
errors: [{
113+
message: 'Expected to return a value in "foo" computed property.',
114+
line: 4
115+
}]
116+
},
117+
{
118+
filename: 'test.vue',
119+
code: `
120+
export default {
121+
computed: {
122+
foo: {
123+
set () {
124+
},
125+
get () {
126+
}
127+
}
128+
}
129+
}
130+
`,
131+
parserOptions: { ecmaVersion: 8, sourceType: 'module' },
132+
errors: [{
133+
message: 'Expected to return a value in "foo" computed property.',
134+
line: 7
135+
}]
136+
},
137+
{
138+
filename: 'test.vue',
139+
code: `
140+
export default {
141+
computed: {
142+
foo: function () {
143+
function bar () {
144+
return this.baz * 2
145+
}
146+
bar()
147+
}
148+
}
149+
}
150+
`,
151+
parserOptions: { ecmaVersion: 8, sourceType: 'module' },
152+
errors: [{
153+
message: 'Expected to return a value in "foo" computed property.',
154+
line: 4
155+
}]
156+
},
157+
{
158+
filename: 'test.vue',
159+
code: `
160+
export default {
161+
computed: {
162+
foo () {
163+
},
164+
bar () {
165+
return
166+
}
167+
}
168+
}
169+
`,
170+
parserOptions: { ecmaVersion: 8, sourceType: 'module' },
171+
options: [{ treatUndefinedAsUnspecified: false }],
172+
errors: [{
173+
message: 'Expected to return a value in "foo" computed property.',
174+
line: 4
175+
}]
176+
},
177+
{
178+
filename: 'test.vue',
179+
code: `
180+
export default {
181+
computed: {
182+
foo () {
183+
return
184+
}
185+
}
186+
}
187+
`,
188+
parserOptions: { ecmaVersion: 8, sourceType: 'module' },
189+
options: [{ treatUndefinedAsUnspecified: true }],
190+
errors: [{
191+
message: 'Expected to return a value in "foo" computed property.',
192+
line: 4
193+
}]
194+
}
195+
]
196+
})

0 commit comments

Comments
 (0)