Frontend Development 17 min read

ESLint: Past, Present, and Future

This article traces the evolution of ESLint from its early role as a simple JavaScript linter to the introduction of the Flat Config system in ESLint 9, examines the challenges of extends, multi‑format support, plugins, and shared configurations, and explores new tools like Config Inspector, eslint‑flat‑config‑utils, ESLint Stylistic, and future multi‑language support.

大转转FE
大转转FE
大转转FE
ESLint: Past, Present, and Future

On October 26, 2023, the author of ESLint announced that future versions would deprecate core formatting rules, encouraging users to adopt external formatters and refocusing ESLint as a pure linting tool.

On April 5, 2024, ESLint 9 was released, introducing the important Flat Config system, which flattens configuration handling.

Historically, ESLint was merely a code‑quality tool, but with the release of Flat Config and the community’s freedom to evolve after core formatting rules were dropped, ESLint gained new possibilities.

The timeline of Flat Config shows that the concept was first proposed in a 2019 draft, and over a five‑year span the ESLint team steadily worked on its implementation.

Key pain points that motivated Flat Config include the cumbersome extends attribute, which required users to locate and understand imported configuration files across node_modules , and the complexity of rule cascading.

Another issue was multi‑format support: ESLint originally allowed .eslintrc, .json, .yml, .yaml, and .js formats, but mixing JavaScript config with non‑JS formats caused incompatibilities.

Shared configurations also suffered because extends relied on require() , making peer dependencies fragile after npm v3 stopped auto‑installing them.

The overrides feature, intended to solve extends cascading, ended up re‑introducing extends inside overrides, further increasing complexity for both users and maintainers.

Flat Config Configuration Approach

ESLint 9 now supports only JavaScript configuration files, eliminating format incompatibility. Two examples illustrate the difference:

{
  "extends": ["./.other-config.json"],
  "rules": {
    "semi": "warn"
  }
}

Legacy .eslintrc.js example (simplified):

module.exports = {
  env: { browser: true, es2021: true, node: true },
  extends: ['eslint:recommended'],
  overrides: [{
    files: ["**/*.{js,jsx,ts,tsx}"],
    plugins: ["react", "jsx-a11y"],
    extends: ["plugin:react/recommended"]
  }],
  parser: '@typescript-eslint/parser',
  parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
  plugins: ['react', '@typescript-eslint', 'react-hooks'],
  rules: { '@typescript-eslint/no-var-requires': 0 }
};

Flat Config ( eslint.config.js ) example:

import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';

export default tseslint.config(
  { ignores: ['dist'] },
  {
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    files: ['**/*.{ts,tsx}'],
    languageOptions: { ecmaVersion: 2020, globals: globals.browser },
    plugins: { 'react-hooks': reactHooks, 'react-refresh': reactRefresh },
    rules: {
      ...reactHooks.configs.recommended.rules,
      'react-refresh/only-export-components': ['warn', { allowConstantExport: true }]
    }
  }
);

In the new system, extends must import the target config via ESM import before referencing it, making dependencies explicit and easier to locate.

Similarly, plugins are now defined as an object where each key is a custom name and the value is the imported plugin module, allowing flexible renaming and clearer mapping between plugins and their rules.

// original config
export default [
  { plugins: { 'react-refresh': reactRefresh }, rules: { 'react-refresh/only-export-components': 'off' } }
];
// after renaming
export default [
  { plugins: { refresh: reactRefresh }, rules: { 'refresh/only-export-components': 'off' } }
];

New Ecosystem Around Flat Config

Config Inspector – an open‑source tool by Anthony Fu that runs a local server to display which rules apply to a given file, helping developers understand inherited configurations.

Factory Function Approach – using eslint-flat-config-utils to compose configurations with a chainable API ( composer , .append , .prepend , .insertAfter , .renamePlugins , .override ), enabling modular and reusable config blocks.

// eslint.config.mjs
import { composer } from 'eslint-flat-config-utils';

export default composer({ plugins: {}, rules: {} })
  .append(/* more configs */)
  .prepend(/* more configs */)
  .insertAfter('config-name', /* configs */)
  .renamePlugins({ 'old-name': 'new-name', n: 'node' })
  .override('config-name', { rules: { 'no-console': 'off' } });

ESLint Stylistic – a community‑driven set of style‑focused plugins that replace ESLint’s built‑in formatter rules, providing @stylistic equivalents for JavaScript, TypeScript, JSX, and additional rules.

import stylistic from '@stylistic/eslint-plugin';
export default [
  stylistic.configs.customize({
    indent: 2,
    quotes: 'single',
    semi: false,
    jsx: true
  })
];

TypeScript Type Hints – the eslint-typegen utility generates a eslint-typegen.d.ts file from a Flat Config, giving editors full type‑completion for rule options.

///
import typegen from 'eslint-typegen';
export default typegen([
  // your flat config items
]);

Future Outlook

ESLint 9’s adoption is still limited, but the project is moving toward becoming a language‑agnostic static analysis platform, already supporting JSON and Markdown linting.

ESLint now officially supports linting of JSON and Markdown – taking the first steps toward a language‑agnostic platform.

With its robust AST core, IDE integrations, and emerging tools like ESLint Plugin Command, ESLint could evolve into a universal linter + formatter for many languages.

Conclusion

From its origins as a flexible alternative to JSHint, ESLint has transformed into a multi‑language static analysis platform. The Flat Config overhaul resolves long‑standing configuration pain points and opens a bright future for extensibility, type‑safe configuration, and broader language support.

References:

[1] ESLint's new config system, Part 1: Background – https://eslint.org/blog/2022/08/new-config-system-part-1/

[2] eslint-flat-config-utils – https://github.com/antfu/eslint-flat-config-utils

[3] ESLint Stylistic – https://eslint.style/

[4] eslint-typegen – https://github.com/antfu/eslint-typegen

[5] ESLint now officially supports linting of JSON and Markdown – https://eslint.org/blog/2024/10/eslint-json-markdown-support/

[6] ESLint Plugin Command – https://eslint-plugin-command.antfu.me/

TypeScriptJavaScriptconfigurationpluginsESLintlintingFlat Config
大转转FE
Written by

大转转FE

Regularly sharing the team's thoughts and insights on frontend development

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.