Skip to content

Commit 253a86e

Browse files
committed
Add return-in-computed-property rule.
1 parent 1d00dd6 commit 253a86e

File tree

3 files changed

+293
-0
lines changed

3 files changed

+293
-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+
```
+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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+
return (!treatUndefinedAsUnspecified && funcInfo.hasReturn) || (treatUndefinedAsUnspecified && funcInfo.hasReturnValue)
27+
}
28+
29+
// ----------------------------------------------------------------------
30+
// Public
31+
// ----------------------------------------------------------------------
32+
33+
return Object.assign({},
34+
{
35+
onCodePathStart (codePath, node) {
36+
funcInfo = {
37+
codePath,
38+
funcInfo: funcInfo,
39+
hasReturn: false,
40+
hasReturnValue: false,
41+
node
42+
}
43+
},
44+
onCodePathEnd () {
45+
funcInfo = funcInfo.funcInfo
46+
},
47+
ReturnStatement (node) {
48+
funcInfo.hasReturn = true
49+
funcInfo.hasReturnValue = Boolean(node.argument)
50+
},
51+
'FunctionExpression:exit' (node) {
52+
if (!isValidReturn() && funcInfo.codePath.currentSegments.some((segment) => segment.reachable)) {
53+
forbiddenNodes.push({
54+
hasReturn: funcInfo.hasReturn,
55+
node: funcInfo.node,
56+
type: 'return'
57+
})
58+
}
59+
}
60+
},
61+
utils.executeOnVueComponent(context, properties => {
62+
const computedProperties = utils.getComputedProperties(properties)
63+
64+
computedProperties.forEach(cp => {
65+
forbiddenNodes.forEach(el => {
66+
if (
67+
cp.value &&
68+
el.node.loc.start.line >= cp.value.loc.start.line &&
69+
el.node.loc.end.line <= cp.value.loc.end.line
70+
) {
71+
context.report({
72+
node: el.node,
73+
message: 'Expected to return a value in "{{name}}" computed property.',
74+
data: {
75+
name: cp.key
76+
}
77+
})
78+
}
79+
})
80+
})
81+
})
82+
)
83+
}
84+
85+
// ------------------------------------------------------------------------------
86+
// Rule Definition
87+
// ------------------------------------------------------------------------------
88+
89+
module.exports = {
90+
meta: {
91+
docs: {
92+
description: 'Enforces that a return statement is present in computed property.',
93+
category: 'Possible Errors',
94+
recommended: false
95+
},
96+
fixable: null, // or "code" or "whitespace"
97+
schema: [
98+
{
99+
type: 'object',
100+
properties: {
101+
treatUndefinedAsUnspecified: {
102+
type: 'boolean'
103+
}
104+
},
105+
additionalProperties: false
106+
}
107+
]
108+
},
109+
110+
create
111+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
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+
})

0 commit comments

Comments
 (0)