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 @@ -267,6 +267,7 @@ For example:
| [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: |
| [vue/require-default-export](./require-default-export.md) | require components to be the default export | | :warning: |
| [vue/require-direct-export](./require-direct-export.md) | require the component to be directly exported | | :hammer: |
| [vue/require-emit-validator](./require-emit-validator.md) | require type definitions in emits | :bulb: | :hammer: |
| [vue/require-explicit-slots](./require-explicit-slots.md) | require slots to be explicitly defined | | :warning: |
Expand Down
57 changes: 57 additions & 0 deletions docs/rules/require-default-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/require-default-export
description: require components to be the default export
---

# vue/require-default-export

> require components to be the default export

- :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 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.

## :couple: Related Rules

- [vue/one-component-per-file](./one-component-per-file.md)

## :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/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 components to be the default export',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/require-default-export.html'
},
fixable: null,
schema: [],
messages: {
mustDefaultExport: 'The component must be the 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: 'mustDefaultExport'
})
}
}
}
}
}
156 changes: 156 additions & 0 deletions tests/lib/rules/require-default-export.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/**
* @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>
`
},
{
filename: 'test.js',
code: `
const foo = 'foo';
export const bar = 'bar';
`
},
{
filename: 'test.js',
code: `
import {defineComponent} from 'vue';
defineComponent({});
`
}
],
invalid: [
{
filename: 'test.vue',
code: `
<script>
const foo = 'foo';
</script>
`,
errors: [
{
messageId: 'mustDefaultExport',
line: 1
}
]
},
{
filename: 'test.vue',
code: `
<script>
export const foo = 'foo';
</script>
`,
errors: [
{
messageId: 'mustDefaultExport',
line: 1
}
]
},
{
filename: 'test.vue',
code: `
<script>
const foo = 'foo';

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

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