Skip to content

Commit a0b782c

Browse files
committed
Add support for reserved names in data & rename rule
1 parent cecd4bf commit a0b782c

File tree

5 files changed

+1272
-181
lines changed

5 files changed

+1272
-181
lines changed

docs/rules/no-duplicate-field-names.md renamed to docs/rules/no-dupe-keys.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Prevent duplicate field names (no-duplicate-field-names)
1+
# Prevent duplicate field names (no-dupe-keys)
22

33
This rule prevents to use duplicated names and reserved/internal names from vue.
44

@@ -61,7 +61,7 @@ This rule has an object option:
6161
### Example 1:
6262

6363
```
64-
vue/no-duplicate-field-names: [2, {
64+
vue/no-dupe-keys: [2, {
6565
reserved: ['foo']
6666
}]
6767
```
@@ -79,7 +79,7 @@ export default {
7979
### Example 2:
8080

8181
```
82-
vue/no-duplicate-field-names: [2, {
82+
vue/no-dupe-keys: [2, {
8383
scope: ['asyncComputed']
8484
}]
8585
```

lib/rules/no-duplicate-field-names.js renamed to lib/rules/no-dupe-keys.js

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
const utils = require('../utils')
88
const assert = require('assert')
9+
const getPropertyName = require('eslint/lib/ast-utils').getStaticPropertyName
910

1011
const RESERVER_NAMES = new Set(require('../utils/vue-reserved.json'))
1112
const SCOPE_NAMES = new Set(['props', 'computed', 'data', 'methods'])
@@ -31,50 +32,60 @@ function create (context) {
3132
// Helpers
3233
// ----------------------------------------------------------------------
3334

34-
function checkUsedNames (name, node) {
35+
function checkUsedNames (name, node, groupName) {
3536
assert(typeof name === 'string')
3637

37-
if (reservedNames.has(name)) {
38+
if (groupName === 'data' && name.substr(0, 1) === '_') {
3839
context.report({
3940
node: node,
40-
message: 'Reserved key \'{{name}}\'.',
41+
message: "Field '{{name}}' which start with _ in data is reserved.",
4142
data: {
42-
name: name
43+
name
44+
}
45+
})
46+
} else if (reservedNames.has(name)) {
47+
context.report({
48+
node: node,
49+
message: "Reserved key '{{name}}'.",
50+
data: {
51+
name
4352
}
4453
})
4554
} else if (usedNames.indexOf(name) !== -1) {
4655
context.report({
4756
node: node,
48-
message: 'Duplicate key \'{{name}}\'.',
57+
message: "Duplicate key '{{name}}'.",
4958
data: {
50-
name: name
59+
name
5160
}
5261
})
5362
}
5463
usedNames.push(name)
5564
}
5665

57-
function checkArrayExpression (node) {
66+
function checkArrayExpression (node, groupName) {
5867
node.elements.forEach(item => {
59-
if (item.type === 'Literal') {
60-
checkUsedNames(item.value, item)
68+
const name = utils.getStaticPropertyName(item)
69+
if (name) {
70+
checkUsedNames(name, item, groupName)
6171
}
6272
})
6373
}
6474

65-
function checkObjectExpression (node) {
75+
function checkObjectExpression (node, groupName) {
6676
node.properties.forEach(item => {
67-
if (item.type === 'Property' && item.key.type === 'Identifier') {
68-
checkUsedNames(item.key.name, item.key)
77+
const name = utils.getStaticPropertyName(item)
78+
if (name) {
79+
checkUsedNames(name, item.key, groupName)
6980
}
7081
})
7182
}
7283

73-
function checkFunctionExpression (node) {
84+
function checkFunctionExpression (node, groupName) {
7485
if (node.body.type === 'BlockStatement') {
7586
node.body.body.forEach(item => {
7687
if (item.type === 'ReturnStatement' && item.argument.type === 'ObjectExpression') {
77-
checkObjectExpression(item.argument)
88+
checkObjectExpression(item.argument, groupName)
7889
}
7990
})
8091
}
@@ -89,11 +100,11 @@ function create (context) {
89100
.filter(p => p.type === 'Property' && p.key.type === 'Identifier' && scopeNames.has(p.key.name))
90101
.forEach(node => {
91102
if (node.value.type === 'ArrayExpression') {
92-
checkArrayExpression(node.value)
103+
checkArrayExpression(node.value, node.key.name)
93104
} else if (node.value.type === 'ObjectExpression') {
94-
checkObjectExpression(node.value)
105+
checkObjectExpression(node.value, node.key.name)
95106
} else if (node.value.type === 'FunctionExpression') {
96-
checkFunctionExpression(node.value)
107+
checkFunctionExpression(node.value, node.key.name)
97108
}
98109
})
99110
})

lib/utils/index.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,48 @@ 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+
case 'Literal':
291+
case 'TemplateLiteral':
292+
case 'Identifier':
293+
prop = node
294+
break
295+
// no default
296+
}
297+
298+
switch (prop && prop.type) {
299+
case 'Literal':
300+
return String(prop.value)
301+
case 'TemplateLiteral':
302+
if (prop.expressions.length === 0 && prop.quasis.length === 1) {
303+
return prop.quasis[0].value.cooked
304+
}
305+
break
306+
case 'Identifier':
307+
if (!node.computed) {
308+
return prop.name
309+
}
310+
break
311+
// no default
312+
}
313+
314+
return null
315+
},
316+
275317
/**
276318
* Get all computed properties by looking at all component's properties
277319
* @param {ObjectExpression} Object with component definition

0 commit comments

Comments
 (0)