When we start a new project, syntax check and style format is important but not easy to config.
That is because, before ESLint 9, it had many conflicts between IDE/Editor, prettier, and ESLint. Now ESLint9 disabled & deprecated some confict rules, and enabled Flat config as default.
(ESLint 9.0 stable version published in 2024/4/6, and developed quickly)
In this tutorial, I have created a Gist based on ESLint 9 for Vue3 + TypeScript
, supported Template
and JSX
.
- Vue3 + TypeScript.
- The Official recommended config.
- Syntax check by ESLint, and style format by Stylistic.
- Conflicts disabled by Stylistic preset
disable-legacy
config. - Extra plugin
simple-import-sort
, and shows how eslint9 is compact with plugins (old)
If you are a pro with ESLint, just straightforward to the Gist content to save your time, please leave a comment to improve the config.
Let's get started!
How to Config ESLint 8
- to extend preset configs, before eslint 9, you need:
{
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier",
"plugin:vue/vue3-essential",
"eslint:recommended"
]
}
- to use eslint plugins, before eslint 9, you need:
{
"plugins": [
"@typescript-eslint",
"simple-import-sort",
"prettier"
]
}
- then you can config eslint rules:
{
"rules": {
"prettier/prettier": ["..."],
"@typescript-eslint/no-var-required": "...",
"simple-import-sort/imports": ["..."]
}
}
- Oh, there are some hidden configs you need to know (parser options):
{
"parser": "espree" // default JavaScript parser provided by eslint
}
if you want eslint to support TypeScript, you need add config like this:
(also, this base config is provided by @typescript-eslint/parser
plugin)
{
"parser": "@typescript-eslint/parser",
"parserOptions": { "sourceType": "module" },
"plugins": [ "@typescript-eslint" ]
}
to support Vue SFC template
syntax, the hidden base config behind eslint-plugin-vue
is:
{
"parser": "vue-eslint-parser",
// for JavaScript
"parserOptions": { "module": 2020, "sourceType": "module" },
"plugins": [ "vue" ]
}
to support JSX syntax:
{
"parser": "@typescript-eslint/parser", // this is not only one choice
"parserOptions": {
"ecmaFeatures": { "jsx": true }
}
}
According to the above, you can get a resolved ESLint 8 config for vue3 like below:
{
"parser": "eslint-plugin-vue",
"parserOptions": {
// <script lang="ts" /> to enable TypeScript in Vue SFC
"parser": "@typescript-eslint/parser",
"sourceType": "module",
"ecmaFeatures": { "jsx": true }
},
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier",
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"plugins": [
"@typescript-eslint",
"simple-import-sort",
"prettier"
],
"rules": {
"prettier/prettier": [ "..." ],
"@typescript-eslint/no-var-required": "...",
"simple-import-sort/imports": [ "..." ]
}
}
It's aready looks like a simple config, but:
- parser config is hiddened by Domain Specific Language (DSL) plugin, you need to know the base config.
- the preset config name you want to extend is loaded hiddenly by ESLint, you may lose some info.
- you need write each DSL rules together, it's not easy to maintain.
- syntax and style rules are mixed and make conflicts with prettier.
Really simple flat config in ESLint 9
-
Create a config file, I recommend
eslint.config.mjs
to use ESM module.
export default [ // your config here ]
-
In ESLint 9, parser and DSL support upgrade to languageOptions which more clearly:
export default [ { files: ["**/*.{js,mjs,cjs,ts,mts,jsx,tsx}"], languageOptions: { // common parser options, enable TypeScript and JSX parser: "@typescript-eslint/parser", parserOptions: { sourceType: "module" } } }, { files: ["*.vue", "**/*.vue"], languageOptions: { parser: "vue-eslint-parser", parserOptions: { // <script lang="ts" /> to enable TypeScript in Vue SFC parser: "@typescript-eslint/parser", sourceType: "module" } } } ]
-
Config code running environment,
globals
holding a bunch of flags forBrowser
andNode.js
:
(you can find details innode_modules/globals/globals.json
)
import globals from "globals" export default [ { languageOptions: { globals: { ...globals.browser, ...globals.node } } } ]
-
add preset configs
import jsLint from "@eslint/js" import tsLint from "typescript-eslint" import vueLint from "eslint-plugin-vue" export default [ jsLint.configs.recommended, ...tsLint.configs.recommended, ...vueLint.configs["flat/essential"], ]
-
fixup old config rules and change some values
import { fixupConfigRules } from "@eslint/compat" import pluginReactConfig from "eslint-plugin-react/configs/recommended.js" export default [ ...fixupConfigRules(pluginReactConfig), { rules: { "react/react-in-jsx-scope": "off" } } ]
-
add a third-party plugin.
To configure plugins inside of a configuration file, use the plugins key, which contains an object with properties representing plugin namespaces and values equal to the plugin object.
simple-import-sort
is one of my favorite plugins I really recommend. Group and sort imports can make your code more readable.
import pluginSimpleImportSort from "eslint-plugin-simple-import-sort" export default [ { plugins: { // key "simple-import-sort" is the plugin namespace "simple-import-sort": pluginSimpleImportSort }, rules: { "simple-import-sort/imports": [ "error", { groups: [ "..." ] } ] } } ]
-
What's more, use stylistic to handle non-syntax code style format:
import stylistic from "@stylistic/eslint-plugin" export default [ // disable legacy conflict rules about code style stylistic.configs["disable-legacy"], // you can customize or use a preset stylistic.configs.customize({ indent: 4, quotes: "double", semi: false, commaDangle: "never", jsx: true }) ]
Finally, you can get a really simple flat config (github Gist):
import globals from "globals"
import { fixupConfigRules } from "@eslint/compat"
import pluginReactConfig from "eslint-plugin-react/configs/recommended.js"
import jsLint from "@eslint/js"
import tsLint from "typescript-eslint"
import vueLint from "eslint-plugin-vue"
import stylistic from "@stylistic/eslint-plugin"
export default [
// config parsers
{
files: ["**/*.{js,mjs,cjs,ts,mts,jsx,tsx}"],
},
{
files: ["*.vue", "**/*.vue"],
languageOptions: {
parserOptions: {
parser: "@typescript-eslint/parser",
sourceType: "module"
}
}
},
// config envs
{
languageOptions: {
globals: { ...globals.browser, ...globals.node }
}
},
// syntax rules
jsLint.configs.recommended,
...tsLint.configs.recommended,
...vueLint.configs["flat/essential"],
...fixupConfigRules(pluginReactConfig),
// code style rules
stylistic.configs["disable-legacy"],
stylistic.configs.customize({
indent: 4,
quotes: "double",
semi: false,
commaDangle: "never",
jsx: true
})
]
Luckily, ESLint provided a friendly CLI command to generate most of the config:
npm init @eslint/config@latest
# or, if you aready installed ESLint
npx eslint --init
Except custommized rules and stylistic.
What's more, to ignore files?
In ESLint 9, it doesn't read ignore file like .eslintignore
automatically, you should manully add ignore patterns
export default [
{
// https://eslint.org/docs/latest/use/configure/ignore
ignores: [
// only ignore node_modules in the same directory
// as the configuration file
"node_modules",
// so you have to add `**/` pattern to include nested directories
// for example, if you use pnpm workspace
"**/node_modules",
// also you can add a new rule to revert a ignore
"!./packages/manual-add-lib/node_modules/local-lib.js"
]
}
]
for compat purpose, you also can use includeIgnoreFile to read .eslintignore file directly.
be concern with gitignore or eslintignore file and ESLint pattern rules
// eslint.config.mjs
import { includeIgnoreFile } from "@eslint/compat"
import path from "node:path"
import { fileURLToPath } from "node:url"
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const ignorePath = path.resolve(__dirname, ".eslintignore")
export default [
{ /* ... your eslint config ...*/ },
includeIgnoreFile(ignorePath)
]
End
Links
- ESLint 9 Realease Note: https://eslint.org/blog/2024/04/eslint-v9.0.0-released/
- Migration Guide: https://eslint.org/docs/latest/use/migrate-to-9.0.0
- Languate Options: https://eslint.org/docs/latest/use/configure/language-options
- Gists: https://gist.github.com/aolyang/8ad9c14209b069806eac45b5927d00de
- Stylistic: https://eslint.style/guide/getting-started
- ESLint 9 ignore rules: https://eslint.org/docs/latest/use/configure/ignore
Top comments (2)
Not clear why at each version we need this revolution switching between different files and formats. Would be not be easier on agree on a file/format and move on with that? dev life is already complicated enough :)
I'm already confused😂