Skip to content

Commit

Permalink
Implements proposed max-template-depth rule (#2525)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevsommer authored Sep 2, 2024
1 parent 6ebd607 commit a4aed0a
Show file tree
Hide file tree
Showing 5 changed files with 319 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 @@ -224,6 +224,7 @@ For example:
| [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/max-template-depth](./max-template-depth.md) | enforce maximum depth of template | | :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
67 changes: 67 additions & 0 deletions docs/rules/max-template-depth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/max-template-depth
description: enforce maximum depth of template
---

# vue/max-template-depth

> enforce maximum depth of template
- :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 depth of the template 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 depth allowed in a Vue SFC template block.
There is one property that can be specified for the object.

- `maxDepth` ... Specify the maximum template depth `template` block.

### `{ maxDepth: 3 }`

<eslint-code-block :rules="{'vue/max-template-depth': ['error', { maxDepth: 3 }]}">

```vue
<!-- ✗ BAD -->
<template>
<main-container>
<child-component>
<div />
</child-component>
<child-component>
<nested-component>
<div>
<div />
</div>
</nested-component>
</child-component>
</main-container>
</template>
```

</eslint-code-block>

<eslint-code-block :rules="{'vue/max-template-depth': ['error', { maxDepth: 3 }]}">

```vue
<!-- ✓ GOOD -->
<template>
<main-container>
<child-component>
<div />
</child-component>
</main-container>
</template>
```

</eslint-code-block>

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/max-template-depth.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/max-template-depth.js)
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const plugin = {
'max-len': require('./rules/max-len'),
'max-lines-per-block': require('./rules/max-lines-per-block'),
'max-props': require('./rules/max-props'),
'max-template-depth': require('./rules/max-template-depth'),
'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
82 changes: 82 additions & 0 deletions lib/rules/max-template-depth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* @author kevsommer Kevin Sommer
* See LICENSE file in root directory for full license.
*/
'use strict'

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce maximum depth of template',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/max-template-depth.html'
},
fixable: null,
schema: [
{
type: 'object',
properties: {
maxDepth: {
type: 'integer',
minimum: 1
}
},
additionalProperties: false,
minProperties: 1
}
],
messages: {
templateTooDeep:
'Element is nested too deeply (depth of {{depth}}, maximum allowed is {{limit}}).'
}
},
/** @param {RuleContext} context */
create(context) {
const option = context.options[0] || {}

/**
* @param {VElement} element
* @param {number} curDepth
*/
function checkMaxDepth(element, curDepth) {
if (curDepth > option.maxDepth) {
context.report({
node: element,
messageId: 'templateTooDeep',
data: {
depth: curDepth,
limit: option.maxDepth
}
})
}

if (!element.children) {
return
}

for (const child of element.children) {
if (child.type === 'VElement') {
checkMaxDepth(child, curDepth + 1)
}
}
}

return {
/** @param {Program} program */
Program(program) {
const element = program.templateBody

if (element == null) {
return
}

if (element.type !== 'VElement') {
return
}

checkMaxDepth(element, 0)
}
}
}
}
168 changes: 168 additions & 0 deletions tests/lib/rules/max-template-depth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/**
* @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-template-depth')

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

tester.run('max-template-depth', rule, {
valid: [
{
filename: 'test.vue',
code: `
<template>
<main-container>
<child-component>
<div />
</child-component>
</main-container>
</template>
`,
options: [{ maxDepth: 3 }]
},
{
filename: 'test.vue',
code: `
<template>
<main-container>
<ul>
<li>Item 1</li>
</ul>
</main-container>
</template>
`,
options: [{ maxDepth: 4 }]
},
{
filename: 'test.vue',
code: `
<template>
<main-container>
<child-component>
<sub-child-component>
<div />
</sub-child-component>
</child-component>
</main-container>
</template>
`,
options: [{ maxDepth: 4 }]
},
{
filename: 'test.vue',
code: `
<template>
<div>
</div>
<main-container>
<child-component>
<ul>
<li />
</ul>
</child-component>
</main-container>
</template>
`,
options: [{ maxDepth: 4 }]
}
],
invalid: [
{
filename: 'test.vue',
code: `
<template>
<div>
<div>
<div>
<div />
</div>
</div>
</div>
</template>
`,
options: [{ maxDepth: 3 }],
errors: [
{
message:
'Element is nested too deeply (depth of 4, maximum allowed is 3).',
line: 6
}
]
},
{
filename: 'test.vue',
code: `
<template>
<side-container>
<child-component />
</side-container>
<main-container>
<child-component>
<nested-component>
<h1 />
</nested-component>
</child-component>
</main-container>
</template>
`,
options: [{ maxDepth: 3 }],
errors: [
{
message:
'Element is nested too deeply (depth of 4, maximum allowed is 3).',
line: 9
}
]
},
{
filename: 'test.vue',
code: `
<template>
<main-container>
<nav-bar>
<div />
</nav-bar>
<child-component>
<nested-component>
<div>
<div />
<div>
</nested-component>
</child-component>
</main-container>
</template>
`,
options: [{ maxDepth: 3 }],
errors: [
{
message:
'Element is nested too deeply (depth of 4, maximum allowed is 3).',
line: 9,
endLine: 12
},
{
message:
'Element is nested too deeply (depth of 5, maximum allowed is 3).',
line: 10,
endLine: 10
},
{
message:
'Element is nested too deeply (depth of 5, maximum allowed is 3).',
line: 11,
endLine: 12
}
]
}
]
})

0 comments on commit a4aed0a

Please sign in to comment.