Skip to content

Commit 9d6d85f

Browse files
committed
Add rule require-render-return.
1 parent 3361366 commit 9d6d85f

File tree

4 files changed

+213
-1
lines changed

4 files changed

+213
-1
lines changed

docs/rules/require-render-return.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Enforces render function to always return value (require-render-return)
2+
3+
This rule aims to enforce render function to allways return value
4+
5+
## :book: Rule Details
6+
7+
:-1: Examples of **incorrect** code for this rule:
8+
9+
```js
10+
export default {
11+
render () {
12+
}
13+
}
14+
```
15+
```js
16+
export default {
17+
render (h) {
18+
if (foo) {
19+
return
20+
}
21+
}
22+
}
23+
```
24+
25+
:+1: Examples of **correct** code for this rule:
26+
27+
```js
28+
export default {
29+
render (h) {
30+
return
31+
}
32+
}
33+
```
34+
35+
## :wrench: Options
36+
37+
Nothing.

lib/rules/require-render-return.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @fileoverview Enforces render function to always return value.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
9+
// ------------------------------------------------------------------------------
10+
// Rule Definition
11+
// ------------------------------------------------------------------------------
12+
13+
module.exports = {
14+
meta: {
15+
docs: {
16+
description: 'Enforces render function to always return value.',
17+
category: 'Possible Errors',
18+
recommended: false
19+
},
20+
fixable: null, // or "code" or "whitespace"
21+
schema: []
22+
},
23+
24+
create (context) {
25+
// ----------------------------------------------------------------------
26+
// Public
27+
// ----------------------------------------------------------------------
28+
29+
return utils.executeOnVue(context, obj => {
30+
obj.properties
31+
.filter(item => item.type === 'Property' &&
32+
utils.getStaticPropertyName(item) === 'render' &&
33+
(item.value.type === 'ArrowFunctionExpression' || item.value.type === 'FunctionExpression') &&
34+
!item.value.expression // render: () => test
35+
)
36+
.forEach(item => {
37+
const body = item.value.body
38+
if (body.type === 'BlockStatement' && !body.body.find(item => item.type === 'ReturnStatement')) {
39+
context.report({
40+
node: item.key,
41+
message: 'Expected to return a value in render function.'
42+
})
43+
}
44+
})
45+
})
46+
}
47+
}

lib/utils/index.js

+38-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module.exports = {
2525
* this generates a warning.
2626
*
2727
* @param {RuleContext} context The rule context to use parser services.
28-
* @param {object} visitor The visitor.
28+
* @param {Object} visitor The visitor.
2929
* @returns {void}
3030
*/
3131
registerTemplateBodyVisitor (context, visitor) {
@@ -272,6 +272,43 @@ module.exports = {
272272
return members.reverse()
273273
},
274274

275+
/**
276+
* Gets the property name of a given node.
277+
* @param {ASTNode} node - The node to get.
278+
* @return {string|null} The property name if static. Otherwise, null.
279+
*/
280+
getStaticPropertyName (node) {
281+
let prop
282+
switch (node && node.type) {
283+
case 'Property':
284+
case 'MethodDefinition':
285+
prop = node.key
286+
break
287+
case 'MemberExpression':
288+
prop = node.property
289+
break
290+
// no default
291+
}
292+
293+
switch (prop && prop.type) {
294+
case 'Literal':
295+
return String(prop.value)
296+
case 'TemplateLiteral':
297+
if (prop.expressions.length === 0 && prop.quasis.length === 1) {
298+
return prop.quasis[0].value.cooked
299+
}
300+
break
301+
case 'Identifier':
302+
if (!node.computed) {
303+
return prop.name
304+
}
305+
break
306+
// no default
307+
}
308+
309+
return null
310+
},
311+
275312
/**
276313
* Get all computed properties by looking at all component's properties
277314
* @param {ObjectExpression} Object with component definition
+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* @fileoverview Enforces render function to always return value.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const rule = require('../../../lib/rules/require-render-return')
12+
const RuleTester = require('eslint').RuleTester
13+
14+
const parserOptions = {
15+
ecmaVersion: 6,
16+
sourceType: 'module',
17+
ecmaFeatures: { experimentalObjectRestSpread: true, jsx: true }
18+
}
19+
20+
// ------------------------------------------------------------------------------
21+
// Tests
22+
// ------------------------------------------------------------------------------
23+
24+
const ruleTester = new RuleTester()
25+
ruleTester.run('require-render-return', rule, {
26+
valid: [
27+
{
28+
code: `Vue.component('test', {
29+
render() {
30+
return {}
31+
}
32+
})`,
33+
parserOptions
34+
},
35+
{
36+
code: `Vue.component('test', {
37+
render() {
38+
return <div></div>
39+
}
40+
})`,
41+
parserOptions
42+
},
43+
{
44+
filename: 'test.vue',
45+
code: `export default {
46+
render() {
47+
return {}
48+
}
49+
}`,
50+
parserOptions
51+
},
52+
{
53+
filename: 'test.vue',
54+
code: `export default {
55+
render: () => null
56+
}`,
57+
parserOptions
58+
}
59+
],
60+
61+
invalid: [
62+
{
63+
filename: 'test.vue',
64+
code: `export default {
65+
render() {
66+
}
67+
}`,
68+
parserOptions,
69+
errors: [{
70+
message: 'Expected to return a value in render function.',
71+
type: 'Identifier',
72+
line: 2
73+
}]
74+
},
75+
{
76+
code: `Vue.component('test', {
77+
render: function () {
78+
if (a) {
79+
return
80+
}
81+
}
82+
})`,
83+
parserOptions,
84+
errors: [{
85+
message: 'Expected to return a value in render function.',
86+
type: 'Identifier',
87+
line: 2
88+
}]
89+
}
90+
]
91+
})

0 commit comments

Comments
 (0)