ESLint Plugin for Vue Composables
This ESLint plugin provides a set of rules specifically designed to enforce best practices and prevent common pitfalls when working with Vue 3 composables and lifecycle hooks. It ensures that composable functions (prefixed with `use`) and lifecycle hook calls (like `onMounted`, `onBeforeUnmount`, etc.) are correctly placed within `setup()` functions, other composables, or Vue's `<script setup>` block. The plugin, currently at version 1.0.0, aims to help developers maintain proper reactivity contexts and avoid issues like calling hooks or composables after `await` expressions in incorrect scopes. It supports both modern ESLint flat configurations (`eslint.config.js`) and legacy `.eslintrc` formats, providing flexibility for different project setups. Its release cadence is likely tied to the evolving best practices for Vue composables, offering focused linting compared to general Vue ESLint plugins.
Common errors
-
ESLint: Composable call must be placed in `setup()` or another composable, not after an `await` expression. (vue-composable/composable-placement)
cause A Vue composable function (e.g., `useSomething()`) was called after an `await` statement in a context other than Vue's `<script setup>`.fixMove the composable call before the `await` expression, or refactor the code to use `<script setup>` if suitable for async operations, or ensure the call is within a valid composable context. -
ESLint: Lifecycle hook call must be placed in `setup()` or another composable, not after an `await` expression. (vue-composable/lifecycle-placement)
cause A Vue lifecycle hook (e.g., `onMounted()`) was invoked after an `await` statement in a context other than Vue's `<script setup>`.fixEnsure all lifecycle hook calls are made before any `await` expressions within `setup()` or other composables, or leverage `<script setup>`'s async capabilities where appropriate. -
ESLint: Composable 'useBar' called in non-composable function 'foo'. (vue-composable/composable-placement)
cause A function recognized as a composable (due to its `use` prefix) was called from a regular JavaScript function that is not itself a composable or `setup()` function.fixRefactor the calling function to be a composable (start its name with `use`) or move the composable call into `setup()` or an existing composable.
Warnings
- gotcha Incorrect placement of composable functions or lifecycle hooks can lead to reactivity issues or unintended behavior. Rules like `composable-placement` and `lifecycle-placement` explicitly warn against calling them after `await` expressions outside of `<script setup>`.
- gotcha Mixing ESLint's legacy (`.eslintrc`) and flat (`eslint.config.js`) configuration formats can cause rules to not be applied correctly or lead to parsing errors. This plugin provides distinct setup instructions for each.
- gotcha Calling composable functions or lifecycle hooks within regular, non-composable JavaScript functions (i.e., functions not starting with `use` or not directly `setup()`) will trigger linting errors for rules like `composable-placement` and `lifecycle-placement`.
Install
-
npm install eslint-plugin-vue-composable -
yarn add eslint-plugin-vue-composable -
pnpm add eslint-plugin-vue-composable
Imports
- vueComposable
const vueComposable = require('eslint-plugin-vue-composable')import vueComposable from 'eslint-plugin-vue-composable'
- flat/recommended config
import vueComposable from 'eslint-plugin-vue-composable'; export default [...vueComposable.configs['flat/recommended']]
- Legacy .eslintrc config
{ "extends": ["plugin:vue-composable/recommended"] }
Quickstart
import vueComposable from 'eslint-plugin-vue-composable'
export default [
// Ensure ESLint is configured for Vue and TypeScript if applicable
// For example:
// ...require('@eslint/js').configs.recommended,
// ...pluginVue.configs['flat/recommended'],
// ...parserTs.configs.recommended,
// Add the vue-composable recommended rules
...vueComposable.configs['flat/recommended'],
// You can override specific rules or add custom configurations
{
rules: {
'vue-composable/lifecycle-placement': 'warn', // Change severity of a specific rule
'vue-composable/composable-placement': 'error' // Enforce composable placement strictly
}
},
// Define files to lint
{
files: ['**/*.vue', '**/*.ts', '**/*.js'],
languageOptions: {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
}
}
}
]