Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: update project to support eslint9 and flat configs #373

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

tagoro9
Copy link
Contributor

@tagoro9 tagoro9 commented Jan 28, 2025

Description

This PR updates the project to be compatible with eslint v9 and flat configurations.

It introduces several breaking changes:

  • Minimum node version is 20. This wasn't strictly needed but why not.
  • Consolidate all sort like plugins into just https://github.com/azat-io/eslint-plugin-perfectionist - one of the plugins we were using was no longer maintained and didn't support eslint v9
  • Remove the legacy config preset. It's only used in a few internal repos and they should be able to update easily

Apart from this I made other features / changes:

  • Add support for eslint to lint this repo using the config in this repo. The applied rules are limited (see below in open questions)
  • Add support for the project to work both with ESM and CJS. It seems ESM support is much better now, so it would be good to start supporting it in more places
  • Update the tests to check for lint errors instead of the config eslint determines (the snapsthots were quite big and with 3 configs it was hard to read)

Getting the grasp of the new config setup took a while but it feels a lot more flexible.

TODOs

  • Test this in a few more repos
  • Add instructions to the update file for how to move from eslintrc configs into eslint.config.js configs

Open questions

  • Should we support non flat configs or just deprecate everything and leave just the flat configuration?
  • Should we drop support for eslint 8? It's in EOL already but left it just in case
  • Should we suppport CJS or just go with ESM? Eslint supports this natively so it wouldn't conflict an actual project code and would simplify things in this repo
  • If we drop support for eslint 8, should we also drop the recommended legacy config preset?
  • eslint v9 no longer has the --ext option and we have to use the files option to tell it what files to lint. Should we tell it to lint also js and jsx extensions? This would allow to lint this repo better or any repo where there is still JS code
  • Should we explore using TS here to define the configurations? We would get better typing and a bit of overhead for building it
  • Should we establish any naming convention for the imports of other libraries in the new config files? I used <tool>Plugin as the variable name but open to any

How has this been tested

  • Via unit tests to ensure all presets work
  • By running the config in this repo
  • In some internal repos locally installing the config via yalc

Changes

  • feat: replace sort plugins with eslint-plugin-perfectionist
  • feat: update required min node version to 20
  • feat: add flat configs, use eslint 9 and remove legacy preset
  • ci: add lint script
  • test: add test for all the config flavors
  • docs: udpate readme and add breaking changes doc

🚀 PR created with fotingo

@tagoro9 tagoro9 requested a review from a team as a code owner January 28, 2025 15:52
index.mjs Outdated

export default tseslint.config(
{
files: ["**/*.ts", "**/*.tsx"],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I mention in the PR. This is the replacement of the -- ext option. Maybe we should include here *.js and *.jsx?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need a separate configuration for *.js files that doesn't include the TS plugins

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try on a JS project to see what happens. It might just work fine. In this repo I had to add some tweaks for the default config so it could pick them

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a separate eslint config repo for *.js, right?
Although I guess the point is that in TS projects we may have .js files,... So I guess we could try adding them here and if there are problems, adding a separate config as Esau mentions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a separate eslint config repo for *.js, right?

We do, but that project is not used at all as we don't have any JS only project

I'll play with things and see if it works!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do, but that project is not used at all as we don't have any JS only project

web-schumacher-app still relies on it for .js!

index.mjs Outdated

export default tseslint.config(
{
files: ["**/*.ts", "**/*.tsx"],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need a separate configuration for *.js files that doesn't include the TS plugins

"lint": "echo noop",
"test": "jest ./test/test.spec.js"
"lint": "eslint './*.{cjs,mjs}' test/test.spec.js",
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jest is still the biggest blocker for ESM... 😞

Copy link
Contributor

@romenrg romenrg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I remember the legacy preset being used by several repos, but I guess it is ok to remove it in this new version and force those repos to be updated to the new default preset when bumping the dependency.

index.mjs Outdated

export default tseslint.config(
{
files: ["**/*.ts", "**/*.tsx"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a separate eslint config repo for *.js, right?
Although I guess the point is that in TS projects we may have .js files,... So I guess we could try adding them here and if there are problems, adding a separate config as Esau mentions

@tagoro9 tagoro9 force-pushed the f/eslint_v9 branch 3 times, most recently from a1262e4 to 776e688 Compare February 1, 2025 00:06
@open-turo open-turo deleted a comment from github-actions bot Feb 1, 2025
BREAKING CHANGE: Several plugins with configs for sorting things have
been replaced with eslint-plugin-perfectionist to consolidate plugins
and better support the migration to eslint 9.
BREAKING CHANGE: Minimum required node version is 20
BREAKING CHANGE: The legacy preset no longer exists, non flat configs
are still supports but require changes in the consumer
use eslint to lint the eslint config
remove snapshot in favor of checking that there are no lint errors
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While trying to integrate this with the react plugin, I realized that I was using the files setting wrong. The configs should use files as little as possible, except for when it's really needed. There are plugins that work with both JS and TS and we are now limiting them to just TS.

There are plugins like jest, that might need to be split into 2: JS and TS tests, as there are particular rules that require type checkers to be present

@open-turo open-turo deleted a comment from github-actions bot Feb 20, 2025
@open-turo open-turo deleted a comment from github-actions bot Feb 22, 2025
@open-turo open-turo deleted a comment from github-actions bot Feb 24, 2025
Copy link

Prerelease build

Build version: 16.0.0-pr-373.11.1.1
Docker image: ``

Build output

Copy link

github-actions bot commented Feb 24, 2025

Release notes preview

Below is a preview of the release notes if your PR gets merged.


16.0.0 (2025-02-24)

⚠ BREAKING CHANGES

  • The legacy preset no longer exists, non flat configs
    are still supports but require changes in the consumer
  • Minimum required node version is 20
  • Several plugins with configs for sorting things have
    been replaced with eslint-plugin-perfectionist to consolidate plugins
    and better support the migration to eslint 9.

Features

  • add flat configs, use eslint 9 and remove legacy preset (8fef88a)
  • convert the export into a function with some options (8d77a58)
  • make default config a function and split config into smaller functions (71c374d)
  • replace sort plugins with eslint-plugin-perfectionist (3808b19)
  • update required min node version to 20 (30bba2d)

Tests

  • add test for all the config flavors (7e5d775)

Continuous Integration

Build System

Documentation

  • add missing env var requirement for legacy configs (3eaf04b)
  • udpate readme and add breaking changes doc (09c7012)

Breaking changes file docs/breaking-changes/v16.md

Breaking changes in v16

Minimum Node.js version

The minimum Node.js supported version is now ^20.

Removed plugins

The following plugins are getting replaced by eslint-plugin-perfectionist:

If you were reconfiguring some of the rules from these plugins in your eslint config you will have to update them and reference
the equivalent rule from https://perfectionist.dev/rules.

Removed legacy preset

The legacy preset is removed, as it only existed for some internal turo projects. Maintaining it to support eslint v9
was not worth the effort.

Default flat configuration

The default configuration for this package is now a flat eslint configuration. Read the
eslint guide for how to update legacy configurations
to flat configs.

The legacy configuration is still supported. To keep using it, you just have to update the .eslintrc file to:

{
--  "extends": "@open-turo/eslint-config-typescript"
++  "extends": "@open-turo/eslint-config-typescript/recommended"
}

If you are using legacy configurations, you will have to set the ESLINT_USE_FLAT_CONFIG env var to true.

Updating to flat configuration

If you wish to update to a flat configuration, you will have to make the following changes (please refer to the above guide
for all the details).

  • Move from .eslintrc (or equivalent file) into eslint.config.js (or equivalent file).
  • If you have a .eslintignore file, replace that into the ignores section of the eslint.config.js file

A .eslintignore file like this:

/lib

Will become a eslint.config.js file like this:

const turoConfig = require("@open-turo/eslint-config-typescript");

module.exports = [
  ...turoConfig,
  {
    ignores: ["/lib"],
  },
];

@tagoro9 tagoro9 requested a review from Toolo February 24, 2025 16:45
@@ -0,0 +1,219 @@
const eslint = require("@eslint/js");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a big refactor of this. Starting this thread so it's easier to add feedback.

  • We now have a single definition of everything. The mjs file just imports this one
  • The default export is no longer a flat config, but a function. The function takes in a few parameters. This makes it easier for a consumer to tweak some settings without having to tell what plugins are in use. See above how I disabled typescript for eslint in this repo. This will open it up to putting in here configs we have in other repos today (like the JS config)
  • I broke down the config into smaller grouped functions as it makes it easier to read. This also creates small differences with the non flat config, as rules plugins like prettier, unicorn or eslint affect all the files and not just TS files. I think this is ok.

parserOptions,
},
overrideConfigFile: "./recommended.cjs",
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at least locally I was able to get decent rule snapshot like this

const calculatedConfig =
        await linter.calculateConfigForFile("./test/sample.ts");
expect(calculatedConfig.rules).toMatchSnapshot();

I tested changing some @typescript-eslint rules, some sonar rules and it seemed to catch the changes. Maybe it'll work for us?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right the snapshot just contains the rules. I don't know what I was testing when I saw odd snapshots and it also works with flat configs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still wonder. Should this be a test or should this be a helper to help see what rules are affecting a file when the config is in use?

I can see good things and some friction with having the rules in the snapshot:

  • Is a 1000 line snapshot valuable? There would be 3 1k line snapshots. Does it provide any value on its own? Probably not. But it can be valuable as a way to tell what has changed between 2 configs
  • Any dependency update may require updating the snapshot for any new rule they add / change even if the change is backwards compatible
  • With flat configs, I think the snapshot will not capture all rules, since we are running it on a specific file extension and now configs can target different types of files and it will require more long snapshots

We can add it and see if it has value long term or not

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would defer to your judgement.

mainly, will try to shed light on the problem statement: In my case, I was looking to change a 'org-wide' default and found that I couldn't parse which eslint-config the rule originated from. The snapshot came to me as an idea to at least get some visibility of the rule output of each config. If I was tracking down again, I could find the rule via searching github.

Other use case might be to get a feel for the scope of changes in major version plugin updates, to help digest the downstream effects.

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants