Add Experimental support of Flat Config#1522
Conversation
|
@dbaeumer Hi, could you take a look when you have time? Support by VSCode's ESLint extension is a a huge step towards the spread of ESLint's new config system. (I'm not an ESLint member though 😅 ) |
|
@uhyo unfortunately this has to wait a little until next month. I am very busy with task I have to complete in other areas. |
|
@dbaeumer may be someone else can review this PR and take care about release (as alpha/beta, doesn't matter)? It doesn't look so complicated, but really blocks adoption of trash-less ESLint config. |
| synchronize: { | ||
| fileEvents: [ | ||
| Workspace.createFileSystemWatcher('**/.eslintr{c.js,c.cjs,c.yaml,c.yml,c,c.json}'), | ||
| Workspace.createFileSystemWatcher('**/eslint.config.js'), |
There was a problem hiding this comment.
You will also need to add this file to this filter:
vscode-eslint/client/src/client.ts
Line 114 in e345da0
server/src/eslint.ts
Outdated
| // 8.21.0 <= version, experimental endpoint that supports Flat Config | ||
| ESLint: undefined; | ||
| CLIEngine: undefined; | ||
| FlatESLint: ESLintClassConstructor; |
There was a problem hiding this comment.
From the use of this type this seems to refer the exports of ESLint main's entry point. If @dbaeumer can confirm that this was the original intention, then this last object is wrong, since the FlatESLint export is still not part of the public API. Moreover, I don't see why we would need to make ESLint undefined here? As of today ESLint is still exposing that class.
server/src/eslint.ts
Outdated
| return resultPromise; | ||
| } | ||
|
|
||
| function getESLintManifest(workingDirectory: DirectoryItem | undefined, configType: ConfigType | undefined) { |
There was a problem hiding this comment.
I truly dislike having these heuristics for figuring out the configuration type to use, since they might not match how ESLint discovers the configuration file to use and then we might end up with inconsistent linting experiences when using the CLI command versus using this extension.
I'm just giving my 2¢ here, but for preserving backwards compatibility and easing the maintenance of this extension while ESLint upgrades its API, I think we should follow a similar approach as the one used with the withESLintClass setting when moving from CLIEngine to ESLint. We could have an opt-in withFlatConfig setting that when set to true (and using the right ESLint version) will import the corresponding FlatConfig and use that API, else it will default to the current behavior until ESLint replaces the ESLint class with the flat one and then that becomes the new default. I'm aware that this would mean that if someone has an eslint.config.js file then they need an extra step of toggling the withFlatConfig setting to make this extension work but with an experimental API that's still changing, I think this is a reasonable sacrifice.
server/src/eslint.ts
Outdated
| [ '.eslintrc.yaml', false ], | ||
| [ '.eslintrc.yml', false ] | ||
| const projectFolderIndicators: [string, boolean, ConfigType | undefined][] = [ | ||
| [ 'eslint.config.js', true, 'flat-config' ], |
There was a problem hiding this comment.
I would find an array of typed objects easier to understand here.
server/src/eslint.ts
Outdated
| if (ESLintModule.hasFlatESLintClass(library) && useESLintClass) { | ||
| return new library.FlatESLint(newOptions); | ||
| } |
There was a problem hiding this comment.
Why have this if the if-block in line 1015 would already handle it?
There was a problem hiding this comment.
Also it seems like you're using the withESLintClass setting in a way that doesn't align with the documented description of the setting.
|
@MariaSolOs Thank you for review! I recreated this PR with the config approach. This is indeed more reasonable; the clutter in the I named the new config Hope you can recheck this. |
|
The code changes actually look good to me. However, I would like to ask to clean up the playground you added and have one small example that uses a flat config. |
dbaeumer
left a comment
There was a problem hiding this comment.
Clean up playground as pointed out in general comment.
|
@dbaeumer Thanks! I cleaned up the playground. |
| validate: Validate; | ||
| packageManager: PackageManagers; | ||
| useESLintClass: boolean; | ||
| experimentalUseFlatConfig: boolean; |
There was a problem hiding this comment.
To remain consistent with the useESLintClass setting, I would remove the experimental suffix here.
There was a problem hiding this comment.
Sorry for confusing you, but I'd like to keep the name experimental because, unlike useESLintClass, this config will be unnecessary in the future when Flat Config is considered stable. At that time the regular ESLint class will have the Flat Config support as described here. Then this config can be removed and users can use Flat Config without additional configuration.
There was a problem hiding this comment.
I understand now. Thanks for explaining!
| "scope": "resource", | ||
| "type": "boolean", | ||
| "default": false, | ||
| "description": "Enable support of experimental Flat Config (aka eslint.config.js, supported by ESLint version 8.21 or later)." |
There was a problem hiding this comment.
As mentioned previously, I would remove the experimental here, since flat config will eventually become stable and we don't want to have separate settings for the experimental and stable APIs.
There was a problem hiding this comment.
You can dismiss this comment now that you've explained why we're using the experimental name :)
package.json
Outdated
| }, | ||
| { |
There was a problem hiding this comment.
Not too important, but unless you did this on purpose, you might have a formatter making changes here.
There was a problem hiding this comment.
Sorry, I reverted the formatting changes.
package.json
Outdated
| } | ||
| "items": { | ||
| "properties": { | ||
| "severity": { |
There was a problem hiding this comment.
What's the change here? Did you replace tabs with spaces?
| const projectFolderIndicators: { | ||
| fileName: string; | ||
| isRoot: boolean; |
There was a problem hiding this comment.
Thanks for following my suggestion! This looks much cleaner 💄
| // we need to import FlatESLint from 'eslint/use-at-your-own-risk'. | ||
| // See: https://eslint.org/blog/2022/08/new-config-system-part-3/ | ||
| const eslintPath = settings.experimentalUseFlatConfig ? 'eslint/use-at-your-own-risk' : 'eslint'; | ||
| if (nodePath !== undefined) { |
There was a problem hiding this comment.
I would suggest first trying to check if the 'eslint' entry point exports a FlatESLint object. If it doesn't and the setting is enabled, try the experimental path. That way we won't need to update this code when flat config becomes stable.
There was a problem hiding this comment.
As described in this comment the 'eslint' entry point will never export a FlatESLint object, but the name ESLint is kept when Flat Config is stablized.
When Flat Config becomes stable, what user needs to do is to disable the experimentalUseFlatConfig config.
server/src/eslint.ts
Outdated
| } | ||
| } else if (lib.FlatESLint === undefined) { | ||
| settings.validate = Validate.off; | ||
| connection.console.error(`The eslint library loaded from ${libraryPath} doesn\'t neither exports a FlatESLint class.`); |
There was a problem hiding this comment.
I think there's a typo in this error message.
There was a problem hiding this comment.
Thanks, fixed 🙂
I also updated error messages so it helps users after Flat Config is stabilized.
server/src/eslint.ts
Outdated
| } | ||
| } else if (library.CLIEngine === undefined && library.ESLint === undefined) { | ||
| settings.validate = Validate.off; | ||
| connection.console.error(`The eslint library loaded from ${libraryPath} doesn\'t neither exports a CLIEngine nor an ESLint class. You need at least eslint@1.0.0`); |
server/src/eslint.ts
Outdated
| settings.validate = Validate.off; | ||
| if (!settings.silent) { | ||
| connection.console.error(`Failed to load eslint library from ${libraryPath}. See output panel for more information.`); | ||
| connection.console.error(`Failed to load eslint library from ${libraryPath}. If you are using ESLint lower than v8.21, try upgrading ESLint. If you are using a fairly new ESLint version, try disabling 'experimentalUseFlatConfig' config. See output panel for more information.`); |
There was a problem hiding this comment.
Sorry for the nit, but I think a clearer message would be "If you are using ESLint v8.21 or earlier, try upgrading it. For newer versions, try disabling the 'experimentalUseFlatConfig' setting. See the output panel for more information.".
Error messages should be as clear as possible to help users encountering them :)
There was a problem hiding this comment.
Other than that, everything else LGTM! Thanks a lot @uhyo for your patience and effort, I'm sure a lot of people will very much appreciate your contribution ❤️
There was a problem hiding this comment.
Fixed the error message! Thank you for patiently working with me.
|
@MariaSolOs since you guided most of that coding can you please approve the changes as well. Then I will merge it into the main branch. |
@dbaeumer @uhyo I tested the changes locally and everything LGTM. Let's ship this! 🚀 |
|
Amazing, thanks for the work from everyone here! 🙌 I take it from the milestone this will be available in the From semver I would have imagined that this would actually go into the |
|
@dbaeumer is probably going to be the one handling the release, and although I do agree with @karlhorky that |
|
|
|
Ok thanks, I'm not sure I understand this explanation in this Release Notes docs section, so I did a PR to try to challenge my assumptions about it: |
Fixes #1518
This PR adds a support of ESLint's new configuration format.
This feature is enabled by setting
eslint.experimentalUseFlatConfigto true.Note that currently
FlatESLintneeds to be imported from a special endpointeslint/use-at-your-own-risk. Therefore we should treat this feature as experimental.