Skip to content

Commit 25a2db5

Browse files
lsdsjyFloEdelmann
andauthored
Add vue/max-lines-per-block rule (#2213)
Co-authored-by: Flo Edelmann <[email protected]>
1 parent e643d44 commit 25a2db5

File tree

3 files changed

+248
-0
lines changed

3 files changed

+248
-0
lines changed

docs/rules/max-lines-per-block.md

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/max-lines-per-block
5+
description: enforce maximum number of lines in Vue SFC blocks
6+
---
7+
# vue/max-lines-per-block
8+
9+
> enforce maximum number of lines in Vue SFC blocks
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
13+
## :book: Rule Details
14+
15+
This rule enforces a maximum number of lines per block, in order to aid in maintainability and reduce complexity.
16+
17+
## :wrench: Options
18+
19+
This rule takes an object, where you can specify the maximum number of lines in each type of SFC block and customize the line counting behavior.
20+
The following properties can be specified for the object.
21+
22+
- `script` ... Specify the maximum number of lines in `<script>` block. Won't enforce limitation if not specified.
23+
- `template` ... Specify the maximum number of lines in `<template>` block. Won't enforce limitation if not specified.
24+
- `style` ... Specify the maximum number of lines in `<style>` block. Won't enforce limitation if not specified.
25+
- `skipBlankLines` ... Ignore lines made up purely of whitespaces.
26+
27+
### `{ template: 2 }`
28+
29+
<eslint-code-block :rules="{'vue/max-lines-per-block': ['error', { template: 2 }]}">
30+
31+
```vue
32+
<!-- ✗ BAD -->
33+
<template>
34+
<div>
35+
hi
36+
</div>
37+
</template>
38+
```
39+
40+
</eslint-code-block>
41+
42+
### `{ script: 1, skipBlankLines: true }`
43+
44+
<eslint-code-block :rules="{'vue/max-lines-per-block': ['error', { script: 1, skipBlankLines: true }]}">
45+
46+
```vue
47+
<!-- ✓ GOOD -->
48+
<script>
49+
50+
console.log(1)
51+
</script>
52+
```
53+
54+
</eslint-code-block>

lib/rules/max-lines-per-block.js

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* @author lsdsjy
3+
* @fileoverview Rule for checking the maximum number of lines in Vue SFC blocks.
4+
*/
5+
'use strict'
6+
7+
const { SourceCode } = require('eslint')
8+
const utils = require('../utils')
9+
10+
/**
11+
* @param {string} text
12+
*/
13+
function isEmptyLine(text) {
14+
return !text.trim()
15+
}
16+
17+
module.exports = {
18+
meta: {
19+
type: 'problem',
20+
docs: {
21+
description: 'enforce maximum number of lines in Vue SFC blocks',
22+
categories: undefined,
23+
url: 'https://eslint.vuejs.org/rules/max-lines-per-block.html'
24+
},
25+
fixable: null,
26+
schema: [
27+
{
28+
type: 'object',
29+
properties: {
30+
style: {
31+
type: 'integer',
32+
minimum: 1
33+
},
34+
template: {
35+
type: 'integer',
36+
minimum: 1
37+
},
38+
script: {
39+
type: 'integer',
40+
minimum: 1
41+
},
42+
skipBlankLines: {
43+
type: 'boolean',
44+
minimum: 0
45+
}
46+
},
47+
additionalProperties: false
48+
}
49+
],
50+
messages: {
51+
tooManyLines:
52+
'Block has too many lines ({{lineCount}}). Maximum allowed is {{limit}}.'
53+
}
54+
},
55+
/** @param {RuleContext} context */
56+
create(context) {
57+
const option = context.options[0] || {}
58+
/**
59+
* @type {Record<string, number>}
60+
*/
61+
const limits = {
62+
template: option.template,
63+
script: option.script,
64+
style: option.style
65+
}
66+
67+
const code = context.getSourceCode()
68+
const documentFragment =
69+
context.parserServices.getDocumentFragment &&
70+
context.parserServices.getDocumentFragment()
71+
72+
function getTopLevelHTMLElements() {
73+
if (documentFragment) {
74+
return documentFragment.children.filter(utils.isVElement)
75+
}
76+
return []
77+
}
78+
79+
return {
80+
/** @param {Program} node */
81+
Program(node) {
82+
if (utils.hasInvalidEOF(node)) {
83+
return
84+
}
85+
for (const block of getTopLevelHTMLElements()) {
86+
if (limits[block.name]) {
87+
// We suppose the start tag and end tag occupy one single line respectively
88+
let lineCount = block.loc.end.line - block.loc.start.line - 1
89+
90+
if (option.skipBlankLines) {
91+
const lines = SourceCode.splitLines(code.getText(block))
92+
lineCount -= lines.filter(isEmptyLine).length
93+
}
94+
95+
if (lineCount > limits[block.name]) {
96+
context.report({
97+
node: block,
98+
messageId: 'tooManyLines',
99+
data: {
100+
limit: limits[block.name],
101+
lineCount
102+
}
103+
})
104+
}
105+
}
106+
}
107+
}
108+
}
109+
}
110+
}
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* @author lsdsjy
3+
*/
4+
'use strict'
5+
6+
const RuleTester = require('eslint').RuleTester
7+
const rule = require('../../../lib/rules/max-lines-per-block')
8+
9+
const tester = new RuleTester({
10+
parser: require.resolve('vue-eslint-parser'),
11+
parserOptions: {
12+
ecmaVersion: 2020,
13+
sourceType: 'module'
14+
}
15+
})
16+
17+
tester.run('max-lines-per-block', rule, {
18+
valid: [
19+
{
20+
code: `
21+
<script>
22+
console.log(1)
23+
</script>
24+
<template>
25+
<div></div>
26+
</template>
27+
`,
28+
options: [{ template: 1 }]
29+
},
30+
{
31+
code: `
32+
<template>
33+
34+
<div></div>
35+
</template>
36+
`,
37+
options: [{ template: 1, skipBlankLines: true }]
38+
},
39+
{
40+
code: `
41+
<template>
42+
<div>
43+
</div>
44+
</template>
45+
`,
46+
options: [{ script: 1, style: 1 }]
47+
}
48+
],
49+
invalid: [
50+
{
51+
code: `
52+
<template>
53+
54+
<div></div>
55+
</template>
56+
`,
57+
options: [{ template: 1 }],
58+
errors: [
59+
{
60+
message: 'Block has too many lines (2). Maximum allowed is 1.',
61+
line: 2,
62+
column: 7
63+
}
64+
]
65+
},
66+
{
67+
code: `
68+
<script>
69+
70+
const a = 1
71+
console.log(a)
72+
</script>
73+
`,
74+
options: [{ script: 1, skipBlankLines: true }],
75+
errors: [
76+
{
77+
message: 'Block has too many lines (2). Maximum allowed is 1.',
78+
line: 2,
79+
column: 7
80+
}
81+
]
82+
}
83+
]
84+
})

0 commit comments

Comments
 (0)