Skip to content

Commit a4aed0a

Browse files
authored
Implements proposed max-template-depth rule (#2525)
1 parent 6ebd607 commit a4aed0a

File tree

5 files changed

+319
-0
lines changed

5 files changed

+319
-0
lines changed

docs/rules/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ For example:
224224
| [vue/match-component-import-name](./match-component-import-name.md) | require the registered component name to match the imported component name | | :warning: |
225225
| [vue/max-lines-per-block](./max-lines-per-block.md) | enforce maximum number of lines in Vue SFC blocks | | :warning: |
226226
| [vue/max-props](./max-props.md) | enforce maximum number of props in Vue component | | :warning: |
227+
| [vue/max-template-depth](./max-template-depth.md) | enforce maximum depth of template | | :warning: |
227228
| [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: |
228229
| [vue/next-tick-style](./next-tick-style.md) | enforce Promise or callback style in `nextTick` | :wrench: | :hammer: |
229230
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | | :hammer: |

docs/rules/max-template-depth.md

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/max-template-depth
5+
description: enforce maximum depth of template
6+
---
7+
8+
# vue/max-template-depth
9+
10+
> enforce maximum depth of template
11+
12+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> _**This rule has not been released yet.**_ </badge>
13+
14+
## :book: Rule Details
15+
16+
This rule enforces a maximum depth of the template in a Vue SFC, in order to aid in maintainability and reduce complexity.
17+
18+
## :wrench: Options
19+
20+
This rule takes an object, where you can specify the maximum depth allowed in a Vue SFC template block.
21+
There is one property that can be specified for the object.
22+
23+
- `maxDepth` ... Specify the maximum template depth `template` block.
24+
25+
### `{ maxDepth: 3 }`
26+
27+
<eslint-code-block :rules="{'vue/max-template-depth': ['error', { maxDepth: 3 }]}">
28+
29+
```vue
30+
<!-- ✗ BAD -->
31+
<template>
32+
<main-container>
33+
<child-component>
34+
<div />
35+
</child-component>
36+
<child-component>
37+
<nested-component>
38+
<div>
39+
<div />
40+
</div>
41+
</nested-component>
42+
</child-component>
43+
</main-container>
44+
</template>
45+
```
46+
47+
</eslint-code-block>
48+
49+
<eslint-code-block :rules="{'vue/max-template-depth': ['error', { maxDepth: 3 }]}">
50+
51+
```vue
52+
<!-- ✓ GOOD -->
53+
<template>
54+
<main-container>
55+
<child-component>
56+
<div />
57+
</child-component>
58+
</main-container>
59+
</template>
60+
```
61+
62+
</eslint-code-block>
63+
64+
## :mag: Implementation
65+
66+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/max-template-depth.js)
67+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/max-template-depth.js)

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const plugin = {
8080
'max-len': require('./rules/max-len'),
8181
'max-lines-per-block': require('./rules/max-lines-per-block'),
8282
'max-props': require('./rules/max-props'),
83+
'max-template-depth': require('./rules/max-template-depth'),
8384
'multi-word-component-names': require('./rules/multi-word-component-names'),
8485
'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'),
8586
'multiline-ternary': require('./rules/multiline-ternary'),

lib/rules/max-template-depth.js

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* @author kevsommer Kevin Sommer
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
module.exports = {
8+
meta: {
9+
type: 'problem',
10+
docs: {
11+
description: 'enforce maximum depth of template',
12+
categories: undefined,
13+
url: 'https://eslint.vuejs.org/rules/max-template-depth.html'
14+
},
15+
fixable: null,
16+
schema: [
17+
{
18+
type: 'object',
19+
properties: {
20+
maxDepth: {
21+
type: 'integer',
22+
minimum: 1
23+
}
24+
},
25+
additionalProperties: false,
26+
minProperties: 1
27+
}
28+
],
29+
messages: {
30+
templateTooDeep:
31+
'Element is nested too deeply (depth of {{depth}}, maximum allowed is {{limit}}).'
32+
}
33+
},
34+
/** @param {RuleContext} context */
35+
create(context) {
36+
const option = context.options[0] || {}
37+
38+
/**
39+
* @param {VElement} element
40+
* @param {number} curDepth
41+
*/
42+
function checkMaxDepth(element, curDepth) {
43+
if (curDepth > option.maxDepth) {
44+
context.report({
45+
node: element,
46+
messageId: 'templateTooDeep',
47+
data: {
48+
depth: curDepth,
49+
limit: option.maxDepth
50+
}
51+
})
52+
}
53+
54+
if (!element.children) {
55+
return
56+
}
57+
58+
for (const child of element.children) {
59+
if (child.type === 'VElement') {
60+
checkMaxDepth(child, curDepth + 1)
61+
}
62+
}
63+
}
64+
65+
return {
66+
/** @param {Program} program */
67+
Program(program) {
68+
const element = program.templateBody
69+
70+
if (element == null) {
71+
return
72+
}
73+
74+
if (element.type !== 'VElement') {
75+
return
76+
}
77+
78+
checkMaxDepth(element, 0)
79+
}
80+
}
81+
}
82+
}

tests/lib/rules/max-template-depth.js

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/**
2+
* @author kevsommer Kevin Sommer
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const RuleTester = require('../../eslint-compat').RuleTester
8+
const rule = require('../../../lib/rules/max-template-depth')
9+
10+
const tester = new RuleTester({
11+
languageOptions: {
12+
parser: require('vue-eslint-parser'),
13+
ecmaVersion: 2020,
14+
sourceType: 'module'
15+
}
16+
})
17+
18+
tester.run('max-template-depth', rule, {
19+
valid: [
20+
{
21+
filename: 'test.vue',
22+
code: `
23+
<template>
24+
<main-container>
25+
<child-component>
26+
<div />
27+
</child-component>
28+
</main-container>
29+
</template>
30+
`,
31+
options: [{ maxDepth: 3 }]
32+
},
33+
{
34+
filename: 'test.vue',
35+
code: `
36+
<template>
37+
<main-container>
38+
<ul>
39+
<li>Item 1</li>
40+
</ul>
41+
</main-container>
42+
</template>
43+
`,
44+
options: [{ maxDepth: 4 }]
45+
},
46+
{
47+
filename: 'test.vue',
48+
code: `
49+
<template>
50+
<main-container>
51+
<child-component>
52+
<sub-child-component>
53+
<div />
54+
</sub-child-component>
55+
</child-component>
56+
</main-container>
57+
</template>
58+
`,
59+
options: [{ maxDepth: 4 }]
60+
},
61+
{
62+
filename: 'test.vue',
63+
code: `
64+
<template>
65+
<div>
66+
</div>
67+
<main-container>
68+
<child-component>
69+
<ul>
70+
<li />
71+
</ul>
72+
</child-component>
73+
</main-container>
74+
</template>
75+
`,
76+
options: [{ maxDepth: 4 }]
77+
}
78+
],
79+
invalid: [
80+
{
81+
filename: 'test.vue',
82+
code: `
83+
<template>
84+
<div>
85+
<div>
86+
<div>
87+
<div />
88+
</div>
89+
</div>
90+
</div>
91+
</template>
92+
`,
93+
options: [{ maxDepth: 3 }],
94+
errors: [
95+
{
96+
message:
97+
'Element is nested too deeply (depth of 4, maximum allowed is 3).',
98+
line: 6
99+
}
100+
]
101+
},
102+
{
103+
filename: 'test.vue',
104+
code: `
105+
<template>
106+
<side-container>
107+
<child-component />
108+
</side-container>
109+
<main-container>
110+
<child-component>
111+
<nested-component>
112+
<h1 />
113+
</nested-component>
114+
</child-component>
115+
</main-container>
116+
</template>
117+
`,
118+
options: [{ maxDepth: 3 }],
119+
errors: [
120+
{
121+
message:
122+
'Element is nested too deeply (depth of 4, maximum allowed is 3).',
123+
line: 9
124+
}
125+
]
126+
},
127+
{
128+
filename: 'test.vue',
129+
code: `
130+
<template>
131+
<main-container>
132+
<nav-bar>
133+
<div />
134+
</nav-bar>
135+
<child-component>
136+
<nested-component>
137+
<div>
138+
<div />
139+
<div>
140+
</nested-component>
141+
</child-component>
142+
</main-container>
143+
</template>
144+
`,
145+
options: [{ maxDepth: 3 }],
146+
errors: [
147+
{
148+
message:
149+
'Element is nested too deeply (depth of 4, maximum allowed is 3).',
150+
line: 9,
151+
endLine: 12
152+
},
153+
{
154+
message:
155+
'Element is nested too deeply (depth of 5, maximum allowed is 3).',
156+
line: 10,
157+
endLine: 10
158+
},
159+
{
160+
message:
161+
'Element is nested too deeply (depth of 5, maximum allowed is 3).',
162+
line: 11,
163+
endLine: 12
164+
}
165+
]
166+
}
167+
]
168+
})

0 commit comments

Comments
 (0)