Repository: vuejs/eslint-plugin-vue
Pull request #1943 (2022-08-08 12:11:07)

feat: add `no-required-prop-with-default` rule
This rule enforce props with default values ​​to be optional.
Because when a required prop declared with a default value, but it doesn't be passed value when using it, it will be assigned the default value. So a required prop with default value is same as a optional prop.

xiaoxiangmoe: Maybe we should add  this rule in vue2 and vue3 presets.
ota-meshi: Thank you for this PR.

I think a rule that checks for unnecessary defaults would be useful. However, I think it would be better to be able to check other than `<script setup lang="ts">`.

```vue
<script>
export default {
  props: {
    foo: {
      required: true,
      default: 42
    }
  }
}
</script>
```

```vue
<script setup>
defineProps({
  foo: {
    required: true,
    default: 42
  }
})
</script>
```

In this case, don't use `withDefaults`, so maybe we should rename the rule.
sodatea: Regarding the design:
`eslint-plugin-react` uses an option in `require-default-props` for a similar purpose: https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/require-default-props.md#forbiddefaultforrequired

I think that design might be more performant as all the checks are done in one pass. What do you think?
ota-meshi: Hmm... I think it's a good idea to follow the precedent,  but I think the name `require-default-props` can be confusing to report unnecessary defaults. If I had to choose one, I think the new rule is better.
ota-meshi: @neferqiqi can you change this PR?
neferqiqi: > 

ok, i will change the rule to supoort the case
```
<script>
export default {
  props: {
    foo: {
      required: true,
      default: 42
    }
  }
}
</script>
```
but i want to reserve the auto-fix. Because firstly, it only fix the props in current file, so it's safe to auto fix; secondly, for example in my project, there are many cases that assign default values ​​to required props, i think need a auto fix way to fix these cases. what do you think? :)
ota-meshi: > but i want to reserve the auto-fix. Because firstly, it only fix the props in current file, so it's safe to auto fix; secondly, for example in my project, there are many cases that assign default values ​​to required props, i think need a auto fix way to fix these cases. what do you think? :)

I don't think it should auto-fix. As I wrote earlier, it's because we don't know which of the two methods the user intended.
I think it's the same reason no-unused-vars doesn't automatically remove variables. It's safe to remove the variable, but we shouldn't, as the user may intend to use it.

However, I do understand that your team need auto-fix. What do you think of adding an option to enable auto-fix?
(However, that in most cases I don't like that option as it just complicates the implementation of the rules.)


neferqiqi: > > but i want to reserve the auto-fix. Because firstly, it only fix the props in current file, so it's safe to auto fix; secondly, for example in my project, there are many cases that assign default values ​​to required props, i think need a auto fix way to fix these cases. what do you think? :)
> 
> I don't think it should auto-fix. As I wrote earlier, it's because we don't know which of the two methods the user intended. I think it's the same reason no-unused-vars doesn't automatically remove variables. It's safe to remove the variable, but we shouldn't, as the user may intend to use it.
> 
> However, I do understand that your team need auto-fix. What do you think of adding an option to enable auto-fix? (However, that in most cases I don't like that option as it just complicates the implementation of the rules.)

ok, i will add an option to enable auto-fix.
FloEdelmann: Maybe better rename the rule to `no-required-prop-with-default`?

Patch:
diff --git a/docs/.vuepress/components/eslint-code-block.vue b/docs/.vuepress/components/eslint-code-block.vue
index f71e606a..b95e0d07 100644
--- a/docs/.vuepress/components/eslint-code-block.vue
+++ b/docs/.vuepress/components/eslint-code-block.vue
@@ -3,7 +3,7 @@
     <eslint-editor
       :linter="linter"
       :config="config"
-      :code="code"
+      v-model="code"
       :style="{ height }"
       class="eslint-code-block"
       :filename="filename"
@@ -43,23 +43,50 @@ export default {
     language: {
       type: String,
       default: 'html'
+    },
+    /**
+     * If enabled, `@typescript-eslint/parser` will be used.
+     * This must be enabled when used for `ts` code blocks.
+     */
+    typescript: {
+      type: Boolean,
+      default: false
     }
   },
 
   data() {
+    const code = this.computeCodeFromSlot()
+    // The height is determined in the initial processing.
+    // This is because later code changes do not change the height.
+    const lines = code.split('\n').length
+    const height = `${Math.max(120, 19 * lines)}px`
     return {
+      code,
+      height,
       linter: null,
       preprocess: processors['.vue'].preprocess,
       postprocess: processors['.vue'].postprocess,
       format: {
         insertSpaces: true,
         tabSize: 2
-      }
+      },
+      tsEslintParser: null
     }
   },
 
   computed: {
     config() {
+      let parser = null // Use default parser (`espree`)
+      if (this.typescript) {
+        // Use `@typescript-eslint/parser`.
+        parser = this.tsEslintParser
+      } else if (this.langTs) {
+        // Use `@typescript-eslint/parser` only when `<script lang="ts">` or `<script lang="typescript">`.
+        parser = {
+          ts: this.tsEslintParser,
+          typescript: this.tsEslintParser
+        }
+      }
       return {
         globals: {
           console: false,
@@ -90,6 +117,7 @@ export default {
         rules: this.rules,
         parser: 'vue-eslint-parser',
         parserOptions: {
+          parser,
           ecmaVersion: 'latest',
           sourceType: 'module',
           ecmaFeatures: {
@@ -99,24 +127,37 @@ export default {
       }
     },
 
-    code() {
-      return `${this.computeCodeFromSlot(this.$slots.default).trim()}\n`
-    },
+    /**
+     * Checks whether code may be using lang="ts" or lang="typescript".
+     * @returns {boolean} If `true`, may be using lang="ts" or lang="typescript".
+     */
+    langTs() {
+      return /lang\s*=\s*(?:"ts"|ts|'ts'|"typescript"|typescript|'typescript')/u.test(
+        this.code
+      )
+    }
+  },
 
-    height() {
-      const lines = this.code.split('\n').length
-      return `${Math.max(120, 19 * lines)}px`
+  watch: {
+    typescript(value) {
+      if (value) {
+        this.loadTypescriptESLint()
+      }
+    },
+    langTs(value) {
+      if (value) {
+        this.loadTypescriptESLint()
+      }
     }
   },
 
   methods: {
-    computeCodeFromSlot(nodes) {
-      if (!Array.isArray(nodes)) {
-        return ''
-      }
-      return nodes
-        .map((node) => node.text || this.computeCodeFromSlot(node.children))
-        .join('')
+    computeCodeFromSlot() {
+      return `${computeCodeFromSlot(this.$slots.default).trim()}\n`
+    },
+
+    async loadTypescriptESLint() {
+      this.tsEslintParser = await import('@typescript-eslint/parser')
     }
   },
 
@@ -126,6 +167,9 @@ export default {
       import('eslint/lib/linter'),
       import('espree').then(() => import('vue-eslint-parser'))
     ])
+    if (this.langTs || this.typescript) {
+      await this.loadTypescriptESLint()
+    }
 
     const linter = (this.linter = new Linter())
 
@@ -136,6 +180,15 @@ export default {
     linter.defineParser('vue-eslint-parser', { parseForESLint })
   }
 }
+
+function computeCodeFromSlot(nodes) {
+  if (!Array.isArray(nodes)) {
+    return ''
+  }
+  return nodes
+    .map((node) => node.text || computeCodeFromSlot(node.children))
+    .join('')
+}
 </script>
 
 <style>
diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index 68aec1af..38ad965e 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -132,7 +132,8 @@ module.exports = {
           '@eslint/eslintrc/universal': path.resolve(
             __dirname,
             '../../node_modules/@eslint/eslintrc/dist/eslintrc-universal.cjs'
-          )
+          ),
+          globby: require.resolve('./shim/globby')
         }
       }
     }
diff --git a/docs/.vuepress/enhanceApp.js b/docs/.vuepress/enhanceApp.js
index 03082d60..1e62f60a 100644
--- a/docs/.vuepress/enhanceApp.js
+++ b/docs/.vuepress/enhanceApp.js
@@ -12,7 +12,7 @@ export default (
     window.process = new Proxy(
       {
         env: {},
-        cwd: () => undefined
+        cwd: () => ''
       },
       {
         get(target, name) {
diff --git a/docs/.vuepress/shim/globby.js b/docs/.vuepress/shim/globby.js
new file mode 100644
index 00000000..4ba52ba2
--- /dev/null
+++ b/docs/.vuepress/shim/globby.js
@@ -0,0 +1,1 @@
+module.exports = {}
diff --git a/docs/.vuepress/shim/module.js b/docs/.vuepress/shim/module.js
index 66ab9785..cfeccf31 100644
--- a/docs/.vuepress/shim/module.js
+++ b/docs/.vuepress/shim/module.js
@@ -1,3 +1,11 @@
 module.exports = {
-  createRequire: () => () => null
+  createRequire: () => (module) => {
+    if (module === 'espree') {
+      return require('espree')
+    }
+    if (module === 'eslint-scope') {
+      return require('eslint-scope')
+    }
+    throw new Error(`Not implemented: ${module}`)
+  }
 }
diff --git a/docs/rules/README.md b/docs/rules/README.md
index de00e7e5..780b10cb 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -212,7 +212,9 @@ For example:
 | [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: | :hammer: |
 | [vue/component-options-name-casing](./component-options-name-casing.md) | enforce the casing of component name in `components` options | :wrench::bulb: | :hammer: |
 | [vue/custom-event-name-casing](./custom-event-name-casing.md) | enforce specific casing for custom event name |  | :hammer: |
+| [vue/define-emits-declaration](./define-emits-declaration.md) | enforce declaration style of `defineEmits` |  | :hammer: |
 | [vue/define-macros-order](./define-macros-order.md) | enforce order of `defineEmits` and `defineProps` compiler macros | :wrench: | :lipstick: |
+| [vue/define-props-declaration](./define-props-declaration.md) | enforce declaration style of `defineProps` |  | :hammer: |
 | [vue/html-button-has-type](./html-button-has-type.md) | disallow usage of button without an explicit type attribute |  | :hammer: |
 | [vue/html-comment-content-newline](./html-comment-content-newline.md) | enforce unified line brake in HTML comments | :wrench: | :lipstick: |
 | [vue/html-comment-content-spacing](./html-comment-content-spacing.md) | enforce unified spacing in HTML comments | :wrench: | :lipstick: |
@@ -228,6 +230,7 @@ For example:
 | [vue/no-multiple-objects-in-class](./no-multiple-objects-in-class.md) | disallow to pass multiple objects into array to class |  | :hammer: |
 | [vue/no-potential-component-option-typo](./no-potential-component-option-typo.md) | disallow a potential typo in your component property | :bulb: | :hammer: |
 | [vue/no-ref-object-destructure](./no-ref-object-destructure.md) | disallow destructuring of ref objects that can lead to loss of reactivity |  | :warning: |
+| [vue/no-required-prop-with-default](./no-required-prop-with-default.md) | enforce props with default values ​​to be optional | :wrench::bulb: | :warning: |
 | [vue/no-restricted-block](./no-restricted-block.md) | disallow specific block |  | :hammer: |
 | [vue/no-restricted-call-after-await](./no-restricted-call-after-await.md) | disallow asynchronously called restricted methods |  | :hammer: |
 | [vue/no-restricted-class](./no-restricted-class.md) | disallow specific classes in Vue components |  | :warning: |
@@ -249,6 +252,7 @@ For example:
 | [vue/no-useless-v-bind](./no-useless-v-bind.md) | disallow unnecessary `v-bind` directives | :wrench: | :hammer: |
 | [vue/no-v-text](./no-v-text.md) | disallow use of v-text |  | :hammer: |
 | [vue/padding-line-between-blocks](./padding-line-between-blocks.md) | require or disallow padding lines between blocks | :wrench: | :lipstick: |
+| [vue/padding-line-between-tags](./padding-line-between-tags.md) | require or disallow newlines between sibling tags in template | :wrench: | :lipstick: |
 | [vue/prefer-prop-type-boolean-first](./prefer-prop-type-boolean-first.md) | enforce `Boolean` comes first in component prop types | :bulb: | :warning: |
 | [vue/prefer-separate-static-class](./prefer-separate-static-class.md) | require static class names in template to be in a separate `class` attribute | :wrench: | :hammer: |
 | [vue/prefer-true-attribute-shorthand](./prefer-true-attribute-shorthand.md) | require shorthand form attribute when `v-bind` value is `true` | :bulb: | :hammer: |
diff --git a/docs/rules/define-emits-declaration.md b/docs/rules/define-emits-declaration.md
new file mode 100644
index 00000000..087f2b69
--- /dev/null
+++ b/docs/rules/define-emits-declaration.md
@@ -0,0 +1,81 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/define-emits-declaration
+description: enforce declaration style of `defineEmits`
+since: v9.5.0
+---
+# vue/define-emits-declaration
+
+> enforce declaration style of `defineEmits`
+
+## :book: Rule Details
+
+This rule enforces `defineEmits` typing style which you should use `type-based` or `runtime` declaration.
+
+This rule only works in setup script and `lang="ts"`.
+
+<eslint-code-block :rules="{'vue/define-emits-declaration': ['error']}">
+
+```vue
+<script setup lang="ts">
+/* ✓ GOOD */
+const emit = defineEmits<{
+  (e: 'change', id: number): void
+  (e: 'update', value: string): void
+}>()
+
+/* ✗ BAD */
+const emit = defineEmits(['change', 'update'])
+</script>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+```json
+  "vue/define-emits-declaration": ["error", "type-based" | "runtime"]
+```
+
+- `type-based` (default) enforces type-based declaration
+- `runtime` enforces runtime declaration
+
+### `runtime`
+
+<eslint-code-block :rules="{'vue/define-emits-declaration': ['error', 'runtime']}">
+
+```vue
+<script setup lang="ts">
+/* ✗ BAD */
+const emit = defineEmits<{
+  (e: 'change', id: number): void
+  (e: 'update', value: string): void
+}>()
+
+/* ✓ GOOD */
+const emit = defineEmits(['change', 'update'])
+</script>
+```
+
+</eslint-code-block>
+
+## :couple: Related Rules
+
+- [vue/define-props-declaration](./define-props-declaration.md)
+- [vue/valid-define-emits](./valid-define-emits.md)
+
+## :books: Further Reading
+
+- [`defineEmits`](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)
+- [Typescript-only-features of `defineEmits`](https://vuejs.org/api/sfc-script-setup.html#typescript-only-features)
+- [Guide - Typing-component-emits](https://vuejs.org/guide/typescript/composition-api.html#typing-component-emits)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.5.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-emits-declaration.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-emits-declaration.js)
diff --git a/docs/rules/define-props-declaration.md b/docs/rules/define-props-declaration.md
new file mode 100644
index 00000000..6d8d2e8d
--- /dev/null
+++ b/docs/rules/define-props-declaration.md
@@ -0,0 +1,83 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/define-props-declaration
+description: enforce declaration style of `defineProps`
+since: v9.5.0
+---
+# vue/define-props-declaration
+
+> enforce declaration style of `defineProps`
+
+## :book: Rule Details
+
+This rule enforces `defineProps` typing style which you should use `type-based` or `runtime` declaration.
+
+This rule only works in setup script and `lang="ts"`.
+
+<eslint-code-block :rules="{'vue/define-props-declaration': ['error']}">
+
+```vue
+<script setup lang="ts">
+/* ✓ GOOD */
+const props = defineProps<{
+  kind: string
+}>()
+
+/* ✗ BAD */
+const props = defineProps({
+  kind: { type: String }
+})
+</script>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+```json
+  "vue/define-props-declaration": ["error", "type-based" | "runtime"]
+```
+
+- `type-based` (default) enforces type-based declaration
+- `runtime` enforces runtime declaration
+
+### `"runtime"`
+
+<eslint-code-block :rules="{'vue/define-emits-declaration': ['error', 'runtime']}">
+
+```vue
+<script setup lang="ts">
+/* ✓ GOOD */
+const props = defineProps({
+  kind: { type: String }
+})
+
+/* ✗ BAD */
+const props = defineProps<{
+  kind: string
+}>()
+</script>
+```
+
+</eslint-code-block>
+
+## :couple: Related Rules
+
+- [vue/define-emits-declaration](./define-emits-declaration.md)
+- [vue/valid-define-props](./valid-define-props.md)
+
+## :books: Further Reading
+
+- [`defineProps`](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)
+- [Typescript-only-features of `defineProps`](https://vuejs.org/api/sfc-script-setup.html#typescript-only-features)
+- [Guide - Typing-component-props](https://vuejs.org/guide/typescript/composition-api.html#typing-component-props)
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.5.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/define-props-declaration.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/define-props-declaration.js)
diff --git a/docs/rules/no-ref-object-destructure.md b/docs/rules/no-ref-object-destructure.md
index 4c4b837a..a3b9d775 100644
--- a/docs/rules/no-ref-object-destructure.md
+++ b/docs/rules/no-ref-object-destructure.md
@@ -3,13 +3,12 @@ pageClass: rule-details
 sidebarDepth: 0
 title: vue/no-ref-object-destructure
 description: disallow destructuring of ref objects that can lead to loss of reactivity
+since: v9.5.0
 ---
 # vue/no-ref-object-destructure
 
 > disallow destructuring of ref objects that can lead to loss of reactivity
 
-- :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 reports the destructuring of ref objects causing the value to lose reactivity.
@@ -49,6 +48,10 @@ const v5 = $computed(() => fn(count) /* ✓ GOOD */)
 
 Nothing.
 
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.5.0
+
 ## :mag: Implementation
 
 - [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-ref-object-destructure.js)
diff --git a/docs/rules/no-required-prop-with-default.md b/docs/rules/no-required-prop-with-default.md
new file mode 100644
index 00000000..92800a0d
--- /dev/null
+++ b/docs/rules/no-required-prop-with-default.md
@@ -0,0 +1,96 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-required-prop-with-default
+description: enforce props with default values ​​to be optional
+---
+# vue/no-required-prop-with-default
+
+> enforce props with default values ​​to be optional
+
+- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
+## :book: Rule Details
+
+If a prop is declared with a default value, whether it is required or not, we can always skip it in actual use. In that situation, the default value would be applied.
+So, a required prop with a default value is essentially the same as an optional prop.
+This rule enforces all props with default values to be optional.
+
+<eslint-code-block fix :rules="{'vue/no-required-prop-with-default': ['error', { autoFix: true }]}">
+
+```vue
+<script setup lang="ts">
+  /* ✓ GOOD */
+  const props = withDefaults(
+    defineProps<{
+      name?: string | number
+      age?: number
+    }>(),
+    {
+      name: "Foo",
+    }
+  );
+
+  /* ✗ BAD */
+  const props = withDefaults(
+    defineProps<{
+      name: string | number
+      age?: number
+    }>(),
+    {
+      name: "Foo",
+    }
+  );
+</script>
+```
+
+</eslint-code-block>
+
+<eslint-code-block fix :rules="{'vue/no-required-prop-with-default': ['error', { autoFix: true }]}">
+
+```vue
+<script>
+  export default {
+    /* ✓ GOOD */
+    props: {
+      name: {
+        required: true,
+        default: 'Hello'
+      }
+    }
+
+    /* ✗ BAD */
+    props: {
+      name: {
+        required: true,
+        default: 'Hello'
+      }
+    }
+  }
+</script>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+```json
+{
+  "vue/no-required-prop-with-default": ["error", {
+    "autofix": false,
+  }]
+}
+```
+
+- `"autofix"` ... If `true`, enable autofix. (Default: `false`)
+
+## :couple: Related Rules
+
+- [vue/require-default-prop](./require-default-prop.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-required-prop-with-default.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-required-prop-with-default.js)
diff --git a/docs/rules/padding-line-between-tags.md b/docs/rules/padding-line-between-tags.md
new file mode 100644
index 00000000..18770104
--- /dev/null
+++ b/docs/rules/padding-line-between-tags.md
@@ -0,0 +1,167 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/padding-line-between-tags
+description: require or disallow newlines between sibling tags in template
+since: v9.5.0
+---
+# vue/padding-line-between-tags
+
+> require or disallow newlines between sibling tags in template
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+
+## :book: Rule Details
+
+This rule requires or disallows newlines between sibling HTML tags.
+
+<eslint-code-block fix :rules="{'vue/padding-line-between-tags': ['error']}">
+
+```vue
+<template>
+  <div>
+    <!-- ✓ GOOD: -->
+    <div></div>
+
+    <div>
+    </div>
+    
+    <div />
+
+    <div />
+    <!-- ✗ BAD: -->
+    <div></div>
+    <div>
+    </div>
+    <div /><div />
+  </div>
+</template>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+```json
+{
+  "vue/padding-line-between-tags": ["error", [
+    { "blankLine": "always", "prev": "*", "next": "*" }
+  ]]
+}
+```
+
+This rule requires blank lines between each sibling HTML tag by default.
+
+A configuration is an object which has 3 properties; blankLine, prev and next. For example, { blankLine: "always", prev: "br", next: "div" } means “one or more blank lines are required between a br tag and a div tag.” You can supply any number of configurations. If a tag pair matches multiple configurations, the last matched configuration will be used.
+
+- `blankLine` is one of the following:
+  - `always` requires one or more blank lines.
+  - `never` disallows blank lines.
+- `prev` any tag name without brackets.
+- `next` any tag name without brackets.
+
+### Disallow blank lines between all tags
+
+`{ blankLine: 'never', prev: '*', next: '*' }`
+
+<eslint-code-block fix :rules="{'vue/padding-line-between-tags': ['error', [
+  { blankLine: 'never', prev: '*', next: '*' }
+]]}">
+
+```vue
+<template>
+  <div>
+    <div></div>
+    <div>
+    </div>
+    <div />
+  </div>
+</template>
+```
+
+</eslint-code-block>
+
+### Require newlines after `<br>`
+
+`{ blankLine: 'always', prev: 'br', next: '*' }`
+
+<eslint-code-block fix :rules="{'vue/padding-line-between-tags': ['error', [
+  { blankLine: 'always', prev: 'br', next: '*' }
+]]}">
+
+```vue
+<template>
+  <div>
+    <ul>
+      <li>
+      </li>
+      <br />
+
+      <li>
+      </li>
+    </ul>
+  </div>
+</template>
+```
+
+</eslint-code-block>
+
+### Require newlines before `<br>`
+
+`{ blankLine: 'always', prev: '*', next: 'br' }`
+
+<eslint-code-block fix :rules="{'vue/padding-line-between-tags': ['error', [
+  { blankLine: 'always', prev: '*', next: 'br' }
+]]}">
+
+```vue
+<template>
+  <div>
+    <ul>
+      <li>
+      </li>
+      
+      <br />
+      <li>
+      </li>
+    </ul>
+  </div>
+</template>
+```
+
+</eslint-code-block>
+
+### Require newlines between `<br>` and `<img>`
+
+`{ blankLine: 'always', prev: 'br', next: 'img' }`
+
+<eslint-code-block fix :rules="{'vue/padding-line-between-tags': ['error', [
+  { blankLine: 'always', prev: 'br', next: 'img' }
+]]}">
+
+```vue
+<template>
+  <div>
+    <ul>
+      <li>
+      </li>
+      <br />
+
+      <img />
+      <li>
+      </li>
+    </ul>
+  </div>
+</template>
+```
+
+</eslint-code-block>
+
+## :rocket: Version
+
+This rule was introduced in eslint-plugin-vue v9.5.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/padding-line-between-tags.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/padding-line-between-tags.js)
diff --git a/docs/rules/valid-define-emits.md b/docs/rules/valid-define-emits.md
index 5718794c..f7de8454 100644
--- a/docs/rules/valid-define-emits.md
+++ b/docs/rules/valid-define-emits.md
@@ -27,8 +27,8 @@ This rule reports `defineEmits` compiler macros in the following cases:
 
 ```vue
 <script setup>
-  /* ✓ GOOD */
-  defineEmits({ notify: null })
+/* ✓ GOOD */
+defineEmits({ notify: null })
 </script>
 ```
 
@@ -38,29 +38,33 @@ This rule reports `defineEmits` compiler macros in the following cases:
 
 ```vue
 <script setup>
-  /* ✓ GOOD */
-  defineEmits(['notify'])
+/* ✓ GOOD */
+defineEmits(['notify'])
 </script>
 ```
 
 </eslint-code-block>
 
+<eslint-code-block :rules="{'vue/valid-define-emits': ['error']}">
+
 ```vue
 <script setup lang="ts">
-  /* ✓ GOOD */
-  defineEmits<(e: 'notify')=>void>()
+/* ✓ GOOD */
+defineEmits<(e: 'notify') => void>()
 </script>
 ```
 
+</eslint-code-block>
+
 <eslint-code-block :rules="{'vue/valid-define-emits': ['error']}">
 
 ```vue
 <script>
-  const def = { notify: null }
+const def = { notify: null }
 </script>
 <script setup>
-  /* ✓ GOOD */
-  defineEmits(def)
+/* ✓ GOOD */
+defineEmits(def)
 </script>
 ```
 
@@ -70,28 +74,32 @@ This rule reports `defineEmits` compiler macros in the following cases:
 
 ```vue
 <script setup>
-  /* ✗ BAD */
-  const def = { notify: null }
-  defineEmits(def)
+/* ✗ BAD */
+const def = { notify: null }
+defineEmits(def)
 </script>
 ```
 
 </eslint-code-block>
 
+<eslint-code-block :rules="{'vue/valid-define-emits': ['error']}">
+
 ```vue
 <script setup lang="ts">
-  /* ✗ BAD */
-  defineEmits<(e: 'notify')=>void>({ submit: null })
+/* ✗ BAD */
+defineEmits<(e: 'notify') => void>({ submit: null })
 </script>
 ```
 
+</eslint-code-block>
+
 <eslint-code-block :rules="{'vue/valid-define-emits': ['error']}">
 
 ```vue
 <script setup>
-  /* ✗ BAD */
-  defineEmits({ notify: null })
-  defineEmits({ submit: null })
+/* ✗ BAD */
+defineEmits({ notify: null })
+defineEmits({ submit: null })
 </script>
 ```
 
@@ -101,13 +109,13 @@ This rule reports `defineEmits` compiler macros in the following cases:
 
 ```vue
 <script>
-  export default {
-    emits: { notify: null }
-  }
+export default {
+  emits: { notify: null }
+}
 </script>
 <script setup>
-  /* ✗ BAD */
-  defineEmits({ submit: null })
+/* ✗ BAD */
+defineEmits({ submit: null })
 </script>
 ```
 
@@ -117,8 +125,8 @@ This rule reports `defineEmits` compiler macros in the following cases:
 
 ```vue
 <script setup>
-  /* ✗ BAD */
-  defineEmits()
+/* ✗ BAD */
+defineEmits()
 </script>
 ```
 
@@ -128,6 +136,11 @@ This rule reports `defineEmits` compiler macros in the following cases:
 
 Nothing.
 
+## :couple: Related Rules
+
+- [vue/define-emits-declaration](./define-emits-declaration.md)
+- [vue/valid-define-props](./valid-define-props.md)
+
 ## :rocket: Version
 
 This rule was introduced in eslint-plugin-vue v7.13.0
diff --git a/docs/rules/valid-define-props.md b/docs/rules/valid-define-props.md
index bd7d5f75..4345d242 100644
--- a/docs/rules/valid-define-props.md
+++ b/docs/rules/valid-define-props.md
@@ -27,8 +27,8 @@ This rule reports `defineProps` compiler macros in the following cases:
 
 ```vue
 <script setup>
-  /* ✓ GOOD */
-  defineProps({ msg: String })
+/* ✓ GOOD */
+defineProps({ msg: String })
 </script>
 ```
 
@@ -38,29 +38,33 @@ This rule reports `defineProps` compiler macros in the following cases:
 
 ```vue
 <script setup>
-  /* ✓ GOOD */
-  defineProps(['msg'])
+/* ✓ GOOD */
+defineProps(['msg'])
 </script>
 ```
 
 </eslint-code-block>
 
+<eslint-code-block :rules="{'vue/valid-define-props': ['error']}">
+
 ```vue
 <script setup lang="ts">
-  /* ✓ GOOD */
-  defineProps<{ msg?:string }>()
+/* ✓ GOOD */
+defineProps<{ msg?: string }>()
 </script>
 ```
 
+</eslint-code-block>
+
 <eslint-code-block :rules="{'vue/valid-define-props': ['error']}">
 
 ```vue
 <script>
-  const def = { msg: String }
+const def = { msg: String }
 </script>
 <script setup>
-  /* ✓ GOOD */
-  defineProps(def)
+/* ✓ GOOD */
+defineProps(def)
 </script>
 ```
 
@@ -70,28 +74,32 @@ This rule reports `defineProps` compiler macros in the following cases:
 
 ```vue
 <script setup>
-  /* ✗ BAD */
-  const def = { msg: String }
-  defineProps(def)
+/* ✗ BAD */
+const def = { msg: String }
+defineProps(def)
 </script>
 ```
 
 </eslint-code-block>
 
+<eslint-code-block :rules="{'vue/valid-define-props': ['error']}">
+
 ```vue
 <script setup lang="ts">
-  /* ✗ BAD */
-  defineProps<{ msg?:string }>({ msg: String })
+/* ✗ BAD */
+defineProps<{ msg?: string }>({ msg: String })
 </script>
 ```
 
+</eslint-code-block>
+
 <eslint-code-block :rules="{'vue/valid-define-props': ['error']}">
 
 ```vue
 <script setup>
-  /* ✗ BAD */
-  defineProps({ msg: String })
-  defineProps({ count: Number })
+/* ✗ BAD */
+defineProps({ msg: String })
+defineProps({ count: Number })
 </script>
 ```
 
@@ -101,13 +109,13 @@ This rule reports `defineProps` compiler macros in the following cases:
 
 ```vue
 <script>
-  export default {
-    props: { msg: String }
-  }
+export default {
+  props: { msg: String }
+}
 </script>
 <script setup>
-  /* ✗ BAD */
-  defineProps({ count: Number })
+/* ✗ BAD */
+defineProps({ count: Number })
 </script>
 ```
 
@@ -117,8 +125,8 @@ This rule reports `defineProps` compiler macros in the following cases:
 
 ```vue
 <script setup>
-  /* ✗ BAD */
-  defineProps()
+/* ✗ BAD */
+defineProps()
 </script>
 ```
 
@@ -128,6 +136,11 @@ This rule reports `defineProps` compiler macros in the following cases:
 
 Nothing.
 
+## :couple: Related Rules
+
+- [vue/define-props-declaration](./define-props-declaration.md)
+- [vue/valid-define-emits](./valid-define-emits.md)
+
 ## :rocket: Version
 
 This rule was introduced in eslint-plugin-vue v7.13.0
diff --git a/lib/configs/no-layout-rules.js b/lib/configs/no-layout-rules.js
index ab03684b..6b15833c 100644
--- a/lib/configs/no-layout-rules.js
+++ b/lib/configs/no-layout-rules.js
@@ -41,6 +41,7 @@ module.exports = {
     'vue/object-property-newline': 'off',
     'vue/operator-linebreak': 'off',
     'vue/padding-line-between-blocks': 'off',
+    'vue/padding-line-between-tags': 'off',
     'vue/script-indent': 'off',
     'vue/singleline-html-element-content-newline': 'off',
     'vue/space-in-parens': 'off',
diff --git a/lib/index.js b/lib/index.js
index ca3a9308..e7dcd5be 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -27,7 +27,9 @@ module.exports = {
     'component-options-name-casing': require('./rules/component-options-name-casing'),
     'component-tags-order': require('./rules/component-tags-order'),
     'custom-event-name-casing': require('./rules/custom-event-name-casing'),
+    'define-emits-declaration': require('./rules/define-emits-declaration'),
     'define-macros-order': require('./rules/define-macros-order'),
+    'define-props-declaration': require('./rules/define-props-declaration'),
     'dot-location': require('./rules/dot-location'),
     'dot-notation': require('./rules/dot-notation'),
     eqeqeq: require('./rules/eqeqeq'),
@@ -105,6 +107,7 @@ module.exports = {
     'no-potential-component-option-typo': require('./rules/no-potential-component-option-typo'),
     'no-ref-as-operand': require('./rules/no-ref-as-operand'),
     'no-ref-object-destructure': require('./rules/no-ref-object-destructure'),
+    'no-required-prop-with-default': require('./rules/no-required-prop-with-default'),
     'no-reserved-component-names': require('./rules/no-reserved-component-names'),
     'no-reserved-keys': require('./rules/no-reserved-keys'),
     'no-reserved-props': require('./rules/no-reserved-props'),
@@ -157,6 +160,7 @@ module.exports = {
     'operator-linebreak': require('./rules/operator-linebreak'),
     'order-in-components': require('./rules/order-in-components'),
     'padding-line-between-blocks': require('./rules/padding-line-between-blocks'),
+    'padding-line-between-tags': require('./rules/padding-line-between-tags'),
     'prefer-import-from-vue': require('./rules/prefer-import-from-vue'),
     'prefer-prop-type-boolean-first': require('./rules/prefer-prop-type-boolean-first'),
     'prefer-separate-static-class': require('./rules/prefer-separate-static-class'),
diff --git a/lib/rules/define-emits-declaration.js b/lib/rules/define-emits-declaration.js
new file mode 100644
index 00000000..758097f9
--- /dev/null
+++ b/lib/rules/define-emits-declaration.js
@@ -0,0 +1,68 @@
+/**
+ * @author Amorites
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description: 'enforce declaration style of `defineEmits`',
+      categories: undefined,
+      url: 'https://eslint.vuejs.org/rules/define-emits-declaration.html'
+    },
+    fixable: null,
+    messages: {
+      hasArg: 'Use type-based declaration instead of runtime declaration.',
+      hasTypeArg: 'Use runtime declaration instead of type-based declaration.'
+    },
+    schema: [
+      {
+        enum: ['type-based', 'runtime']
+      }
+    ]
+  },
+  /** @param {RuleContext} context */
+  create(context) {
+    const scriptSetup = utils.getScriptSetupElement(context)
+    if (!scriptSetup || !utils.hasAttribute(scriptSetup, 'lang', 'ts')) {
+      return {}
+    }
+
+    const defineType = context.options[0] || 'type-based'
+    return utils.defineScriptSetupVisitor(context, {
+      onDefineEmitsEnter(node) {
+        switch (defineType) {
+          case 'type-based':
+            if (node.arguments.length > 0) {
+              context.report({
+                node,
+                messageId: 'hasArg'
+              })
+            }
+            break
+
+          case 'runtime':
+            if (node.typeParameters && node.typeParameters.params.length > 0) {
+              context.report({
+                node,
+                messageId: 'hasTypeArg'
+              })
+            }
+            break
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/define-props-declaration.js b/lib/rules/define-props-declaration.js
new file mode 100644
index 00000000..2a73ed10
--- /dev/null
+++ b/lib/rules/define-props-declaration.js
@@ -0,0 +1,68 @@
+/**
+ * @author Amorites
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description: 'enforce declaration style of `defineProps`',
+      categories: undefined,
+      url: 'https://eslint.vuejs.org/rules/define-props-declaration.html'
+    },
+    fixable: null,
+    messages: {
+      hasArg: 'Use type-based declaration instead of runtime declaration.',
+      hasTypeArg: 'Use runtime declaration instead of type-based declaration.'
+    },
+    schema: [
+      {
+        enum: ['type-based', 'runtime']
+      }
+    ]
+  },
+  /** @param {RuleContext} context */
+  create(context) {
+    const scriptSetup = utils.getScriptSetupElement(context)
+    if (!scriptSetup || !utils.hasAttribute(scriptSetup, 'lang', 'ts')) {
+      return {}
+    }
+
+    const defineType = context.options[0] || 'type-based'
+    return utils.defineScriptSetupVisitor(context, {
+      onDefinePropsEnter(node) {
+        switch (defineType) {
+          case 'type-based':
+            if (node.arguments.length > 0) {
+              context.report({
+                node,
+                messageId: 'hasArg'
+              })
+            }
+            break
+
+          case 'runtime':
+            if (node.typeParameters && node.typeParameters.params.length > 0) {
+              context.report({
+                node,
+                messageId: 'hasTypeArg'
+              })
+            }
+            break
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-required-prop-with-default.js b/lib/rules/no-required-prop-with-default.js
new file mode 100644
index 00000000..6d62b89d
--- /dev/null
+++ b/lib/rules/no-required-prop-with-default.js
@@ -0,0 +1,155 @@
+/**
+ * @author @neferqiqi
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+/**
+ * @typedef {import('../utils').ComponentTypeProp} ComponentTypeProp
+ * @typedef {import('../utils').ComponentArrayProp} ComponentArrayProp
+ * @typedef {import('../utils').ComponentObjectProp} ComponentObjectProp
+ * @typedef {import('../utils').ComponentUnknownProp} ComponentUnknownProp
+ * @typedef {import('../utils').ComponentProp} ComponentProp
+ */
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    hasSuggestions: true,
+    type: 'problem',
+    docs: {
+      description: 'enforce props with default values ​​to be optional',
+      categories: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-required-prop-with-default.html'
+    },
+    fixable: 'code',
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          autofix: {
+            type: 'boolean'
+          }
+        },
+        additionalProperties: false
+      }
+    ],
+    messages: {
+      requireOptional: `Prop "{{ key }}" should be optional.`,
+      fixRequiredProp: `Change this prop to be optional.`
+    }
+  },
+  /** @param {RuleContext} context */
+  create(context) {
+    let canAutoFix = false
+    const option = context.options[0]
+    if (option) {
+      canAutoFix = option.autofix
+    }
+
+    /**
+     * @param {ComponentArrayProp | ComponentObjectProp | ComponentUnknownProp | ComponentProp} prop
+     * */
+    const handleObjectProp = (prop) => {
+      if (
+        prop.type === 'object' &&
+        prop.propName &&
+        prop.value.type === 'ObjectExpression' &&
+        utils.findProperty(prop.value, 'default')
+      ) {
+        const requiredProperty = utils.findProperty(prop.value, 'required')
+        if (!requiredProperty) return
+        const requiredNode = requiredProperty.value
+        if (
+          requiredNode &&
+          requiredNode.type === 'Literal' &&
+          !!requiredNode.value
+        ) {
+          context.report({
+            node: prop.node,
+            loc: prop.node.loc,
+            data: {
+              key: prop.propName
+            },
+            messageId: 'requireOptional',
+            fix: canAutoFix
+              ? (fixer) => fixer.replaceText(requiredNode, 'false')
+              : null,
+            suggest: canAutoFix
+              ? null
+              : [
+                  {
+                    messageId: 'fixRequiredProp',
+                    fix: (fixer) => fixer.replaceText(requiredNode, 'false')
+                  }
+                ]
+          })
+        }
+      }
+    }
+
+    return utils.compositingVisitors(
+      utils.defineVueVisitor(context, {
+        onVueObjectEnter(node) {
+          utils.getComponentPropsFromOptions(node).map(handleObjectProp)
+        }
+      }),
+      utils.defineScriptSetupVisitor(context, {
+        onDefinePropsEnter(node, props) {
+          if (!utils.hasWithDefaults(node)) {
+            props.map(handleObjectProp)
+            return
+          }
+          const withDefaultsProps = Object.keys(
+            utils.getWithDefaultsPropExpressions(node)
+          )
+          const requiredProps = props.flatMap((item) =>
+            item.type === 'type' && item.required ? [item] : []
+          )
+
+          for (const prop of requiredProps) {
+            if (withDefaultsProps.includes(prop.propName)) {
+              // skip setter & getter case
+              if (
+                prop.node.type === 'TSMethodSignature' &&
+                (prop.node.kind === 'get' || prop.node.kind === 'set')
+              ) {
+                return
+              }
+              // skip computed
+              if (prop.node.computed) {
+                return
+              }
+              context.report({
+                node: prop.node,
+                loc: prop.node.loc,
+                data: {
+                  key: prop.propName
+                },
+                messageId: 'requireOptional',
+                fix: canAutoFix
+                  ? (fixer) => fixer.insertTextAfter(prop.key, '?')
+                  : null,
+                suggest: canAutoFix
+                  ? null
+                  : [
+                      {
+                        messageId: 'fixRequiredProp',
+                        fix: (fixer) => fixer.insertTextAfter(prop.key, '?')
+                      }
+                    ]
+              })
+            }
+          }
+        }
+      })
+    )
+  }
+}
diff --git a/lib/rules/no-undef-components.js b/lib/rules/no-undef-components.js
index f6e66400..3ffe9cc3 100644
--- a/lib/rules/no-undef-components.js
+++ b/lib/rules/no-undef-components.js
@@ -50,7 +50,9 @@ module.exports = {
       }
     ],
     messages: {
-      undef: "The '<{{name}}>' component has been used, but not defined."
+      undef: "The '<{{name}}>' component has been used, but not defined.",
+      typeOnly:
+        "The '<{{name}}>' component has been used, but '{{name}}' only refers to a type."
     }
   },
   /** @param {RuleContext} context */
@@ -121,6 +123,7 @@ module.exports = {
       // For <script setup>
       /** @type {Set<string>} */
       const scriptVariableNames = new Set()
+      const scriptTypeOnlyNames = new Set()
       const globalScope = context.getSourceCode().scopeManager.globalScope
       if (globalScope) {
         for (const variable of globalScope.variables) {
@@ -130,7 +133,33 @@ module.exports = {
           (scope) => scope.type === 'module'
         )
         for (const variable of (moduleScope && moduleScope.variables) || []) {
-          scriptVariableNames.add(variable.name)
+          if (
+            // Check for type definitions. e.g. type Foo = {}
+            (variable.isTypeVariable && !variable.isValueVariable) ||
+            // type-only import seems to have isValueVariable set to true. So we need to check the actual Node.
+            (variable.defs.length > 0 &&
+              variable.defs.every((def) => {
+                if (def.type !== 'ImportBinding') {
+                  return false
+                }
+                if (def.parent.importKind === 'type') {
+                  // check for `import type Foo from './xxx'`
+                  return true
+                }
+                if (
+                  def.node.type === 'ImportSpecifier' &&
+                  def.node.importKind === 'type'
+                ) {
+                  // check for `import { type Foo } from './xxx'`
+                  return true
+                }
+                return false
+              }))
+          ) {
+            scriptTypeOnlyNames.add(variable.name)
+          } else {
+            scriptVariableNames.add(variable.name)
+          }
         }
       }
       /**
@@ -167,7 +196,7 @@ module.exports = {
 
         context.report({
           node: reportNode,
-          messageId: 'undef',
+          messageId: scriptTypeOnlyNames.has(rawName) ? 'typeOnly' : 'undef',
           data: {
             name: rawName
           }
diff --git a/lib/rules/padding-line-between-tags.js b/lib/rules/padding-line-between-tags.js
new file mode 100644
index 00000000..3e0994a1
--- /dev/null
+++ b/lib/rules/padding-line-between-tags.js
@@ -0,0 +1,189 @@
+/**
+ * @author dev1437
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+/**
+ * Split the source code into multiple lines based on the line delimiters.
+ * Copied from padding-line-between-blocks
+ * @param {string} text Source code as a string.
+ * @returns {string[]} Array of source code lines.
+ */
+function splitLines(text) {
+  return text.split(/\r\n|[\r\n\u2028\u2029]/gu)
+}
+
+/**
+ * @param {RuleContext} context
+ * @param {VElement} tag
+ * @param {VElement} sibling
+ */
+function insertNewLine(context, tag, sibling) {
+  context.report({
+    messageId: 'always',
+    loc: sibling.loc,
+    // @ts-ignore
+    fix(fixer) {
+      return fixer.insertTextAfter(tag, '\n')
+    }
+  })
+}
+
+/**
+ * @param {RuleContext} context
+ * @param {VEndTag | VStartTag} endTag
+ * @param {VElement} sibling
+ */
+function removeExcessLines(context, endTag, sibling) {
+  context.report({
+    messageId: 'never',
+    loc: sibling.loc,
+    // @ts-ignore
+    fix(fixer) {
+      const start = endTag.range[1]
+      const end = sibling.range[0]
+      const paddingText = context.getSourceCode().text.slice(start, end)
+      const textBetween = splitLines(paddingText)
+      let newTextBetween = `\n${textBetween.pop()}`
+      for (let i = textBetween.length - 1; i >= 0; i--) {
+        if (!/^\s*$/.test(textBetween[i])) {
+          newTextBetween = `${i === 0 ? '' : '\n'}${
+            textBetween[i]
+          }${newTextBetween}`
+        }
+      }
+      return fixer.replaceTextRange([start, end], `${newTextBetween}`)
+    }
+  })
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+/**
+ * @param {RuleContext} context
+ */
+function checkNewline(context) {
+  /** @type {Array<{blankLine: "always" | "never", prev: string, next: string}>} */
+  const configureList = context.options[0] || [
+    { blankLine: 'always', prev: '*', next: '*' }
+  ]
+
+  /**
+   * @param {VElement} block
+   */
+  return (block) => {
+    if (!block.parent.parent) {
+      return
+    }
+
+    const endTag = block.endTag || block.startTag
+    const lowerSiblings = block.parent.children
+      .filter(
+        (element) =>
+          element.type === 'VElement' && element.range !== block.range
+      )
+      .filter((sibling) => sibling.range[0] - endTag.range[1] >= 0)
+
+    if (lowerSiblings.length === 0) {
+      return
+    }
+
+    const closestSibling = /** @type {VElement} */ (lowerSiblings[0])
+
+    for (let i = configureList.length - 1; i >= 0; --i) {
+      const configure = configureList[i]
+      const matched =
+        (configure.prev === '*' || block.name === configure.prev) &&
+        (configure.next === '*' || closestSibling.name === configure.next)
+
+      if (matched) {
+        const lineDifference =
+          closestSibling.loc.start.line - endTag.loc.end.line
+        if (configure.blankLine === 'always') {
+          if (lineDifference === 1) {
+            insertNewLine(context, block, closestSibling)
+          } else if (lineDifference === 0) {
+            context.report({
+              messageId: 'always',
+              loc: closestSibling.loc,
+              // @ts-ignore
+              fix(fixer) {
+                const lastSpaces = /** @type {RegExpExecArray} */ (
+                  /^\s*/.exec(
+                    context.getSourceCode().lines[endTag.loc.start.line - 1]
+                  )
+                )[0]
+
+                return fixer.insertTextAfter(endTag, `\n\n${lastSpaces}`)
+              }
+            })
+          }
+        } else {
+          if (lineDifference > 1) {
+            let hasOnlyTextBetween = true
+            for (
+              let i = endTag.loc.start.line;
+              i < closestSibling.loc.start.line - 1 && hasOnlyTextBetween;
+              i++
+            ) {
+              hasOnlyTextBetween = !/^\s*$/.test(
+                context.getSourceCode().lines[i]
+              )
+            }
+            if (!hasOnlyTextBetween) {
+              removeExcessLines(context, endTag, closestSibling)
+            }
+          }
+        }
+        break
+      }
+    }
+  }
+}
+
+module.exports = {
+  meta: {
+    type: 'layout',
+    docs: {
+      description:
+        'require or disallow newlines between sibling tags in template',
+      categories: undefined,
+      url: 'https://eslint.vuejs.org/rules/padding-line-between-tags.html'
+    },
+    fixable: 'whitespace',
+    schema: [
+      {
+        type: 'array',
+        items: {
+          type: 'object',
+          properties: {
+            blankLine: { enum: ['always', 'never'] },
+            prev: { type: 'string' },
+            next: { type: 'string' }
+          },
+          additionalProperties: false,
+          required: ['blankLine', 'prev', 'next']
+        }
+      }
+    ],
+    messages: {
+      never: 'Unexpected blank line before this tag.',
+      always: 'Expected blank line before this tag.'
+    }
+  },
+  /** @param {RuleContext} context */
+  create(context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      VElement: checkNewline(context)
+    })
+  }
+}
diff --git a/package.json b/package.json
index 69b588fc..6fa02bc8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-vue",
-  "version": "9.4.0",
+  "version": "9.5.1",
   "description": "Official ESLint plugin for Vue.js",
   "main": "lib/index.js",
   "scripts": {
diff --git a/tsconfig.json b/tsconfig.json
index 40f2ceee..cbaefbd8 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
 {
   "compilerOptions": {
-    "target": "ES2020",
+    "target": "ES2019",
     "module": "commonjs",
     "lib": ["es2020"],
     "allowJs": true,
diff --git a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts
index 16a734d2..b3d5b6e2 100644
--- a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts
+++ b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts
@@ -268,11 +268,13 @@ export interface ImportDeclaration extends HasParentNode {
     | ImportNamespaceSpecifier
   )[]
   source: Literal & { value: string }
+  importKind?: 'type' | 'value'
 }
 export interface ImportSpecifier extends HasParentNode {
   type: 'ImportSpecifier'
   imported: Identifier
   local: Identifier
+  importKind?: 'type' | 'value'
 }
 export interface ImportDefaultSpecifier extends HasParentNode {
   type: 'ImportDefaultSpecifier'
diff --git a/typings/eslint/index.d.ts b/typings/eslint/index.d.ts
index 7bfda024..949d56f6 100644
--- a/typings/eslint/index.d.ts
+++ b/typings/eslint/index.d.ts
@@ -54,6 +54,9 @@ export namespace Scope {
     eslintExplicitGlobal?: boolean | undefined
     eslintExplicitGlobalComments?: Comment[] | undefined
     eslintImplicitGlobalSetting?: 'readonly' | 'writable' | undefined
+
+    isTypeVariable?: boolean
+    isValueVariable?: boolean
   }
   interface Reference {
     identifier: VAST.Identifier


Test patch:
diff --git a/tests/lib/rules/define-emits-declaration.js b/tests/lib/rules/define-emits-declaration.js
new file mode 100644
index 00000000..279d8049
--- /dev/null
+++ b/tests/lib/rules/define-emits-declaration.js
@@ -0,0 +1,151 @@
+/**
+ * @author Amorites
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/define-emits-declaration')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: {
+    ecmaVersion: 2020,
+    sourceType: 'module'
+  }
+})
+
+tester.run('define-emits-declaration', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup>
+          const emit = defineEmits(['change', 'update'])
+        </script>
+       `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+        const emit = defineEmits<{
+          (e: 'change', id: number): void
+          (e: 'update', value: string): void
+        }>()
+        </script>
+       `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+        const emit = defineEmits<{
+          (e: 'change', id: number): void
+          (e: 'update', value: string): void
+        }>()
+        </script>
+       `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      options: ['type-based']
+    },
+    {
+      filename: 'test.vue',
+      code: `
+       <script setup lang="ts">
+       const emit = defineEmits(['change', 'update'])
+       </script>
+       `,
+      options: ['runtime']
+    },
+    {
+      filename: 'test.vue',
+      // ignore code without defineEmits
+      code: `
+        <script setup lang="ts">
+        const props = defineProps({
+          kind: { type: String },
+        })
+        </script>
+       `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    },
+    {
+      filename: 'test.vue',
+      code: `
+         <script lang="ts">
+         import { PropType } from 'vue'
+ 
+         export default {
+           props: {
+             kind: { type: String as PropType<'primary' | 'secondary'> },
+           },
+           emits: ['check']
+         }
+         </script>
+       `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+       <script setup lang="ts">
+       const emit = defineEmits(['change', 'update'])
+       </script>
+       `,
+      errors: [
+        {
+          message: 'Use type-based declaration instead of runtime declaration.',
+          line: 3
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+       <script setup lang="ts">
+       const emit = defineEmits(['change', 'update'])
+       </script>
+       `,
+      errors: [
+        {
+          message: 'Use type-based declaration instead of runtime declaration.',
+          line: 3
+        }
+      ],
+      options: ['type-based']
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+        const emit = defineEmits<{
+          (e: 'change', id: number): void
+          (e: 'update', value: string): void
+        }>()
+        </script>
+       `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Use runtime declaration instead of type-based declaration.',
+          line: 3
+        }
+      ],
+      options: ['runtime']
+    }
+  ]
+})
diff --git a/tests/lib/rules/define-props-declaration.js b/tests/lib/rules/define-props-declaration.js
new file mode 100644
index 00000000..67ea338e
--- /dev/null
+++ b/tests/lib/rules/define-props-declaration.js
@@ -0,0 +1,157 @@
+/**
+ * @author Amorites
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/define-props-declaration')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: {
+    ecmaVersion: 2020,
+    sourceType: 'module'
+  }
+})
+
+tester.run('define-props-declaration', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup lang="ts">
+      const props = defineProps<{
+        kind: string;
+      }>()
+      </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup lang="ts">
+      const props = defineProps<{
+        kind: string;
+      }>()
+      </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      options: ['type-based']
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup lang="ts">
+      const props = defineProps({
+        kind: { type: String },
+      })
+      </script>
+      `,
+      options: ['runtime']
+    },
+    {
+      filename: 'test.vue',
+      // ignore script without lang="ts"
+      code: `
+      <script setup>
+      const props = defineProps({
+        kind: { type: String },
+      })
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup lang="ts">
+      const emit = defineEmits({
+        click: (event: PointerEvent) => !!event
+      })
+      </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    },
+    {
+      filename: 'test.vue',
+      // ignore non-setup script
+      code: `
+        <script lang="ts">
+        import { PropType } from 'vue'
+
+        export default {
+          props: {
+            kind: { type: String as PropType<'primary' | 'secondary'> },
+          },
+          emits: ['check']
+        }
+        </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup lang="ts">
+      const props = defineProps({
+        kind: { type: String },
+      })
+      </script>
+      `,
+      errors: [
+        {
+          message: 'Use type-based declaration instead of runtime declaration.',
+          line: 3
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup lang="ts">
+      const props = defineProps({
+        kind: { type: String },
+      })
+      </script>
+      `,
+      errors: [
+        {
+          message: 'Use type-based declaration instead of runtime declaration.',
+          line: 3
+        }
+      ],
+      options: ['type-based']
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup lang="ts">
+      const props = defineProps<{
+        kind: string;
+      }>()
+      </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      options: ['runtime'],
+      errors: [
+        {
+          message: 'Use runtime declaration instead of type-based declaration.',
+          line: 3
+        }
+      ]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-required-prop-with-default.js b/tests/lib/rules/no-required-prop-with-default.js
new file mode 100644
index 00000000..236de8cd
--- /dev/null
+++ b/tests/lib/rules/no-required-prop-with-default.js
@@ -0,0 +1,922 @@
+/**
+ * @author neferqiqi
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/no-required-prop-with-default')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: {
+    ecmaVersion: 2020,
+    sourceType: 'module'
+  }
+})
+
+tester.run('no-required-prop-with-default', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            name?: string
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          type TestPropType = {
+            name?: string
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            name?
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            get name(): string
+            set name(a: string)
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              'name': 'World',
+            }
+          );
+        </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            get name(): void
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              'name': 'World',
+            }
+          );
+        </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          const [name] = 'test'
+
+          interface TestPropType {
+            [name]: string
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              [name]: 'World'
+            }
+          );
+        </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      }
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script>
+        export default {
+          props: {
+            name: {
+              required: false,
+              default: 'Hello'
+            }
+          }
+        }
+        </script>
+      `
+    },
+    // ignore array prop
+    {
+      filename: 'test.vue',
+      code: `
+        <script>
+        export default {
+          props: ['name']
+        }
+        </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup>
+          const props = defineProps({
+            name: {
+              required: false,
+              default: 'Hello'
+            }
+          })
+        </script>
+      `
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            name: string
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            name?: string
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            name: string | number
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            name?: string | number
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            'na::me': string
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              'na::me': "World",
+            }
+          );
+        </script>
+      `,
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            'na::me'?: string
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              'na::me': "World",
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "na::me" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          import nameType from 'name.ts';
+          interface TestPropType {
+            name: nameType
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      output: `
+        <script setup lang="ts">
+          import nameType from 'name.ts';
+          interface TestPropType {
+            name?: nameType
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            name
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            name?
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            name
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            name?
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: "World",
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            'na\\"me2'
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              'na\\"me2': "World",
+            }
+          );
+        </script>
+      `,
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            'na\\"me2'?
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              'na\\"me2': "World",
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "na"me2" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            foo(): void
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              foo() {console.log(123)},
+            }
+          );
+        </script>
+      `,
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            foo?(): void
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              foo() {console.log(123)},
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "foo" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            readonly foo(): void
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              foo() {console.log(123)},
+            }
+          );
+        </script>
+      `,
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            readonly foo?(): void
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              foo() {console.log(123)},
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "foo" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            readonly name
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: 'World',
+            }
+          );
+        </script>
+      `,
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            readonly name?
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              name: 'World',
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            readonly 'name'
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              'name': 'World',
+            }
+          );
+        </script>
+      `,
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            readonly 'name'?
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              'name': 'World',
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            readonly 'a'
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              '\\u0061': 'World',
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            readonly 'a'?
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              '\\u0061': 'World',
+            }
+          );
+        </script>
+      `,
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "a" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup lang="ts">
+          interface TestPropType {
+            readonly '\\u0061'
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              'a': 'World',
+            }
+          );
+        </script>
+      `,
+      output: `
+        <script setup lang="ts">
+          interface TestPropType {
+            readonly '\\u0061'?
+            age?: number
+          }
+          const props = withDefaults(
+            defineProps<TestPropType>(),
+            {
+              'a': 'World',
+            }
+          );
+        </script>
+      `,
+      options: [{ autofix: true }],
+      parserOptions: {
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      errors: [
+        {
+          message: 'Prop "a" should be optional.',
+          line: 4
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script>
+        export default {
+          props: {
+            name: {
+              required: true,
+              default: 'Hello'
+            }
+          }
+        }
+        </script>
+      `,
+      output: `
+        <script>
+        export default {
+          props: {
+            name: {
+              required: false,
+              default: 'Hello'
+            }
+          }
+        }
+        </script>
+      `,
+      options: [{ autofix: true }],
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script>
+        export default {
+          props: {
+            'name': {
+              required: true,
+              default: 'Hello'
+            }
+          }
+        }
+        </script>
+      `,
+      output: `
+        <script>
+        export default {
+          props: {
+            'name': {
+              required: false,
+              default: 'Hello'
+            }
+          }
+        }
+        </script>
+      `,
+      options: [{ autofix: true }],
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script>
+        import { defineComponent } from 'vue'
+        export default defineComponent({
+          props: {
+            'name': {
+              required: true,
+              default: 'Hello'
+            }
+          }
+        })
+        </script>
+      `,
+      output: `
+        <script>
+        import { defineComponent } from 'vue'
+        export default defineComponent({
+          props: {
+            'name': {
+              required: false,
+              default: 'Hello'
+            }
+          }
+        })
+        </script>
+      `,
+      options: [{ autofix: true }],
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 6
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script>
+        import { defineComponent } from 'vue'
+        export default defineComponent({
+          props: {
+            name: {
+              required: true,
+              default: 'Hello'
+            }
+          }
+        })
+        </script>
+      `,
+      output: `
+        <script>
+        import { defineComponent } from 'vue'
+        export default defineComponent({
+          props: {
+            name: {
+              required: false,
+              default: 'Hello'
+            }
+          }
+        })
+        </script>
+      `,
+      options: [{ autofix: true }],
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 6
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script>
+        import { defineComponent } from 'vue'
+        export default defineComponent({
+          props: {
+            name: {
+              required: true,
+              default: 'Hello'
+            }
+          }
+        })
+        </script>
+      `,
+      output: `
+        <script>
+        import { defineComponent } from 'vue'
+        export default defineComponent({
+          props: {
+            name: {
+              required: true,
+              default: 'Hello'
+            }
+          }
+        })
+        </script>
+      `,
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 6
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        <script setup>
+          const props = defineProps({
+            name: {
+              required: true,
+              default: 'Hello'
+            }
+          })
+        </script>
+      `,
+      output: `
+        <script setup>
+          const props = defineProps({
+            name: {
+              required: false,
+              default: 'Hello'
+            }
+          })
+        </script>
+      `,
+      options: [{ autofix: true }],
+      errors: [
+        {
+          message: 'Prop "name" should be optional.',
+          line: 4
+        }
+      ]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-undef-components.js b/tests/lib/rules/no-undef-components.js
index b3a7af10..5af9468f 100644
--- a/tests/lib/rules/no-undef-components.js
+++ b/tests/lib/rules/no-undef-components.js
@@ -6,6 +6,7 @@
 
 const RuleTester = require('eslint').RuleTester
 const rule = require('../../../lib/rules/no-undef-components')
+const semver = require('semver')
 
 const tester = new RuleTester({
   parser: require.resolve('vue-eslint-parser'),
@@ -598,6 +599,26 @@ tester.run('no-undef-components', rule, {
       <script setup>
       import TheModal from 'foo'
       </script>`
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup lang="ts">
+        import Foo from './Foo.vue'
+        import {HelloWorld1} from './components/HelloWorld'
+      </script>
+
+      <template>
+        <Foo />
+        <HelloWorld1 />
+      </template>
+      `,
+      parserOptions: {
+        ecmaVersion: 6,
+        sourceType: 'module',
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      parser: require.resolve('vue-eslint-parser')
     }
   ],
   invalid: [
@@ -661,6 +682,98 @@ tester.run('no-undef-components', rule, {
         }
       ]
     },
+    ...(semver.gte(
+      require('@typescript-eslint/parser/package.json').version,
+      '5.0.0'
+    )
+      ? [
+          {
+            filename: 'test.vue',
+            code: `
+      <script setup lang="ts">
+        import type Foo from './Foo.vue'
+        import type {HelloWorld1} from './components/HelloWorld'
+        import { type HelloWorld2 } from './components/HelloWorld2'
+        import type {HelloWorld as HelloWorld3} from './components/HelloWorld3'
+        import { type HelloWorld as HelloWorld4 } from './components/HelloWorld4';
+        import { type default as HelloWorld5 } from './components/HelloWorld5';
+      </script>
+
+      <template>
+        <Foo />
+        <HelloWorld1 />
+        <HelloWorld2 />
+        <HelloWorld3 />
+        <HelloWorld4 />
+        <HelloWorld5 />
+      </template>
+      `,
+            parserOptions: {
+              ecmaVersion: 6,
+              sourceType: 'module',
+              parser: require.resolve('@typescript-eslint/parser')
+            },
+            parser: require.resolve('vue-eslint-parser'),
+            errors: [
+              {
+                message:
+                  "The '<Foo>' component has been used, but 'Foo' only refers to a type.",
+                line: 12
+              },
+              {
+                message:
+                  "The '<HelloWorld1>' component has been used, but 'HelloWorld1' only refers to a type.",
+                line: 13
+              },
+              {
+                message:
+                  "The '<HelloWorld2>' component has been used, but 'HelloWorld2' only refers to a type.",
+                line: 14
+              },
+              {
+                message:
+                  "The '<HelloWorld3>' component has been used, but 'HelloWorld3' only refers to a type.",
+                line: 15
+              },
+              {
+                message:
+                  "The '<HelloWorld4>' component has been used, but 'HelloWorld4' only refers to a type.",
+                line: 16
+              },
+              {
+                message:
+                  "The '<HelloWorld5>' component has been used, but 'HelloWorld5' only refers to a type.",
+                line: 17
+              }
+            ]
+          }
+        ]
+      : []),
+    {
+      filename: 'test.vue',
+      code: `
+      <script setup lang="ts">
+        type Foo = {}
+      </script>
+
+      <template>
+        <Foo />
+      </template>
+      `,
+      parserOptions: {
+        ecmaVersion: 6,
+        sourceType: 'module',
+        parser: require.resolve('@typescript-eslint/parser')
+      },
+      parser: require.resolve('vue-eslint-parser'),
+      errors: [
+        {
+          message:
+            "The '<Foo>' component has been used, but 'Foo' only refers to a type.",
+          line: 7
+        }
+      ]
+    },
 
     // options API
     {
diff --git a/tests/lib/rules/padding-line-between-tags.js b/tests/lib/rules/padding-line-between-tags.js
new file mode 100644
index 00000000..18d6743a
--- /dev/null
+++ b/tests/lib/rules/padding-line-between-tags.js
@@ -0,0 +1,1032 @@
+/**
+ * @author dev1437
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/padding-line-between-tags')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: {
+    ecmaVersion: 2020,
+    sourceType: 'module'
+  }
+})
+
+tester.run('padding-line-between-tags', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <div></div>
+
+          <div>
+          </div>
+
+          <div />
+        </div>
+      </template>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <p>Foo <b>bar</b> baz.</p>
+      </template>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <div></div>
+          <div>
+          </div>
+          <div />
+        </div>
+      </template>
+      `,
+      options: [[{ blankLine: 'never', prev: '*', next: '*' }]]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <br />
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      options: [
+        [
+          { blankLine: 'always', prev: '*', next: '*' },
+          { blankLine: 'never', prev: '*', next: 'br' },
+          { blankLine: 'never', prev: 'br', next: '*' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+
+            <br />
+            <div></div>
+
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      options: [
+        [
+          { blankLine: 'always', prev: '*', next: '*' },
+          { blankLine: 'never', prev: 'br', next: 'div' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <br />
+
+            <div></div>
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      options: [
+        [
+          { blankLine: 'never', prev: '*', next: '*' },
+          { blankLine: 'always', prev: 'br', next: 'div' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <br />
+
+            <img />
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      options: [[{ blankLine: 'always', prev: 'br', next: 'img' }]]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+      </template>
+      <script>
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <br />
+
+            <img />
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      options: [
+        [
+          { blankLine: 'never', prev: 'br', next: 'img' },
+          { blankLine: 'always', prev: 'br', next: 'img' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          </div>A
+          B
+          C<div />
+        </div>
+      </template>
+      `,
+      options: [[{ blankLine: 'never', prev: '*', next: '*' }]]
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <div></div>
+          <div>
+          </div>
+          <div />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <div></div>
+
+          <div>
+          </div>
+
+          <div />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 5,
+          column: 11
+        },
+        {
+          message: 'Expected blank line before this tag.',
+          line: 7,
+          column: 11
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <li>
+            </li>
+            <br />
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+
+            <li>
+            </li>
+
+            <br />
+
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 7,
+          column: 13
+        },
+        {
+          message: 'Expected blank line before this tag.',
+          line: 9,
+          column: 13
+        },
+        {
+          message: 'Expected blank line before this tag.',
+          line: 10,
+          column: 13
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+          </ul>
+          <ul>
+            <li>
+              Test Text
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <ul>
+          </ul>
+
+          <ul>
+            <li>
+              Test Text
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 6,
+          column: 11
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+          </ul>
+          <ul>
+            <li>
+              <p>
+              </p>
+              <p>
+              </p>
+            </li>
+          </ul>
+        </div>
+        <div>
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <ul>
+          </ul>
+
+          <ul>
+            <li>
+              <p>
+              </p>
+
+              <p>
+              </p>
+            </li>
+          </ul>
+        </div>
+
+        <div>
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 6,
+          column: 11
+        },
+        {
+          message: 'Expected blank line before this tag.',
+          line: 10,
+          column: 15
+        },
+        {
+          message: 'Expected blank line before this tag.',
+          line: 15,
+          column: 9
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <br />
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+
+            <br />
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 7,
+          column: 13
+        }
+      ],
+      options: [
+        [
+          { blankLine: 'always', prev: '*', next: '*' },
+          { blankLine: 'never', prev: 'br', next: '*' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <br />
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <br />
+
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 8,
+          column: 13
+        }
+      ],
+      options: [
+        [
+          { blankLine: 'always', prev: '*', next: '*' },
+          { blankLine: 'never', prev: '*', next: 'br' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+
+
+            <br />
+
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <br />
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Unexpected blank line before this tag.',
+          line: 9,
+          column: 13
+        },
+        {
+          message: 'Unexpected blank line before this tag.',
+          line: 11,
+          column: 13
+        }
+      ],
+      options: [[{ blankLine: 'never', prev: '*', next: '*' }]]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <div></div>
+
+          <div>
+          </div>
+
+          <div />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <div></div>
+          <div>
+          </div>
+          <div />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Unexpected blank line before this tag.',
+          line: 6,
+          column: 11
+        },
+        {
+          message: 'Unexpected blank line before this tag.',
+          line: 9,
+          column: 11
+        }
+      ],
+      options: [[{ blankLine: 'never', prev: '*', next: '*' }]]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <br />
+            <div></div>
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <ul>
+            <li>
+            </li>
+            <br />
+
+            <div></div>
+            <li>
+            </li>
+          </ul>
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 8,
+          column: 13
+        }
+      ],
+      options: [
+        [
+          { blankLine: 'never', prev: '*', next: '*' },
+          { blankLine: 'always', prev: 'br', next: 'div' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <h1></h1>
+          <br />
+          <div></div>
+          <br />
+          <img />
+          <h1></h1>
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <h1></h1>
+
+          <br />
+          <div></div>
+
+          <br />
+          <img />
+
+          <h1></h1>
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 5,
+          column: 11
+        },
+        {
+          message: 'Expected blank line before this tag.',
+          line: 7,
+          column: 11
+        },
+        {
+          message: 'Expected blank line before this tag.',
+          line: 9,
+          column: 11
+        }
+      ],
+      options: [
+        [
+          { blankLine: 'always', prev: '*', next: '*' },
+          { blankLine: 'never', prev: 'br', next: 'div' },
+          { blankLine: 'never', prev: 'br', next: 'img' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <img />
+          <br />
+
+          <div></div>
+          <br />
+          <ul></ul>
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <img />
+          <br />
+
+          <div></div>
+
+          <br />
+          <ul></ul>
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 8,
+          column: 11
+        }
+      ],
+      options: [
+        [
+          { blankLine: 'always', prev: 'br', next: 'div' },
+          { blankLine: 'always', prev: 'div', next: 'br' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <img />
+          <br />
+
+          <div></div>
+          <br />
+          <br />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <img />
+          <br />
+
+          <div></div>
+          <br />
+
+          <br />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 9,
+          column: 11
+        }
+      ],
+      options: [
+        [
+          { blankLine: 'always', prev: 'br', next: 'div' },
+          { blankLine: 'always', prev: 'br', next: 'br' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <img />
+          <br />
+
+          <div></div>
+          <br />
+
+          <br />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <img />
+
+          <br />
+
+          <div></div>
+
+          <br />
+          <br />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 5,
+          column: 11
+        },
+        {
+          message: 'Expected blank line before this tag.',
+          line: 8,
+          column: 11
+        },
+        {
+          message: 'Unexpected blank line before this tag.',
+          line: 10,
+          column: 11
+        }
+      ],
+      options: [
+        [
+          { blankLine: 'always', prev: '*', next: '*' },
+          { blankLine: 'never', prev: 'br', next: 'br' }
+        ]
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <img />
+          <br />
+
+          <div></div>
+
+          <br />
+
+          <br />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <img />
+          <br />
+
+          <div></div>
+
+          <br />
+          <br />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Unexpected blank line before this tag.',
+          line: 11,
+          column: 11
+        }
+      ],
+      options: [[{ blankLine: 'never', prev: 'br', next: 'br' }]]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <div></div>
+          <div>
+          </div>
+          <br />
+          <div />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <div></div>
+          <div>
+          </div>
+
+          <br />
+          <div />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 7,
+          column: 11
+        }
+      ],
+      options: [[{ blankLine: 'always', prev: '*', next: 'br' }]]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          <div /><div />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          <div />
+
+          <div />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 4,
+          column: 18
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          Foo  <div /><div />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          Foo  <div />
+
+          <div />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Expected blank line before this tag.',
+          line: 4,
+          column: 23
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          </div>A
+
+          C<div />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          </div>A
+          C<div />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Unexpected blank line before this tag.',
+          line: 6,
+          column: 12
+        }
+      ],
+      options: [[{ blankLine: 'never', prev: '*', next: '*' }]]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          </div>A
+          B
+
+          C<div />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          </div>A
+          B
+          C<div />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Unexpected blank line before this tag.',
+          line: 7,
+          column: 12
+        }
+      ],
+      options: [[{ blankLine: 'never', prev: '*', next: '*' }]]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          </div>A
+
+
+          B
+          C<div />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          </div>A
+          B
+          C<div />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Unexpected blank line before this tag.',
+          line: 8,
+          column: 12
+        }
+      ],
+      options: [[{ blankLine: 'never', prev: '*', next: '*' }]]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <template>
+        <div>
+          </div>A
+
+
+          B
+
+
+          C<div />
+        </div>
+      </template>
+      `,
+      output: `
+      <template>
+        <div>
+          </div>A
+          B
+          C<div />
+        </div>
+      </template>
+      `,
+      errors: [
+        {
+          message: 'Unexpected blank line before this tag.',
+          line: 10,
+          column: 12
+        }
+      ],
+      options: [[{ blankLine: 'never', prev: '*', next: '*' }]]
+    }
+  ]
+})