Skip to content

daflyinbed/eslint-config

Repository files navigation

@xwbx/eslint-config

A opinionated ESLint config preset for JavaScript, TypeScript, Vue 2 or Vue 3, and Prettier.

Features

  • Format with Prettier.
  • Designed to work with TypeScript, Vue 2 and 3 out-of-box.
  • Support Astro, JSON(5), YAML, Markdown, ...
  • Sort imports, package.json, tsconfig.json...
  • ESLint Flat config, compose easily!
  • Ignores common files like dist, node_modules, coverage, and files in .gitignore.
  • Reasonable defaults, best practices, only one-line of config
  • Reasonable strict, but with better code quality.
  • Requires ESLint v9.5.0+

Install

npm i -D @xwbx/eslint-config

Require Node.js >= 18.18, and ESLint >= 9.5.0.

Usage

import { xwbx } from "@xwbx/eslint-config";
export default xwbx(
  {
    // TypeScript, Vue, Unocss And Astro are autodetected, you can also explicitly enable them:
    typescript: true,
    vue: true,
    unocss: true,
    astro: true,
    // Disable jsonc yaml and toml support
    jsonc: false,
    yaml: false,
    toml: false,
    // `.eslintignore` is no longer supported in Flat config, use `ignores` instead
    ignores: [
      "**/fixtures",
      // ...globs
    ],
  },
  // From the second arguments they are ESLint Flat Configs
  // you can have multiple configs
  {
    files: ["**/*.ts"],
    rules: {},
  },
);

Unocss

Unocss support is detected automatically by checking if unocssor@unocss/webpackor@unocss/nuxt' is installed in your project. You can also explicitly enable/disable it:

// eslint.config.js
import xwbx from "@xwbx/eslint-config";

export default xwbx({
  unocss: true,
});

Running npx eslint should prompt you to install the required dependencies, otherwise, you can install them manually:

pnpm i -D @unocss/eslint-plugin

Astro

Astro support is detected automatically by checking if astro is installed in your project. You can also explicitly enable/disable it:

// eslint.config.js
import xwbx from "@xwbx/eslint-config";

export default xwbx({
  astro: true,
});

Running npx eslint should prompt you to install the required dependencies, otherwise, you can install them manually:

pnpm i -D astro-eslint-parser eslint-plugin-astro prettier-plugin-astro

Prettier

Default option is

const prettierOptions = {
  tabWidth: 2,
  useTabs: false,
  trailingComma: "all",
  singleQuote: false,
  semi: true,
  endOfLine: "lf",
};

Override by formatters.prettierOptions

// eslint.config.js
export default xwbx({
  formatters: {
    prettierOptions: {
      semi: false,
    },
  },
});

perfectionist (sorting)

This plugin eslint-plugin-perfectionist allows you to sort object keys, imports, etc, with auto-fix.

The plugin is installed, but only perfectionist/sort-imports, perfectionist/sort-named-exports and perfectionist/sort-named-imports are enabled by default.

It's recommended to opt-in on each file individually using configuration comments.

/* eslint perfectionist/sort-exports: "error" */
export * from "./astro";
export * from "./command";
export * from "./comments";

command

Powered by eslint-plugin-command. It is not a typical rule for linting, but an on-demand micro-codemod tool that triggers by specific comments.

For a few triggers, for example:

  • /// to-function - converts an arrow function to a normal function
  • /// to-arrow - converts a normal function to an arrow function
  • /// to-for-each - converts a for-in/for-of loop to .forEach()
  • /// to-for-of - converts a .forEach() to a for-of loop
  • /// keep-sorted - sorts an object/array/interface
  • ... etc. - refer to the documentation

You can add the trigger comment one line above the code you want to transform, for example (note the triple slash):

/// to-function
const foo = async (msg: string): void => {
  console.log(msg)
}

Will be transformed to this when you hit save with your editor or run eslint . --fix:

async function foo(msg: string): void {
  console.log(msg);
}

The command comments are usually one-off and will be removed along with the transformation.

Plugins Renaming

Since flat config requires us to explicitly provide the plugin names (instead of the mandatory convention from 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/* import-x/* 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
test/* no-only-tests/* eslint-plugin-no-only-tests

When you want to override rules, or disable them inline, you need to update to the new prefix:

-// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
+// eslint-disable-next-line ts/consistent-type-definitions
type foo = { bar: 2 }

Editor Specific Disables

Some rules are disabled when inside ESLint IDE integrations, namely unused-imports/no-unused-imports test/no-only-tests

This is to prevent unused imports from getting removed by the IDE during refactoring to get a better developer experience. Those rules will be applied when you run ESLint in the terminal or Lint Staged. If you don't want this behavior, you can disable them:

// eslint.config.js
import xwbx from "@xwbx/eslint-config";

export default xwbx({
  isInEditor: false,
});

Rules Overrides

Certain rules would only be enabled in specific files, for example, ts/* rules would only be enabled in .ts files and vue/* rules would only be enabled in .vue files. If you want to override the rules, you need to specify the file extension:

// eslint.config.js
import xwbx from "@xwbx/eslint-config";

export default xwbx(
  {
    vue: true,
    typescript: true,
  },
  {
    // Remember to specify the file glob here, otherwise it might cause the vue plugin to handle non-vue files
    files: ["**/*.vue"],
    rules: {
      "vue/operator-linebreak": ["error", "before"],
    },
  },
  {
    // Without `files`, they are general rules for all files
    rules: {
      "style/semi": ["error", "never"],
    },
  },
);

We also provided the overrides options in each integration to make it easier:

// eslint.config.js
import xwbx from "@xwbx/eslint-config";

export default xwbx({
  vue: {
    overrides: {
      "vue/operator-linebreak": ["error", "before"],
    },
  },
  typescript: {
    overrides: {
      "ts/consistent-type-definitions": ["error", "interface"],
    },
  },
  yaml: {
    overrides: {
      // ...
    },
  },
});

VSCode

Enable flat config if you are using ESLint < 9.

{
  // Disable the default formatter, use eslint instead
  "prettier.enable": false,
  "eslint.experimental.useFlatConfig": true,
  "eslint.useFlatConfig": true,
  // Auto fix
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.organizeImports": "never",
  },
  // Enable eslint for all supported languages
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "vue",
    "html",
    "markdown",
    "json",
    "jsonc",
    "yaml",
    "toml",
    "xml",
    "svg",
    "gql",
    "graphql",
    "astro",
    "css",
    "less",
    "scss",
    "pcss",
    "postcss",
  ],
}

Credits

This setup is heavily inspired on a few projects: