eslint-plugin-harlanzw
v0.1.0
Published
Harlan's opinionated ESLint rules
Readme
eslint-plugin-harlanzw
Harlan's ESLint rules for Vue projects with focus on link hygiene, Nuxt best practices, and Vue reactivity patterns.
Playground
Try the rules in action with a Nuxt ESLint interactive playground:
Rules
Note: These rules are experimental and may change. They will be submitted to the official Vue ESLint plugin for consideration.
The rules are organized into the following categories:
- Link Rules - Ensure link URLs are clean, accessible, and SEO-friendly
- Nuxt Rules - Best practices for Nuxt applications
- Vue Rules - Vue composition API and reactivity best practices
| Rule | Description |
| --- | --- |
| link-ascii-only | ensure link URLs contain only ASCII characters |
| link-lowercase | ensure link URLs do not contain uppercase characters |
| link-no-double-slashes | ensure link URLs do not contain consecutive slashes |
| link-no-whitespace | ensure link URLs do not contain whitespace characters |
| nuxt-await-navigate-to | enforce awaiting navigateTo() calls |
| nuxt-no-redundant-import-meta | disallow redundant import.meta.server or import.meta.client checks in scoped components |
| nuxt-no-side-effects-in-async-data-handler | disallow side effects in async data handlers |
| nuxt-no-side-effects-in-setup | disallow side effects in setup functions |
| nuxt-prefer-navigate-to-over-router-push-replace | prefer navigateTo() over router.push() or router.replace() |
| nuxt-prefer-nuxt-link-over-router-link | prefer NuxtLink over RouterLink |
| vue-no-faux-composables | stop fake composables that don't use Vue reactivity |
| vue-no-nested-reactivity | don't mix ref() and reactive() together |
| vue-no-passing-refs-as-props | don't pass refs as props - unwrap them first |
| vue-no-reactive-destructuring | avoid destructuring reactive objects |
| vue-no-ref-access-in-templates | don't use .value in Vue templates |
| vue-no-torefs-on-props | don't use toRefs() on the props object |
Installation
Install the plugin:
pnpm add -D eslint-plugin-harlanzwUsage
With @antfu/eslint-config
// eslint.config.js
import antfu from '@antfu/eslint-config'
import harlanzw from 'eslint-plugin-harlanzw'
export default antfu(
{
vue: true,
},
{
plugins: {
harlanzw
},
rules: {
'harlanzw/link-ascii-only': 'error',
'harlanzw/link-lowercase': 'error',
'harlanzw/link-no-double-slashes': 'error',
'harlanzw/link-no-whitespace': 'error',
'harlanzw/nuxt-await-navigate-to': 'error',
'harlanzw/nuxt-no-redundant-import-meta': 'error',
'harlanzw/nuxt-no-side-effects-in-async-data-handler': 'error',
'harlanzw/nuxt-no-side-effects-in-setup': 'error',
'harlanzw/nuxt-prefer-navigate-to-over-router-push-replace': 'error',
'harlanzw/nuxt-prefer-nuxt-link-over-router-link': 'error',
'harlanzw/vue-no-faux-composables': 'error',
'harlanzw/vue-no-nested-reactivity': 'error',
'harlanzw/vue-no-passing-refs-as-props': 'error',
'harlanzw/vue-no-reactive-destructuring': 'error',
'harlanzw/vue-no-ref-access-in-templates': 'error',
'harlanzw/vue-no-torefs-on-props': 'error'
}
}
)With Nuxt ESLint
// eslint.config.mjs
import harlanzw from 'eslint-plugin-harlanzw'
import withNuxt from './.nuxt/eslint.config.mjs'
export default withNuxt([{
plugins: {
harlanzw
},
rules: {
'harlanzw/link-ascii-only': 'error',
'harlanzw/link-lowercase': 'error',
'harlanzw/link-no-double-slashes': 'error',
'harlanzw/link-no-whitespace': 'error',
'harlanzw/nuxt-await-navigate-to': 'error',
'harlanzw/nuxt-no-redundant-import-meta': 'error',
'harlanzw/nuxt-no-side-effects-in-async-data-handler': 'error',
'harlanzw/nuxt-no-side-effects-in-setup': 'error',
'harlanzw/nuxt-prefer-navigate-to-over-router-push-replace': 'error',
'harlanzw/nuxt-prefer-nuxt-link-over-router-link': 'error',
'harlanzw/vue-no-faux-composables': 'error',
'harlanzw/vue-no-nested-reactivity': 'error',
'harlanzw/vue-no-passing-refs-as-props': 'error',
'harlanzw/vue-no-reactive-destructuring': 'error',
'harlanzw/vue-no-ref-access-in-templates': 'error',
'harlanzw/vue-no-torefs-on-props': 'error'
}
}])Standalone Usage
Add the plugin to your ESLint configuration:
// eslint.config.js
import harlanzw from 'eslint-plugin-harlanzw'
import vueParser from 'vue-eslint-parser'
export default [
{
files: ['**/*.vue'],
languageOptions: {
parser: vueParser
},
plugins: {
harlanzw
},
rules: {
'harlanzw/link-ascii-only': 'error',
'harlanzw/link-lowercase': 'error',
'harlanzw/link-no-double-slashes': 'error',
'harlanzw/link-no-whitespace': 'error',
'harlanzw/nuxt-await-navigate-to': 'error',
'harlanzw/nuxt-no-redundant-import-meta': 'error',
'harlanzw/nuxt-no-side-effects-in-async-data-handler': 'error',
'harlanzw/nuxt-no-side-effects-in-setup': 'error',
'harlanzw/nuxt-prefer-navigate-to-over-router-push-replace': 'error',
'harlanzw/nuxt-prefer-nuxt-link-over-router-link': 'error',
'harlanzw/vue-no-faux-composables': 'error',
'harlanzw/vue-no-nested-reactivity': 'error',
'harlanzw/vue-no-passing-refs-as-props': 'error',
'harlanzw/vue-no-reactive-destructuring': 'error',
'harlanzw/vue-no-ref-access-in-templates': 'error',
'harlanzw/vue-no-torefs-on-props': 'error'
}
}
]Sponsors
Credits
This plugin is based on eslint-plugin-antfu by Anthony Fu.
License
Licensed under the MIT license.
