Standardized Petal specification linting configuration
npm install @flowr/eslint[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
- easy reasonable defaults with just one line of config
- auto fix for formatting and best practices (without prettier)
- designed to work with typescript, jsx, json, yaml, toml, markdown, etc. out of the box
- optional react, unocss, solid, astro, vue and svelte support
- optional formatter support for css, html, xml, graphql, etc.
- easily composable [eslint flat config]
- respects .gitignore by default, without the need for .eslintignore
- petal specification: minimal, stable, consistent code and diffs
- sorted imports, dangling commas
- single quotes, semicolons
- uses eslint [stylistic]
- supports eslint v9.5+
``bash`
pnpm i -D eslint @flowr/eslint
`js
// eslint.config.mjs
import { defineConfig } from '@flowr/eslint';
export default defineConfig();
`
combined with a legacy configuration:
if you still use some configuration from the legacy eslintrc format, you can use the @eslint/eslintrc package to convert them to the flat config.
`js
// eslint.config.mjs
import { FlatCompat } from '@eslint/eslintrc';
import { defineConfig } from '@flowr/eslint';
const compat = new FlatCompat();
export default defineConfig(
{
ignores: [],
},
// legacy config
...compat.config({
extends: [
'eslint:recommended'
// other legacy config options...
]
})
// other flat configs...
);
`
> note that .eslintignore no longer works in flat config, see customization for more details.
`json`
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
}
}
🟦 vscode support
install the [vscode eslint extension] and add the following settings to your .vscode/settings.json:
`jsonc
{
// disable the default formatter, use eslint
"prettier.enable": false,
"editor.formatOnSave": false,
// automatically fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
// silent stylistic rules in the ide, but still fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off", "fixable": true },
{ "rule": "*-indent", "severity": "off", "fixable": true },
{ "rule": "*-spacing", "severity": "off", "fixable": true },
{ "rule": "*-spaces", "severity": "off", "fixable": true },
{ "rule": "*-order", "severity": "off", "fixable": true },
{ "rule": "*-dangle", "severity": "off", "fixable": true },
{ "rule": "*-newline", "severity": "off", "fixable": true },
{ "rule": "*quotes", "severity": "off", "fixable": true },
{ "rule": "*semi", "severity": "off", "fixable": true }
],
// enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"json5",
"jsonc",
"yaml",
"astro",
"toml",
"graphql",
"gql",
"xml",
"css",
"less",
"scss",
"pcss",
"postcss"
]
}
`
🟩 neovim support
`lua
local customizations = {
{ rule = 'style/*', severity = 'off', fixable = true },
{ rule = 'format/*', severity = 'off', fixable = true },
{ rule = '*-indent', severity = 'off', fixable = true },
{ rule = '*-spacing', severity = 'off', fixable = true },
{ rule = '*-spaces', severity = 'off', fixable = true },
{ rule = '*-order', severity = 'off', fixable = true },
{ rule = '*-dangle', severity = 'off', fixable = true },
{ rule = '*-newline', severity = 'off', fixable = true },
{ rule = '*quotes', severity = 'off', fixable = true },
{ rule = '*semi', severity = 'off', fixable = true },
}
local lspconfig = require('lspconfig')
-- enable eslint for all supported languages
lspconfig.eslint.setup({
filetypes = {
"javascript",
"javascriptreact",
"javascript.jsx",
"typescript",
"typescriptreact",
"typescript.tsx",
"vue",
"html",
"markdown",
"json",
"jsonc",
"json5",
"yaml",
"toml",
"xml",
"gql",
"graphql",
"astro",
"svelte",
"css",
"less",
"scss",
"pcss",
"postcss"
},
settings = {
-- silence the stylistic rules in neovim, but still autofix them
rulesCustomizations = customizations,
},
-- nvim-lspconfig has an EslintFixAll command predefined. you can create an autocmd to call this command upon saving a file`
on_attach = function(client, bufnr)
vim.api.nvim_create_autocmd("BufWritePre", {
buffer = bufnr,
command = "EslintFixAll",
})
end,
})
there are some preexisting libraries for eslint nvim support as well, which you can use for auto-fixing too:
- use conform.nvim
- use none-ls.nvim
- use nvim-lint
we use [eslint flat config]. it provides much better orginzation and composition. normally you only need to import and use the petal preset:
`js
// eslint.config.js
import { defineConfig } from '@flowr/eslint';
export default defineConfig();
`
alternatively, you can confgure each integration individually, for example:
`js
// eslint.config.js
import { defineConfig } from '@flowr/eslint';
export default defineConfig({
// type of the project
type: 'lib', // 'lib' or 'app', default is 'app'
// enable stylistic formatting rules
stylistic: {
indent: 'tab', // 4, or 2
quotes: 'single', // or 'double'
},
// frameworks are automatically detected. you can manually enable them:
typescript: true,
astro: true,
vue: true,
// disable jsonc and yaml support
jsonc: false,
yaml: false,
// .eslintignore isn't supported in flat configs, use ignores instead`
ignores: [
'**/fixtures',
// ...globs
],
// ...
});
the defineConfig factory function also accepts any number of arbitrary custom config overrides:
`js
// eslint.config.js
import { defineConfig } from '@flowr/eslint';
export default defineConfig(
{
// configures for petal preset
},
// eslint flat config overrides
{
files: ['*/.ts'],
rules: {
'do-something': 'error',
},
},
{
rules: {
'do-something': 'off',
},
},
);
`
going more advanced, you can also import fine-grained configs and compose them as wanted:
advanced example
we wouldn't recommend using this style in general unless you know what you are doing, as there are shared options between configs and might need extra care to make consistent:
`js
// eslint.config.js
import {
combine,
comments,
ignores,
imports,
javascript,
jsdoc,
jsonc,
markdown,
node,
sortPackageJson,
sortTsconfig,
stylistic,
toml,
typescript,
unicorn,
vue,
yaml,
} from '@flowr/eslint';
export default combine(
ignores(),
javascript(/ options /),
comments(),
node(),
jsdoc(),
imports(),
unicorn(),
typescript(/ options /),
stylistic(),
vue(),
jsonc(),
yaml(),
toml(),
markdown(),
/ ... /
);
`
check out the [configs] and [factory] for more details.
since [eslint flat config] allows us to explicitly provide the plugin names (rather than the mandatory convention derived from the npm package name), we renamed some plugins to make the overall scope more consistent and easier to write:
| New Prefix | Original Prefix | Source Plugin |
| ------------ | ----------------------- | --------------------------------------- |
| import/ | i/ | [eslint-plugin-import-x] |node/
| | n/ | [eslint-plugin-n] |yaml/
| | yml/ | [eslint-plugin-yml] |ts/
| | @typescript-eslint/ | [@typescript-eslint/eslint-plugin] |style/
| | @stylistic/ | [@stylistic/eslint-plugin] |test/
| | vitest/ | [@vitest/eslint-plugin] |vue-a11y/
| | vuejs-accessibility/ | [eslint-plugin-vuejs-accessibility] |schema/*
| | json-schema-validator | [eslint-plugin-json-schema-validator] |
when you want to override rules, or disable them inline, you need to update to the new prefix (unfortunately the vscode eslint extension doesn't automatically rewrite this):
`diff`
-// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
+// eslint-disable-next-line ts/consistent-type-definitions
type foo = { bar: 2 }
change back to original prefixes/change other prefixes
if you really want to use the original prefix, you can revert the plugin renaming using:
`ts
// eslint.config.js
import { defineConfig } from '@flowr/eslint';
export default defineConfig()
.renamePlugins({
node: 'n',
ts: '@typescript-eslint',
yaml: 'yml',
// ...
});
`
certain rules would only be enabled in specific files, for example ts/* rules would only be enabled in .ts files. if you want to override those rules, you need to specifiy the file extension:
`js
// eslint.config.js
import { defineConfig } from '@flowr/eslint';
export default defineConfig(
{
vue: true,
typescript: true
},
{
// remember to specify the file glob here, otherwise the vue plugin might apply to non-vue files
files: ['*/.vue'],
rules: {
'vue/operator-linebreak': ['error', 'before'],
},
},
{
// without the file globs, they are general rules for all files
rules: {
'style/semi': ['error', 'never'],
},
},
);
`
we also provide the overrides option for each integration to use our default globs:
`js
// eslint.config.js
import { defineConfig } from '@flowr/eslint';
export default defineConfig({
vue: {
overrides: {
'vue/operator-linebreak': ['error', 'before'],
}
},
typescript: {
overrides: {
'ts/consistent-type-definitions': ['error', 'interface'],
}
},
yaml: {
overrides: {
/ ... /
}
},
});
`
the factory function defineConfig() returns a FlatConfigComposer object from eslint-flat-config-utils where you can chain the methods to compose the config even more flexibly.
`js
// eslint.config.ks
import { defineConfig } from '@flowr/eslint';
export default defineConfig()
.prepend(
// some configs before the main config
)
.override(
'petal/typescript',
{
rules: {
'import/order': ['error', { 'newlines-between': 'always' }],
}
}
)
.renamePlugins({
'old-prefix': 'new-prefix',
/ ... /
});
/ ... /
`
we provide some additional configs for specific cases, that we don't include their dependencies by default to reduce package size:
#### vue
vue support is auto-detected based on the vue dependency. you can also explicitly enable or disable it:
`js
// eslint.config.js
import { defineConfig } from '@flowr/eslint';
export default defineConfig({
vue: true,
});
`
we also support additional options, such as accessibility for a11y rules, and sfcBlocks for inline code blocks:
`js
// eslint.config.js
import { defineConfig } from '@flowr/eslint';
export default defineConfig({
vue: {
sfcBlocks: true, // default is true, requires eslint-processor-vue-blocksfalse
accessibility: true, // default is , requires eslint-plugin-vuejs-accessibility`
},
});
the required dev dependencies are: eslint-plugin-vue (you should be prompted to install these when running eslint)
if you have vue.sfcBlocks enabled (set to enabled by default), you will also need to install eslint-processor-vue-blocks as a dev dependency, also prompted upon running eslint
if you have vue.accessibility enabled (set to disabled by default), you will also need to install eslint-plugin-vuejs-accessibility as a dev dependency, also prompted upon running eslint
#### vue 2
we have limited support for vue 2 (as it's already reached eol). if you are still using vue 2, you can set it manually by setting vueVersion to 2.
`js
// eslint.config.js
import { defineConfig } from '@flowr/eslint';
export default defineConfig({
vue: {
vueVersion: 2,
},
});
`
this support may be removed when eslint-plugin-vue drops support for vue 2 and it is recommended to update to vue 3 if possible.
#### formatters
use external formatters to format files that eslint cannot handle yet (.css, .html, etc.)
`js
// eslint.config.js
import { defineConfig } from '@flowr/eslint';
export default defineConfig({
formatters: {
css: true, // format CSS, LESS, SCSS files, also the