Skip to content

Commit a7f95ce

Browse files
authored
Fixed vue/no-unused-properties, vue/require-valid-default-prop, vue/require-default-prop and vue/no-multiple-objects-in-class rules crash on sparse arrays. (#1242)
1 parent 8c981ea commit a7f95ce

10 files changed

+119
-57
lines changed

lib/rules/no-multiple-objects-in-class.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const { defineTemplateBodyVisitor } = require('../utils')
2121
*/
2222
function countObjectExpression(node) {
2323
return node.value.expression.elements.filter(
24-
(element) => element.type === 'ObjectExpression'
24+
(element) => element && element.type === 'ObjectExpression'
2525
).length
2626
}
2727

lib/rules/require-default-prop.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*/
1111

1212
const utils = require('../utils')
13+
const { isDef } = require('../utils')
1314

1415
const NATIVE_TYPES = new Set([
1516
'String',
@@ -99,16 +100,21 @@ module.exports = {
99100
/**
100101
* Detects whether given value node is a Boolean type
101102
* @param {Expression | Pattern} value
102-
* @return {Boolean}
103+
* @return {boolean}
103104
*/
104105
function isValueNodeOfBooleanType(value) {
105-
return (
106-
(value.type === 'Identifier' && value.name === 'Boolean') ||
107-
(value.type === 'ArrayExpression' &&
108-
value.elements.length === 1 &&
109-
value.elements[0].type === 'Identifier' &&
110-
value.elements[0].name === 'Boolean')
111-
)
106+
if (value.type === 'Identifier' && value.name === 'Boolean') {
107+
return true
108+
}
109+
if (value.type === 'ArrayExpression') {
110+
const elements = value.elements.filter(isDef)
111+
return (
112+
elements.length === 1 &&
113+
elements[0].type === 'Identifier' &&
114+
elements[0].name === 'Boolean'
115+
)
116+
}
117+
return false
112118
}
113119

114120
/**

lib/rules/require-prop-type-constructor.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
'use strict'
66

77
const utils = require('../utils')
8+
const { isDef } = require('../utils')
89

910
// ------------------------------------------------------------------------------
1011
// Rule Definition
@@ -49,10 +50,11 @@ module.exports = {
4950
*/
5051
function checkPropertyNode(propName, node) {
5152
/** @type {ESNode[]} */
52-
const nodes = node.type === 'ArrayExpression' ? node.elements : [node]
53+
const nodes =
54+
node.type === 'ArrayExpression' ? node.elements.filter(isDef) : [node]
5355

5456
nodes
55-
.filter((prop) => prop && isForbiddenType(prop))
57+
.filter((prop) => isForbiddenType(prop))
5658
.forEach((prop) =>
5759
context.report({
5860
node: prop,

lib/rules/require-valid-default-prop.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ function getTypes(node) {
5858
return node.elements
5959
.filter(
6060
/**
61-
* @param {Expression | SpreadElement} item
61+
* @param {Expression | SpreadElement | null} item
6262
* @returns {item is Identifier}
6363
*/
64-
(item) => item.type === 'Identifier'
64+
(item) => item != null && item.type === 'Identifier'
6565
)
6666
.map((item) => item.name)
6767
}

lib/utils/index.js

+42-43
Original file line numberDiff line numberDiff line change
@@ -737,29 +737,27 @@ module.exports = {
737737
}
738738
})
739739
} else {
740-
return propsNode.value.elements
741-
.filter((prop) => prop)
742-
.map((prop) => {
743-
if (prop.type === 'Literal' || prop.type === 'TemplateLiteral') {
744-
const propName = getStringLiteralValue(prop)
745-
if (propName != null) {
746-
return {
747-
type: 'array',
748-
key: prop,
749-
propName,
750-
value: null,
751-
node: prop
752-
}
740+
return propsNode.value.elements.filter(isDef).map((prop) => {
741+
if (prop.type === 'Literal' || prop.type === 'TemplateLiteral') {
742+
const propName = getStringLiteralValue(prop)
743+
if (propName != null) {
744+
return {
745+
type: 'array',
746+
key: prop,
747+
propName,
748+
value: null,
749+
node: prop
753750
}
754751
}
755-
return {
756-
type: 'array',
757-
key: null,
758-
propName: null,
759-
value: null,
760-
node: prop
761-
}
762-
})
752+
}
753+
return {
754+
type: 'array',
755+
key: null,
756+
propName: null,
757+
value: null,
758+
node: prop
759+
}
760+
})
763761
}
764762
},
765763

@@ -810,29 +808,27 @@ module.exports = {
810808
}
811809
})
812810
} else {
813-
return emitsNode.value.elements
814-
.filter((prop) => prop)
815-
.map((prop) => {
816-
if (prop.type === 'Literal' || prop.type === 'TemplateLiteral') {
817-
const emitName = getStringLiteralValue(prop)
818-
if (emitName != null) {
819-
return {
820-
type: 'array',
821-
key: prop,
822-
emitName,
823-
value: null,
824-
node: prop
825-
}
811+
return emitsNode.value.elements.filter(isDef).map((prop) => {
812+
if (prop.type === 'Literal' || prop.type === 'TemplateLiteral') {
813+
const emitName = getStringLiteralValue(prop)
814+
if (emitName != null) {
815+
return {
816+
type: 'array',
817+
key: prop,
818+
emitName,
819+
value: null,
820+
node: prop
826821
}
827822
}
828-
return {
829-
type: 'array',
830-
key: null,
831-
emitName: null,
832-
value: null,
833-
node: prop
834-
}
835-
})
823+
}
824+
return {
825+
type: 'array',
826+
key: null,
827+
emitName: null,
828+
value: null,
829+
node: prop
830+
}
831+
})
836832
}
837833
},
838834

@@ -1109,7 +1105,10 @@ module.exports = {
11091105
*/
11101106
*iterateArrayExpression(node, groupName) {
11111107
for (const item of node.elements) {
1112-
if (item.type === 'Literal' || item.type === 'TemplateLiteral') {
1108+
if (
1109+
item &&
1110+
(item.type === 'Literal' || item.type === 'TemplateLiteral')
1111+
) {
11131112
const name = getStringLiteralValue(item)
11141113
if (name) {
11151114
yield { type: 'array', name, groupName, node: item }

tests/lib/rules/no-multiple-objects-in-class.js

+11
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@ ruleTester.run('no-multiple-objects-in-class', rule, {
4343
type: 'VAttribute'
4444
}
4545
]
46+
},
47+
48+
// sparse array
49+
{
50+
code: `<template><div v-bind:class="[,{'foo': isFoo}, {'bar': isBar}]" /></template>`,
51+
errors: [
52+
{
53+
message: 'Unexpected multiple objects. Merge objects.',
54+
type: 'VAttribute'
55+
}
56+
]
4657
}
4758
]
4859
})

tests/lib/rules/no-unused-properties.js

+15
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,21 @@ tester.run('no-unused-properties', rule, {
981981
}
982982
</script>`,
983983
options: [{ groups: ['props', 'setup'] }]
984+
},
985+
986+
// sparse array
987+
{
988+
filename: 'test.vue',
989+
code: `
990+
<template>
991+
<div>{{ count }}</div>
992+
</template>
993+
<script>
994+
export default {
995+
props: [, 'count']
996+
}
997+
</script>
998+
`
984999
}
9851000
],
9861001

tests/lib/rules/require-default-prop.js

+15
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,21 @@ ruleTester.run('require-default-prop', rule, {
179179
}
180180
}
181181
`
182+
},
183+
184+
// sparse array
185+
{
186+
filename: 'test.vue',
187+
code: `
188+
export default {
189+
props: {
190+
a: {
191+
type: [,Boolean]
192+
},
193+
b: [,Boolean],
194+
}
195+
}
196+
`
182197
}
183198
],
184199

tests/lib/rules/require-valid-default-prop.js

+14
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,20 @@ ruleTester.run('require-valid-default-prop', rule, {
181181
}
182182
}`,
183183
parserOptions
184+
},
185+
186+
// sparse array
187+
{
188+
filename: 'test.vue',
189+
code: `export default {
190+
props: {
191+
foo: {
192+
type: [,Object, Number],
193+
default: 10
194+
}
195+
}
196+
}`,
197+
parserOptions
184198
}
185199
],
186200

typings/eslint-plugin-vue/util-types/ast/es-ast.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ export interface ThisExpression extends HasParentNode {
298298
}
299299
export interface ArrayExpression extends HasParentNode {
300300
type: 'ArrayExpression'
301-
elements: (Expression | SpreadElement)[]
301+
elements: (Expression | SpreadElement | null)[]
302302
}
303303
export interface ObjectExpression extends HasParentNode {
304304
type: 'ObjectExpression'

0 commit comments

Comments
 (0)