Skip to content

Commit

Permalink
Add vue/no-reserved-props rule (#1678)
Browse files Browse the repository at this point in the history
* Add  `vue/no-reserved-props` rule

* add 'class' and 'style'
  • Loading branch information
ota-meshi committed Oct 29, 2021
1 parent afec265 commit 0aeaaaf
Show file tree
Hide file tree
Showing 10 changed files with 576 additions and 7 deletions.
2 changes: 2 additions & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| [vue/no-parsing-error](./no-parsing-error.md) | disallow parsing errors in `<template>` | |
| [vue/no-ref-as-operand](./no-ref-as-operand.md) | disallow use of value wrapped by `ref()` (Composition API) as an operand | :wrench: |
| [vue/no-reserved-keys](./no-reserved-keys.md) | disallow overwriting reserved keys | |
| [vue/no-reserved-props](./no-reserved-props.md) | disallow reserved names in props | |
| [vue/no-setup-props-destructure](./no-setup-props-destructure.md) | disallow destructuring of `props` passed to `setup` | |
| [vue/no-shared-component-data](./no-shared-component-data.md) | enforce component's data property to be a function | :wrench: |
| [vue/no-side-effects-in-computed-properties](./no-side-effects-in-computed-properties.md) | disallow side effects in computed properties | |
Expand Down Expand Up @@ -195,6 +196,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| [vue/no-mutating-props](./no-mutating-props.md) | disallow mutation of component props | |
| [vue/no-parsing-error](./no-parsing-error.md) | disallow parsing errors in `<template>` | |
| [vue/no-reserved-keys](./no-reserved-keys.md) | disallow overwriting reserved keys | |
| [vue/no-reserved-props](./no-reserved-props.md) | disallow reserved names in props | |
| [vue/no-shared-component-data](./no-shared-component-data.md) | enforce component's data property to be a function | :wrench: |
| [vue/no-side-effects-in-computed-properties](./no-side-effects-in-computed-properties.md) | disallow side effects in computed properties | |
| [vue/no-template-key](./no-template-key.md) | disallow `key` attribute on `<template>` | |
Expand Down
52 changes: 52 additions & 0 deletions docs/rules/no-reserved-props.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-reserved-props
description: disallow reserved names in props
---
# vue/no-reserved-props

> disallow reserved names in props
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/essential"`, `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`.

## :book: Rule Details

This rule disallow reserved names to be used in props.

<eslint-code-block :rules="{'vue/no-reserved-props': ['error']}">

```vue
<script>
export default {
props: {
/* ✗ BAD */
ref: String,
key: String,
/* ✓ GOOD */
foo: String,
bar: String,
}
}
</script>
```

</eslint-code-block>

## :wrench: Options

```json
{
"vue/no-reserved-props": ["error", {
"vueVersion": 3, // or 2
}]
}
```

- `vueVersion` (`2 | 3`) ... Specify the version of Vue you are using. Default is `3`.

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-reserved-props.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-reserved-props.js)
6 changes: 6 additions & 0 deletions lib/configs/essential.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ module.exports = {
'vue/no-mutating-props': 'error',
'vue/no-parsing-error': 'error',
'vue/no-reserved-keys': 'error',
'vue/no-reserved-props': [
'error',
{
vueVersion: 2
}
],
'vue/no-shared-component-data': 'error',
'vue/no-side-effects-in-computed-properties': 'error',
'vue/no-template-key': 'error',
Expand Down
1 change: 1 addition & 0 deletions lib/configs/vue3-essential.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ module.exports = {
'vue/no-parsing-error': 'error',
'vue/no-ref-as-operand': 'error',
'vue/no-reserved-keys': 'error',
'vue/no-reserved-props': 'error',
'vue/no-setup-props-destructure': 'error',
'vue/no-shared-component-data': 'error',
'vue/no-side-effects-in-computed-properties': 'error',
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ module.exports = {
'no-ref-as-operand': require('./rules/no-ref-as-operand'),
'no-reserved-component-names': require('./rules/no-reserved-component-names'),
'no-reserved-keys': require('./rules/no-reserved-keys'),
'no-reserved-props': require('./rules/no-reserved-props'),
'no-restricted-block': require('./rules/no-restricted-block'),
'no-restricted-call-after-await': require('./rules/no-restricted-call-after-await'),
'no-restricted-class': require('./rules/no-restricted-class'),
Expand Down
97 changes: 97 additions & 0 deletions lib/rules/no-reserved-props.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

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

/**
* @typedef {import('../utils').ComponentArrayProp} ComponentArrayProp
* @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
* @typedef {import('../utils').ComponentTypeProp} ComponentTypeProp
*/

// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------

const RESERVED = {
3: ['key', 'ref'],
2: ['key', 'ref', 'is', 'slot', 'slot-scope', 'slotScope', 'class', 'style']
}

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

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'disallow reserved names in props',
categories: ['vue3-essential', 'essential'],
url: 'https://eslint.vuejs.org/rules/no-reserved-props.html',
defaultOptions: {
vue2: [{ vueVersion: 2 }]
}
},
fixable: null,
schema: [
{
type: 'object',
properties: {
vueVersion: {
enum: [2, 3]
}
},
additionalProperties: false
}
],
messages: {
reserved:
"'{{propName}}' is a reserved attribute and cannot be used as props."
}
},
/** @param {RuleContext} context */
create(context) {
const options = context.options[0] || {}
/** @type {2|3} */
const vueVersion = options.vueVersion || 3

const reserved = new Set(RESERVED[vueVersion])

/**
* @param {(ComponentArrayProp | ComponentObjectProp | ComponentTypeProp)[]} props
*/
function processProps(props) {
for (const prop of props) {
if (prop.propName != null && reserved.has(prop.propName)) {
context.report({
node: prop.node,
messageId: `reserved`,
data: {
propName: casing.kebabCase(prop.propName)
}
})
}
}
}

return utils.compositingVisitors(
utils.defineScriptSetupVisitor(context, {
onDefinePropsEnter(_node, props) {
processProps(props)
}
}),
utils.executeOnVue(context, (obj) => {
processProps(utils.getComponentProps(obj))
})
)
}
}
6 changes: 3 additions & 3 deletions lib/utils/ts-ast-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ function* extractRuntimeProps(context, node) {
for (const m of members) {
if (
(m.type === 'TSPropertySignature' || m.type === 'TSMethodSignature') &&
m.key.type === 'Identifier'
(m.key.type === 'Identifier' || m.key.type === 'Literal')
) {
let type
if (m.type === 'TSMethodSignature') {
Expand All @@ -132,8 +132,8 @@ function* extractRuntimeProps(context, node) {
}
yield {
type: 'type',
key: /** @type {Identifier} */ (m.key),
propName: m.key.name,
key: /** @type {Identifier | Literal} */ (m.key),
propName: m.key.type === 'Identifier' ? m.key.name : `${m.key.value}`,
value: null,
node: /** @type {TSPropertySignature | TSMethodSignature} */ (m),

Expand Down

0 comments on commit 0aeaaaf

Please sign in to comment.