Skip to content

Add vue/require-default-export rule #2494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 13, 2024
1 change: 1 addition & 0 deletions docs/rules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ Rules in this category are enabled for all presets provided by eslint-plugin-vue
| [vue/no-watch-after-await](./no-watch-after-await.md) | disallow asynchronously registered `watch` | | :three::hammer: |
| [vue/prefer-import-from-vue](./prefer-import-from-vue.md) | enforce import from 'vue' instead of import from '@vue/*' | :wrench: | :three::hammer: |
| [vue/require-component-is](./require-component-is.md) | require `v-bind:is` of `<component>` elements | | :three::two::warning: |
| [vue/require-default-export](./require-default-export.md) | require default export | | :three::two::warning: |
| [vue/require-prop-type-constructor](./require-prop-type-constructor.md) | require prop type to be a constructor | :wrench: | :three::two::hammer: |
| [vue/require-render-return](./require-render-return.md) | enforce render function to always return value | | :three::two::warning: |
| [vue/require-slots-as-functions](./require-slots-as-functions.md) | enforce properties of `$slots` to be used as a function | | :three::warning: |
Expand Down
2 changes: 1 addition & 1 deletion docs/rules/object-curly-newline.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v7.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/object-curly-newline.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/object-curly-newline.js)

<sup>Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/js/object-curly-newline)</sup>
<sup>Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/object-curly-newline)</sup>
2 changes: 1 addition & 1 deletion docs/rules/object-property-newline.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ This rule was introduced in eslint-plugin-vue v7.0.0
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/object-property-newline.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/object-property-newline.js)

<sup>Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/js/object-property-newline)</sup>
<sup>Taken with ❤️ [from ESLint Stylistic](https://eslint.style/rules/ts/object-property-newline)</sup>
54 changes: 54 additions & 0 deletions docs/rules/require-default-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/require-default-export
description: require default export
---

# vue/require-default-export

> require default export

- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> _**This rule has not been released yet.**_ </badge>
- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `*.configs["flat/essential"]`, `"plugin:vue/essential"`, `*.configs["flat/vue2-essential"]`, `"plugin:vue/vue3-strongly-recommended"`, `*.configs["flat/strongly-recommended"]`, `"plugin:vue/strongly-recommended"`, `*.configs["flat/vue2-strongly-recommended"]`, `"plugin:vue/vue3-recommended"`, `*.configs["flat/recommended"]`, `"plugin:vue/recommended"` and `*.configs["flat/vue2-recommended"]`.

## :book: Rule Details

This rule reports when a Vue component does not have a default export, if the component is not defined as `<script setup>`.

<eslint-code-block :rules="{'vue/require-default-export': ['error']}">

```vue
<!-- ✗ BAD -->
<script>
const foo = 'foo';
</script>
```

</eslint-code-block>

<eslint-code-block :rules="{'vue/require-default-export': ['error']}">

```vue
<!-- ✓ GOOD -->
<script>
export default {
data() {
return {
foo: 'foo'
};
}
};
</script>
```

</eslint-code-block>

## :wrench: Options

Nothing.

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-default-export.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-default-export.js)
1 change: 1 addition & 0 deletions lib/configs/flat/vue2-essential.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ module.exports = [
'vue/no-v-model-argument': 'error',
'vue/no-v-text-v-html-on-component': 'error',
'vue/require-component-is': 'error',
'vue/require-default-export': 'error',
'vue/require-prop-type-constructor': 'error',
'vue/require-render-return': 'error',
'vue/require-v-for-key': 'error',
Expand Down
1 change: 1 addition & 0 deletions lib/configs/flat/vue3-essential.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ module.exports = [
'vue/no-watch-after-await': 'error',
'vue/prefer-import-from-vue': 'error',
'vue/require-component-is': 'error',
'vue/require-default-export': 'error',
'vue/require-prop-type-constructor': 'error',
'vue/require-render-return': 'error',
'vue/require-slots-as-functions': 'error',
Expand Down
1 change: 1 addition & 0 deletions lib/configs/vue2-essential.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module.exports = {
'vue/no-v-model-argument': 'error',
'vue/no-v-text-v-html-on-component': 'error',
'vue/require-component-is': 'error',
'vue/require-default-export': 'error',
'vue/require-prop-type-constructor': 'error',
'vue/require-render-return': 'error',
'vue/require-v-for-key': 'error',
Expand Down
1 change: 1 addition & 0 deletions lib/configs/vue3-essential.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ module.exports = {
'vue/no-watch-after-await': 'error',
'vue/prefer-import-from-vue': 'error',
'vue/require-component-is': 'error',
'vue/require-default-export': 'error',
'vue/require-prop-type-constructor': 'error',
'vue/require-render-return': 'error',
'vue/require-slots-as-functions': 'error',
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@
'prop-name-casing': require('./rules/prop-name-casing'),
'quote-props': require('./rules/quote-props'),
'require-component-is': require('./rules/require-component-is'),
'require-default-export': require('./rules/require-default-export'),
'require-default-prop': require('./rules/require-default-prop'),
'require-direct-export': require('./rules/require-direct-export'),
'require-emit-validator': require('./rules/require-emit-validator'),
Expand Down Expand Up @@ -277,7 +278,7 @@
vue: require('./processor')
},
environments: {
// TODO Remove in the next major version

Check warning on line 281 in lib/index.js

View workflow job for this annotation

GitHub Actions / Lint

Unexpected 'todo' comment: 'TODO Remove in the next major version'
/** @deprecated */
'setup-compiler-macros': {
globals: {
Expand Down
58 changes: 58 additions & 0 deletions lib/rules/require-default-export.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @author ItMaga
* See LICENSE file in root directory for full license.
*/
'use strict'

const utils = require('../utils')

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'require default export',
categories: ['vue3-essential', 'vue2-essential'],
url: 'https://eslint.vuejs.org/rules/require-default-export.html'
},
fixable: null,
schema: [],
messages: {
missing: 'Missing default export.'
}
},
/** @param {RuleContext} context */
create(context) {
const sourceCode = context.getSourceCode()
const documentFragment = sourceCode.parserServices.getDocumentFragment?.()

const hasScript =
documentFragment &&
documentFragment.children.some(
(e) => utils.isVElement(e) && e.name === 'script'
)

if (utils.isScriptSetup(context) || !hasScript) {
return {}
}

let hasDefaultExport = false

return {
'Program > ExportDefaultDeclaration'() {
hasDefaultExport = true
},

/**
* @param {Program} node
*/
'Program:exit'(node) {
if (!hasDefaultExport && node.body.length > 0) {
context.report({
loc: { line: 1, column: 0 },
messageId: 'missing'
})
}
}
}
}
}
142 changes: 142 additions & 0 deletions tests/lib/rules/require-default-export.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* @author ItMaga
* See LICENSE file in root directory for full license.
*/
'use strict'

const RuleTester = require('../../eslint-compat').RuleTester
const rule = require('../../../lib/rules/require-default-export')

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

tester.run('require-default-export', rule, {
valid: [
{
filename: 'test.vue',
code: `
<template>Without script</template>
`
},
{
filename: 'test.vue',
code: `
<script>
import { ref } from 'vue';

export default {}
</script>
`
},
{
filename: 'test.vue',
code: `
<script setup>
const foo = 'foo';
</script>
`
},
{
filename: 'test.vue',
code: `
<script>
const component = {};

export default component;
</script>
`
},
{
filename: 'test.vue',
code: `
<script>
import {defineComponent} from 'vue';

export default defineComponent({});
</script>
`
}
],
invalid: [
{
filename: 'test.vue',
code: `
<script>
const foo = 'foo';
</script>
`,
errors: [
{
messageId: 'missing',
line: 1
}
]
},
{
filename: 'test.vue',
code: `
<script>
export const foo = 'foo';
</script>
`,
errors: [
{
messageId: 'missing',
line: 1
}
]
},
{
filename: 'test.vue',
code: `
<script>
const foo = 'foo';

export { foo };
</script>
`,
errors: [
{
messageId: 'missing',
line: 1
}
]
},
{
filename: 'test.vue',
code: `
<script>
export const foo = 'foo';
export const bar = 'bar';
</script>
`,
errors: [
{
messageId: 'missing',
line: 1
}
]
},
{
filename: 'test.vue',
code: `
<script>
import { defineComponent } from 'vue';

export const component = defineComponent({});
</script>
`,
errors: [
{
messageId: 'missing',
line: 1
}
]
}
]
})
Loading