Skip to content

Commit

Permalink
Add rules: no-dupe-keys and no-reserved-keys. (#88)
Browse files Browse the repository at this point in the history
fixes #86
  • Loading branch information
armano2 authored and michalsnik committed Aug 1, 2017
1 parent e42491f commit 39c9df5
Show file tree
Hide file tree
Showing 9 changed files with 607 additions and 3 deletions.
78 changes: 78 additions & 0 deletions docs/rules/no-dupe-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Prevents duplication of field names (no-dupe-keys)

This rule prevents to use duplicated names.

## :book: Rule Details

This rule is aimed at preventing duplicated property names.

:-1: Examples of **incorrect** code for this rule:

```js
export default {
props: {
foo: String
},
computed: {
foo: {
get () {
}
}
},
data: {
foo: null
},
methods: {
foo () {
}
}
}
```

:+1: Examples of **correct** code for this rule:

```js
export default {
props: ['foo'],
computed: {
bar () {
}
},
data () {
return {
dat: null
}
},
methods: {
test () {
}
}
}
```

## :wrench: Options

This rule has an object option:

`"groups"`: [] (default) array of additional groups to search for duplicates.

### Example:

```
vue/no-dupe-keys: [2, {
groups: ['asyncComputed']
}]
```

:-1: Examples of **incorrect** code for this configuration

```js
export default {
computed: {
foo () {}
},
asyncComputed: {
foo () {}
}
}
```
54 changes: 54 additions & 0 deletions docs/rules/no-reservered-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Prevent overwrite reserved keys (no-reservered-keys)

This rule prevents to use reserved names from to avoid conflicts and unexpected behavior.

## Rule Details

:-1: Examples of **incorrect** code for this rule:

```js
export default {
props: {
$el: String
},
computed: {
$on: {
get () {
}
}
},
data: {
_foo: null
},
methods: {
$nextTick () {
}
}
}
```

## :wrench: Options

This rule has an object option:

`"reserved"`: [] (default) array of dissalowed names inside `groups`.

`"groups"`: [] (default) array of additional groups to search for duplicates.

### Example:

```
vue/no-dupe-keys: [2, {
reserved: ['foo']
}]
```

:-1: Examples of **incorrect** code for this configuration

```js
export default {
computed: {
foo () {}
}
}
```
68 changes: 68 additions & 0 deletions lib/rules/no-dupe-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @fileoverview Prevents duplication of field names.
* @author Armano
*/
'use strict'

const utils = require('../utils')

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

const GROUP_NAMES = ['props', 'computed', 'data', 'methods']

function create (context) {
const usedNames = []

const options = context.options[0] || {}
const groups = new Set(GROUP_NAMES.concat(options.groups || []))

// ----------------------------------------------------------------------
// Public
// ----------------------------------------------------------------------

return utils.executeOnVue(context, (obj) => {
const properties = utils.iterateProperties(obj, groups)
for (const o of properties) {
if (usedNames.indexOf(o.name) !== -1) {
context.report({
node: o.node,
message: "Duplicated key '{{name}}'.",
data: {
name: o.name
}
})
}
usedNames.push(o.name)
}
})
}

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: 'Prevents duplication of field names.',
category: 'Possible Errors',
recommended: false
},
fixable: null, // or "code" or "whitespace"
schema: [
{
type: 'object',
properties: {
groups: {
type: 'array'
}
},
additionalProperties: false
}
]
},

create
}
74 changes: 74 additions & 0 deletions lib/rules/no-reservered-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* @fileoverview Prevent overwrite reserved keys
* @author Armano
*/
'use strict'

const utils = require('../utils')

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

const RESERVED_KEYS = require('../utils/vue-reserved.json')
const GROUP_NAMES = ['props', 'computed', 'data', 'methods']

function create (context) {
const options = context.options[0] || {}
const reservedKeys = new Set(RESERVED_KEYS.concat(options.reserved || []))
const groups = new Set(GROUP_NAMES.concat(options.groups || []))

// ----------------------------------------------------------------------
// Public
// ----------------------------------------------------------------------

return utils.executeOnVue(context, (obj) => {
const properties = utils.iterateProperties(obj, groups)
for (const o of properties) {
if (o.groupName === 'data' && o.name[0] === '_') {
context.report({
node: o.node,
message: "Keys starting with with '_' are reserved in '{{name}}' group.",
data: {
name: o.name
}
})
} else if (reservedKeys.has(o.name)) {
context.report({
node: o.node,
message: "Key '{{name}}' is reserved.",
data: {
name: o.name
}
})
}
}
})
}

module.exports = {
meta: {
docs: {
description: 'Prevent overwrite reserved keys.',
category: 'Possible Errors',
recommended: false
},
fixable: null, // or "code" or "whitespace"
schema: [
{
type: 'object',
properties: {
reserved: {
type: 'array'
},
groups: {
type: 'array'
}
},
additionalProperties: false
}
]
},

create
}
67 changes: 67 additions & 0 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -453,5 +453,72 @@ module.exports = {
cb(node.arguments.slice(-1)[0])
}
}
},

/**
* Return generator with all properties
* @param {ASTNode} node Node to check
* @param {string} groupName Name of parent group
*/
* iterateProperties (node, groups) {
const nodes = node.properties.filter(p => p.type === 'Property' && groups.has(this.getStaticPropertyName(p.key)))
for (const item of nodes) {
const name = this.getStaticPropertyName(item.key)
if (item.value.type === 'ArrayExpression') {
yield * this.iterateArrayExpression(item.value, name)
} else if (item.value.type === 'ObjectExpression') {
yield * this.iterateObjectExpression(item.value, name)
} else if (item.value.type === 'FunctionExpression') {
yield * this.iterateFunctionExpression(item.value, name)
}
}
},

/**
* Return generator with all elements inside ArrayExpression
* @param {ASTNode} node Node to check
* @param {string} groupName Name of parent group
*/
* iterateArrayExpression (node, groupName) {
assert(node.type === 'ArrayExpression')
for (const item of node.elements) {
const name = this.getStaticPropertyName(item)
if (name) {
const obj = { name, groupName, node: item }
yield obj
}
}
},

/**
* Return generator with all elements inside ObjectExpression
* @param {ASTNode} node Node to check
* @param {string} groupName Name of parent group
*/
* iterateObjectExpression (node, groupName) {
assert(node.type === 'ObjectExpression')
for (const item of node.properties) {
const name = this.getStaticPropertyName(item)
if (name) {
const obj = { name, groupName, node: item.key }
yield obj
}
}
},

/**
* Return generator with all elements inside FunctionExpression
* @param {ASTNode} node Node to check
* @param {string} groupName Name of parent group
*/
* iterateFunctionExpression (node, groupName) {
assert(node.type === 'FunctionExpression')
if (node.body.type === 'BlockStatement') {
for (const item of node.body.body) {
if (item.type === 'ReturnStatement' && item.argument.type === 'ObjectExpression') {
yield * this.iterateObjectExpression(item.argument, groupName)
}
}
}
}
}
4 changes: 4 additions & 0 deletions lib/utils/vue-reserved.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[
"$data", "$props", "$el", "$options", "$parent", "$root", "$children", "$slots", "$scopedSlots", "$refs", "$isServer", "$attrs", "$listeners",
"$watch", "$set", "$delete", "$on", "$once", "$off", "$emit", "$mount", "$forceUpdate", "$nextTick", "$destroy"
]
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 39c9df5

Please sign in to comment.