Skip to content

Commit

Permalink
implements proposed max-props rule (#2430)
Browse files Browse the repository at this point in the history
Co-authored-by: Flo Edelmann <[email protected]>
  • Loading branch information
kevsommer and FloEdelmann authored Jul 13, 2024
1 parent f5cb93d commit 291c6f9
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ For example:
| [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | :bulb: | :hammer: |
| [vue/match-component-import-name](./match-component-import-name.md) | require the registered component name to match the imported component name | | :warning: |
| [vue/max-lines-per-block](./max-lines-per-block.md) | enforce maximum number of lines in Vue SFC blocks | | :warning: |
| [vue/max-props](./max-props.md) | enforce maximum number of props in Vue component | | :warning: |
| [vue/new-line-between-multi-line-property](./new-line-between-multi-line-property.md) | enforce new lines between multi-line properties in Vue components | :wrench: | :lipstick: |
| [vue/next-tick-style](./next-tick-style.md) | enforce Promise or callback style in `nextTick` | :wrench: | :hammer: |
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | | :hammer: |
Expand Down
62 changes: 62 additions & 0 deletions docs/rules/max-props.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/max-props
description: enforce maximum number of props in Vue component
---

# vue/max-props

> enforce maximum number of props in Vue component
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> _**This rule has not been released yet.**_ </badge>

## :book: Rule Details

This rule enforces a maximum number of props in a Vue SFC, in order to aid in maintainability and reduce complexity.

## :wrench: Options

This rule takes an object, where you can specify the maximum number of props allowed in a Vue SFC.
There is one property that can be specified for the object.

- `maxProps` ... Specify the maximum number of props in the `script` block.

### `{ maxProps: 1 }`

<eslint-code-block :rules="{'vue/max-props': ['error', { maxProps: 1 }]}">

```vue
<!-- ✗ BAD -->
<template>
</template>
<script setup>
defineProps({
prop1: String,
prop2: String,
})
</script>
```

</eslint-code-block>

### `{ maxProps: 5 }`

<eslint-code-block :rules="{'vue/max-props': ['error', { maxProps: 5 }]}">

```vue
<!-- ✓ GOOD -->
<script>
defineProps({
prop1: String
})
</script>
```

</eslint-code-block>

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/max-props.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/max-props.js)
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ const plugin = {
'max-attributes-per-line': require('./rules/max-attributes-per-line'),
'max-len': require('./rules/max-len'),
'max-lines-per-block': require('./rules/max-lines-per-block'),
'max-props': require('./rules/max-props'),
'multi-word-component-names': require('./rules/multi-word-component-names'),
'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'),
'multiline-ternary': require('./rules/multiline-ternary'),
Expand Down
67 changes: 67 additions & 0 deletions lib/rules/max-props.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @author kevsommer Kevin Sommer
* See LICENSE file in root directory for full license.
*/
'use strict'
const utils = require('../utils')

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce maximum number of props in Vue component',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/max-props.html'
},
fixable: null,
schema: [
{
type: 'object',
properties: {
maxProps: {
type: 'integer',
minimum: 1
}
},
additionalProperties: false,
minProperties: 1
}
],
messages: {
tooManyProps:
'Component has too many props ({{propCount}}). Maximum allowed is {{limit}}.'
}
},
/** @param {RuleContext} context */
create(context) {
/** @type {Record<string, number>} */
const option = context.options[0] || {}

/**
* @param {import('../utils').ComponentProp[]} props
*/
function checkMaxNumberOfProps(props) {
if (props.length > option.maxProps && props[0].node) {
context.report({
node: props[0].node,
messageId: 'tooManyProps',
data: {
propCount: props.length,
limit: option.maxProps
}
})
}
}

return utils.compositingVisitors(
utils.executeOnVue(context, (obj) => {
checkMaxNumberOfProps(utils.getComponentPropsFromOptions(obj))
}),
utils.defineScriptSetupVisitor(context, {
onDefinePropsEnter(_, props) {
checkMaxNumberOfProps(props)
}
})
)
}
}
162 changes: 162 additions & 0 deletions tests/lib/rules/max-props.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* @author kevsommer Kevin Sommer
* See LICENSE file in root directory for full license.
*/
'use strict'

const RuleTester = require('../../eslint-compat').RuleTester
const rule = require('../../../lib/rules/max-props')

const tester = new RuleTester({
languageOptions: {
parser: require('vue-eslint-parser'),
ecmaVersion: 2020,
sourceType: 'module'
}
})

tester.run('max-props', rule, {
valid: [
{
filename: 'test.vue',
code: `
<script setup>
defineProps({ prop1: '', prop2: '' })
</script>
`,
options: [{ maxProps: 5 }]
},
{
filename: 'test.vue',
code: `
<script>
export default {
props: {
prop1: String,
prop2: String
}
}
</script>
`,
options: [{ maxProps: 5 }]
},
{
filename: 'test.vue',
code: `
<script>
export default {
props: {
prop1: String,
prop2: String,
prop3: String,
prop4: String,
prop5: String
}
}
</script>
`,
options: [{ maxProps: 5 }]
},
{
filename: 'test.vue',
code: `
<script>
export default {
props: {}
}
</script>
`,
options: [{ maxProps: 5 }]
},
{
filename: 'test.vue',
code: `
<script setup>
defineProps({})
</script>
`,
options: [{ maxProps: 5 }]
},
{
filename: 'test.vue',
code: `
<script>
</script>
`,
options: [{ maxProps: 5 }]
},
{
filename: 'test.vue',
code: `
<script setup lang="ts">
defineProps<{ prop1: string, prop2: string }>();
</script>
`,
options: [{ maxProps: 5 }],
languageOptions: {
parser: require('vue-eslint-parser'),
parserOptions: {
parser: require.resolve('@typescript-eslint/parser')
}
}
}
],
invalid: [
{
filename: 'test.vue',
code: `
<script setup>
defineProps({ prop1: '', prop2: '' })
</script>
`,
options: [{ maxProps: 1 }],
errors: [
{
message: 'Component has too many props (2). Maximum allowed is 1.',
line: 3
}
]
},
{
filename: 'test.vue',
code: `
<script>
export default {
props: {
prop1: String,
prop2: String
}
}
</script>
`,
options: [{ maxProps: 1 }],
errors: [
{
message: 'Component has too many props (2). Maximum allowed is 1.',
line: 5
}
]
},
{
filename: 'test.vue',
code: `
<script setup lang="ts">
defineProps<{ prop1: string, prop2: string, prop3: string }>();
</script>
`,
options: [{ maxProps: 2 }],
languageOptions: {
parser: require('vue-eslint-parser'),
parserOptions: {
parser: require.resolve('@typescript-eslint/parser')
}
},
errors: [
{
message: 'Component has too many props (3). Maximum allowed is 2.',
line: 3
}
]
}
]
})

0 comments on commit 291c6f9

Please sign in to comment.