Skip to content

Usage of presets in FlatConfig #148

@kachkaev

Description

@kachkaev

👋 again @schoero! I am using FlatConfig and I believe that I have found an incompatibility of the presets. Here is an MWE (eslint.config.js):

import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";

/** @type {import("eslint").Linter.Config[]} */
const configObjects = [
  // ...other configs...

  eslintPluginBetterTailwindcss.configs["recommended"] ?? {},
];

export default configObjects;

If type-checking is on, eslintPluginBetterTailwindcss.configs["recommended"] produces this tsc error:

Type '{ plugins: string[]; rules: { [x: string]: "warn" | "error"; }; } | {}' is not assignable to type 'Config<RulesRecord>' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
  Type '{ plugins: string[]; rules: { [x: string]: "warn" | "error"; }; }' is not assignable to type 'Config<RulesRecord>'.
    Types of property 'plugins' are incompatible.
      Type 'string[]' is not assignable to type 'Record<string, Plugin>'.
        Index signature for type 'string' is missing in type 'string[]'.ts(2375)

Running eslint with the above config gives a runtime error:

Oops! Something went wrong! :(

ESLint: 9.29.0


A config object has a "plugins" key defined as an array of strings. It looks something like this:

    {
        "plugins": ["better-tailwindcss"]
    }

Flat config requires "plugins" to be an object, like this:

    {
        plugins: {
            better-tailwindcss: pluginObject
        }
    }

Please see the following page for information on how to convert your config object into the correct format:
https://eslint.org/docs/latest/use/configure/migration-guide#importing-plugins-and-custom-parsers

If you're using a shareable config that you cannot rewrite in flat config format, then use the compatibility utility:
https://eslint.org/docs/latest/use/configure/migration-guide#using-eslintrc-configs-in-flat-config

I was able to do this as a workaround for Tailwind v4:

import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";

/** @type {import("eslint").Linter.Config[]} */
const configObjects = [
  // ...other configs...

  {
    plugins: {
      "better-tailwindcss": eslintPluginBetterTailwindcss,
    },
    settings: {
      "better-tailwindcss": {
        entryPoint: `${import.meta.dirname}/path/to/entry-point.css`,
      },
    },
    rules: {
      "better-tailwindcss/enforce-consistent-class-order": "warn",
      "better-tailwindcss/enforce-consistent-variable-syntax": "warn",
      "better-tailwindcss/no-conflicting-classes": "error",
      "better-tailwindcss/no-duplicate-classes": "warn",
      "better-tailwindcss/no-restricted-classes": "error",
      "better-tailwindcss/no-unnecessary-whitespace": "warn",
      "better-tailwindcss/no-unregistered-classes": "error",
    },
  },
];

export default configObjects;

It was necessary to hand-pick the rules but everything worked in the end!

If you are looking for inspiration, take a look at eslint-plugin-react-hooks@6.0.0-rc.1. This plugin supports both FlatConfig and LegacyConfig and I can use it like this:

import eslintPluginReactHooks from "eslint-plugin-react-hooks";

/** @type {import("eslint").Linter.Config[]} */
const configObjects = [
  // ...other configs...

  eslintPluginReactHooks.configs.recommended,
];

export default configObjects;

PS: I’m really glad that I’ve discovered eslint-plugin-better-tailwindcss, many thanks for working on it! It finally unblocks my migration to TailwindCSS v4 which was previously impossible because of francoismassart/eslint-plugin-tailwindcss#325 / francoismassart/eslint-plugin-tailwindcss#295.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions