Skip to content

Commit dbb36d8

Browse files
committed
Add computed-property-return rule.
1 parent 4b2d945 commit dbb36d8

File tree

3 files changed

+292
-0
lines changed

3 files changed

+292
-0
lines changed
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Enforces that a return statement is present in computed property (computed-property-return)
2+
3+
Please describe the origin of the rule here.
4+
5+
## :book: Rule Details
6+
7+
This rule enforces that a return statement is present in computed properties.
8+
9+
:-1: Examples of **incorrect** code for this rule:
10+
11+
```js
12+
export default {
13+
computed: {
14+
foo () {
15+
},
16+
bar: function () {
17+
}
18+
}
19+
}
20+
21+
```
22+
23+
:+1: Examples of **correct** code for this rule:
24+
25+
```js
26+
export default {
27+
computed: {
28+
foo () {
29+
return true
30+
},
31+
bar: function () {
32+
return false
33+
}
34+
}
35+
}
36+
```
37+
38+
## :wrench: Options
39+
40+
This rule has an object option:
41+
- `"treatUndefinedAsUnspecified"`: `true` (default) allows implicitly returning undefined with a `return;` statement.
42+
43+
```
44+
vue/order-in-components: [2, {
45+
treatUndefinedAsUnspecified: true
46+
}]
47+
```

lib/rules/computed-property-return.js

+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 (computed-property-return)
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+
el.node &&
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+
}
+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* @fileoverview Enforces that a return statement is present in computed property (computed-property-return)
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const rule = require('../../../lib/rules/computed-property-return')
12+
13+
const RuleTester = require('eslint').RuleTester
14+
15+
// ------------------------------------------------------------------------------
16+
// Tests
17+
// ------------------------------------------------------------------------------
18+
19+
const ruleTester = new RuleTester()
20+
ruleTester.run('computed-property-return', 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+
}]
80+
},
81+
{
82+
filename: 'test.vue',
83+
code: `
84+
export default {
85+
computed: {
86+
foo: function () {
87+
}
88+
}
89+
}
90+
`,
91+
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
92+
errors: [{
93+
message: 'Expected to return a value in "foo" computed property.'
94+
}]
95+
},
96+
{
97+
filename: 'test.vue',
98+
code: `
99+
export default {
100+
computed: {
101+
foo: function () {
102+
if (a) {
103+
return
104+
}
105+
}
106+
}
107+
}
108+
`,
109+
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
110+
errors: [{
111+
message: 'Expected to return a value in "foo" computed property.'
112+
}]
113+
},
114+
{
115+
filename: 'test.vue',
116+
code: `
117+
export default {
118+
computed: {
119+
foo: {
120+
set () {
121+
},
122+
get () {
123+
}
124+
}
125+
}
126+
}
127+
`,
128+
parserOptions: { ecmaVersion: 8, sourceType: 'module' },
129+
errors: [{
130+
message: 'Expected to return a value in "foo" computed property.'
131+
}]
132+
}
133+
]
134+
})

0 commit comments

Comments
 (0)