Repository: vuejs/eslint-plugin-vue (js)

Issue:
want: vue/max-line rule, limit the number of lines of code per block
**Please describe what the rule should do:**

This rule applies to the number of lines of code.

Help people not to write thousands of lines of javascript code in the same file. Split the code to improve maintainability and readability.

**What category should the rule belong to?**
<!-- (place an "X" next to just one item) -->
[ ] Enforces code style (layout)
[ ] Warns about a potential error (problem)
[x] Suggests an alternate way of doing something (suggestion)
[ ] Other (please specify:)

**Provide 2-3 code examples that this rule should warn about:**

```vue
<template>
  <view>
  <view>
</template>

<script>
// There is a very long javascript code here
export default {
 data () {
  return {}
 }
}
</script>

<style>
root {
  long-long-style: none;
}
</style>
```

**Additional context**

the rule look like:

```js
rules: {
  "vue/max-line": ['error', {
    // Specify a block of code via lang="xxx"
    // eg.
    // <style lang="sass"><style>
    // sass: 800
 	js: 800,         // limit max line for javascript block
    jsx: 800,
    ts: 800,         // limit max line for typescript block
    tsx: 800,
    html: 1200,      // limit max line for HTML block
    css: 1200        // limit max line for css/sass/scss block
  }]
}
```

It receives a parameter to specify the maximum length of a certain code block



Pull request description:
Add `vue/max-lines-per-block` rule
Closes #1443 

Patch:
diff --git a/docs/rules/max-lines-per-block.md b/docs/rules/max-lines-per-block.md
new file mode 100644
index 00000000..02fd2be4
--- /dev/null
+++ b/docs/rules/max-lines-per-block.md
@@ -0,0 +1,54 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/max-lines-per-block
+description: enforce maximum number of lines in Vue SFC blocks
+---
+# vue/max-lines-per-block
+
+> enforce maximum number of lines in Vue SFC blocks
+
+- :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 lines per block, in order to aid in maintainability and reduce complexity.
+
+## :wrench: Options
+
+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.
+The following properties can be specified for the object.
+
+- `script` ... Specify the maximum number of lines in `<script>` block. Won't enforce limitation if not specified.
+- `template` ... Specify the maximum number of lines in `<template>` block. Won't enforce limitation if not specified.
+- `style` ... Specify the maximum number of lines in `<style>` block. Won't enforce limitation if not specified.
+- `skipBlankLines` ... Ignore lines made up purely of whitespaces.
+
+### `{ template: 2 }`
+
+<eslint-code-block :rules="{'vue/max-lines-per-block': ['error', { template: 2 }]}">
+
+```vue
+<!-- ✗ BAD -->
+<template>
+  <div>
+    hi
+  </div>
+</template>
+```
+
+</eslint-code-block>
+
+### `{ script: 1, skipBlankLines: true }`
+
+<eslint-code-block :rules="{'vue/max-lines-per-block': ['error', { script: 1, skipBlankLines: true }]}">
+
+```vue
+<!-- ✓ GOOD -->
+<script>
+
+  console.log(1)
+</script>
+```
+
+</eslint-code-block>
diff --git a/lib/rules/max-lines-per-block.js b/lib/rules/max-lines-per-block.js
new file mode 100644
index 00000000..bcb82d56
--- /dev/null
+++ b/lib/rules/max-lines-per-block.js
@@ -0,0 +1,110 @@
+/**
+ * @author lsdsjy
+ * @fileoverview Rule for checking the maximum number of lines in Vue SFC blocks.
+ */
+'use strict'
+
+const { SourceCode } = require('eslint')
+const utils = require('../utils')
+
+/**
+ * @param {string} text
+ */
+function isEmptyLine(text) {
+  return !text.trim()
+}
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'enforce maximum number of lines in Vue SFC blocks',
+      categories: undefined,
+      url: 'https://eslint.vuejs.org/rules/max-lines-per-block.html'
+    },
+    fixable: null,
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          style: {
+            type: 'integer',
+            minimum: 1
+          },
+          template: {
+            type: 'integer',
+            minimum: 1
+          },
+          script: {
+            type: 'integer',
+            minimum: 1
+          },
+          skipBlankLines: {
+            type: 'boolean',
+            minimum: 0
+          }
+        },
+        additionalProperties: false
+      }
+    ],
+    messages: {
+      tooManyLines:
+        'Block has too many lines ({{lineCount}}). Maximum allowed is {{limit}}.'
+    }
+  },
+  /** @param {RuleContext} context */
+  create(context) {
+    const option = context.options[0] || {}
+    /**
+     * @type {Record<string, number>}
+     */
+    const limits = {
+      template: option.template,
+      script: option.script,
+      style: option.style
+    }
+
+    const code = context.getSourceCode()
+    const documentFragment =
+      context.parserServices.getDocumentFragment &&
+      context.parserServices.getDocumentFragment()
+
+    function getTopLevelHTMLElements() {
+      if (documentFragment) {
+        return documentFragment.children.filter(utils.isVElement)
+      }
+      return []
+    }
+
+    return {
+      /** @param {Program} node */
+      Program(node) {
+        if (utils.hasInvalidEOF(node)) {
+          return
+        }
+        for (const block of getTopLevelHTMLElements()) {
+          if (limits[block.name]) {
+            // We suppose the start tag and end tag occupy one single line respectively
+            let lineCount = block.loc.end.line - block.loc.start.line - 1
+
+            if (option.skipBlankLines) {
+              const lines = SourceCode.splitLines(code.getText(block))
+              lineCount -= lines.filter(isEmptyLine).length
+            }
+
+            if (lineCount > limits[block.name]) {
+              context.report({
+                node: block,
+                messageId: 'tooManyLines',
+                data: {
+                  limit: limits[block.name],
+                  lineCount
+                }
+              })
+            }
+          }
+        }
+      }
+    }
+  }
+}


Test patch:
diff --git a/tests/lib/rules/max-lines-per-block.js b/tests/lib/rules/max-lines-per-block.js
new file mode 100644
index 00000000..01d868e6
--- /dev/null
+++ b/tests/lib/rules/max-lines-per-block.js
@@ -0,0 +1,84 @@
+/**
+ * @author lsdsjy
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/max-lines-per-block')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: {
+    ecmaVersion: 2020,
+    sourceType: 'module'
+  }
+})
+
+tester.run('max-lines-per-block', rule, {
+  valid: [
+    {
+      code: `
+      <script>
+        console.log(1)
+      </script>
+      <template>
+        <div></div>
+      </template>
+      `,
+      options: [{ template: 1 }]
+    },
+    {
+      code: `
+      <template>
+
+        <div></div>
+      </template>
+      `,
+      options: [{ template: 1, skipBlankLines: true }]
+    },
+    {
+      code: `
+      <template>
+        <div>
+        </div>
+      </template>
+      `,
+      options: [{ script: 1, style: 1 }]
+    }
+  ],
+  invalid: [
+    {
+      code: `
+      <template>
+
+        <div></div>
+      </template>
+      `,
+      options: [{ template: 1 }],
+      errors: [
+        {
+          message: 'Block has too many lines (2). Maximum allowed is 1.',
+          line: 2,
+          column: 7
+        }
+      ]
+    },
+    {
+      code: `
+      <script>
+
+        const a = 1
+        console.log(a)
+      </script>
+      `,
+      options: [{ script: 1, skipBlankLines: true }],
+      errors: [
+        {
+          message: 'Block has too many lines (2). Maximum allowed is 1.',
+          line: 2,
+          column: 7
+        }
+      ]
+    }
+  ]
+})