Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Experimental support of Flat Config #1522

Merged
merged 11 commits into from
Oct 24, 2022
Merged

Conversation

uhyo
Copy link
Contributor

@uhyo uhyo commented Sep 8, 2022

Fixes #1518

This PR adds a support of ESLint's new configuration format.

This feature is enabled by setting eslint.experimentalUseFlatConfig to true.

Note that currently FlatESLint needs to be imported from a special endpoint eslint/use-at-your-own-risk. Therefore we should treat this feature as experimental.

@uhyo
Copy link
Contributor Author

uhyo commented Sep 16, 2022

@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 😅 )

@dbaeumer
Copy link
Member

@uhyo unfortunately this has to wait a little until next month. I am very busy with task I have to complete in other areas.

@koshic
Copy link

koshic commented Sep 18, 2022

@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.

@@ -394,6 +394,7 @@ export namespace ESLintClient {
synchronize: {
fileEvents: [
Workspace.createFileSystemWatcher('**/.eslintr{c.js,c.cjs,c.yaml,c.yml,c,c.json}'),
Workspace.createFileSystemWatcher('**/eslint.config.js'),
Copy link
Contributor

Choose a reason for hiding this comment

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

You will also need to add this file to this filter:

const configFileFilter: DocumentFilter = { scheme: 'file', pattern: '**/.eslintr{c.js,c.yaml,c.yml,c,c.json}' };

// 8.21.0 <= version, experimental endpoint that supports Flat Config
ESLint: undefined;
CLIEngine: undefined;
FlatESLint: ESLintClassConstructor;
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

@@ -947,13 +970,51 @@ export namespace ESLint {
return resultPromise;
}

function getESLintManifest(workingDirectory: DirectoryItem | undefined, configType: ConfigType | undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

[ '.eslintrc.yaml', false ],
[ '.eslintrc.yml', false ]
const projectFolderIndicators: [string, boolean, ConfigType | undefined][] = [
[ 'eslint.config.js', true, 'flat-config' ],
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 find an array of typed objects easier to understand here.

Comment on lines 1009 to 1011
if (ESLintModule.hasFlatESLintClass(library) && useESLintClass) {
return new library.FlatESLint(newOptions);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why have this if the if-block in line 1015 would already handle it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also it seems like you're using the withESLintClass setting in a way that doesn't align with the documented description of the setting.

@uhyo
Copy link
Contributor Author

uhyo commented Oct 2, 2022

@MariaSolOs Thank you for review! I recreated this PR with the config approach. This is indeed more reasonable; the clutter in the ESLintModule type is no longer there.

I named the new config experimentalUseFlatConfig because ESLint is going to merge the Flat Config support into the regular ESLint class, so this config will finally be unneeded after the experimental period.

Hope you can recheck this.

@MariaSolOs
Copy link
Contributor

@uhyo Thanks to you for the quick updates! Before you do any further changes though I think that @dbaeumer (being the main maintainer of this extension) should take a look, since I don't want you to spend your precious time and effort unnecessarily :)

@dbaeumer
Copy link
Member

dbaeumer commented Oct 7, 2022

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.

Copy link
Member

@dbaeumer dbaeumer left a comment

Choose a reason for hiding this comment

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

Clean up playground as pointed out in general comment.

@uhyo
Copy link
Contributor Author

uhyo commented Oct 8, 2022

@dbaeumer Thanks! I cleaned up the playground.

@@ -155,6 +155,7 @@ export type ConfigurationSettings = {
validate: Validate;
packageManager: PackageManagers;
useESLintClass: boolean;
experimentalUseFlatConfig: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

To remain consistent with the useESLintClass setting, I would remove the experimental suffix here.

Copy link
Contributor Author

@uhyo uhyo Oct 9, 2022

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

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)."
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

You can dismiss this comment now that you've explained why we're using the experimental name :)

package.json Outdated
Comment on lines 346 to 347
},
{
Copy link
Contributor

Choose a reason for hiding this comment

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

Not too important, but unless you did this on purpose, you might have a formatter making changes here.

Copy link
Contributor Author

@uhyo uhyo Oct 9, 2022

Choose a reason for hiding this comment

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

Sorry, I reverted the formatting changes.

package.json Outdated
}
"items": {
"properties": {
"severity": {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the change here? Did you replace tabs with spaces?

Comment on lines +727 to +729
const projectFolderIndicators: {
fileName: string;
isRoot: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for following my suggestion! This looks much cleaner 💄

// During Flat Config is considered experimental,
// 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) {
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 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.

Copy link
Contributor Author

@uhyo uhyo Oct 9, 2022

Choose a reason for hiding this comment

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

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.

}
} 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.`);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think there's a typo in this error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, fixed 🙂
I also updated error messages so it helps users after Flat Config is stabilized.

}
} 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`);
Copy link
Contributor

Choose a reason for hiding this comment

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

neither exports?

@@ -851,11 +851,11 @@ export namespace ESLint {
if (lib === undefined) {
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.`);
Copy link
Contributor

Choose a reason for hiding this comment

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

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 :)

Copy link
Contributor

Choose a reason for hiding this comment

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

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 ❤️

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed the error message! Thank you for patiently working with me.

@dbaeumer
Copy link
Member

@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.

@MariaSolOs
Copy link
Contributor

@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! 🚀

@dbaeumer dbaeumer merged commit 5ae084e into microsoft:main Oct 24, 2022
@dbaeumer dbaeumer added this to the 2.2.next milestone Oct 24, 2022
@uhyo uhyo deleted the flat-config branch October 24, 2022 11:43
@karlhorky
Copy link
Contributor

Amazing, thanks for the work from everyone here! 🙌

I take it from the milestone this will be available in the 2.2.7 release? (because the current release on the marketplace is 2.2.6)

From semver I would have imagined that this would actually go into the 2.3.0 or even 3.0.0 release, but the 2.2.next milestone that this is a part of seems to indicate that it would instead be 2.2.7.

@MariaSolOs
Copy link
Contributor

@dbaeumer is probably going to be the one handling the release, and although I do agree with @karlhorky that 2.2.7 doesn't feel like the right semver version, I would suggest including this as part of a 2.3.0 release since we want to keep 3.0.0 for the pull diagnostics change coming up with #1534 (and in this way editors that use push diagnostics will still benefit from this flat config support).

@dbaeumer
Copy link
Member

2.2.next only denote the next release. Doesn't denote a concrete version number. The version number I always decide when I do the release. It will be some wired version number increase due to the fact how the market-place handles insider versions. See https://github.com/microsoft/vscode-eslint#release-notes

@karlhorky
Copy link
Contributor

karlhorky commented Oct 25, 2022

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:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

any plan for support eslint new config file eslint.config.js?
5 participants