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

feat: Better error message for flat config plugins #17399

Merged
merged 5 commits into from Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 39 additions & 5 deletions docs/src/use/configure/migration-guide.md
Expand Up @@ -211,7 +211,7 @@ In eslintrc files, you configure various language options across the `env`, `glo

In flat config files, the `globals`, and `parserOptions` are consolidated under the `languageOptions` key; the `env` property doesn't exist. Groups of global variables for specific runtimes are imported from the [globals](https://www.npmjs.com/package/globals) npm package and included in the `globals` property. You can use the spread operator (`...`) to import multiple globals at once.

For example, here's a eslintrc file with language options:
For example, here's an eslintrc file with language options:

```javascript
// .eslintrc.js
Expand Down Expand Up @@ -255,14 +255,14 @@ export default [
];
```

### Predefined Configs
### Predefined and Shareable Configs

In eslintrc files, use the `extends` property to use predefined configs. ESLint comes with two predefined configs that you can access as strings:
In eslintrc files, use the `extends` property to use predefined and shareable configs. ESLint comes with two predefined configs that you can access as strings:

* `"eslint:recommended"`: the rules recommended by ESLint
* `"eslint:all"`: all rules shipped with ESLint

You can also use the `extends` property to extend a custom config. Custom configs can either be paths to local config files or npm package names.
You can also use the `extends` property to extend a shareable config. Shareable configs can either be paths to local config files or npm package names.

In flat config files, predefined configs are imported from separate modules into flat config files. The `recommended` and `all` rules configs are located in the [`@eslint/js`](https://www.npmjs.com/package/@eslint/js) package. You must import this package to use these configs:

Expand All @@ -287,7 +287,7 @@ module.exports = {
}
```

This eslintrc file uses built-in config, local custom config, and custom config from an npm package:
This eslintrc file uses built-in config, local custom config, and shareable config from an npm package:

```javascript
// .eslintrc.js
Expand Down Expand Up @@ -341,6 +341,40 @@ export default [
];
```

#### Using eslintrc Configs in Flat Config

You may find that there's a shareable config you rely on that hasn't yet been updated to flat config format. In that case, you can use the `FlatCompat` utility to translate the eslintrc format into flat config format. First, install the `@eslint/eslintrc` package:

```shell
npm install @eslint/js --save-dev
nzakas marked this conversation as resolved.
Show resolved Hide resolved
```

Then, import `FlatCompat` and create a new instance to convert an existing eslintrc config. For example, if the npm package `eslint-config-my-config` is in eslintrc format, you can write this:

```js
import { FlatCompat } from "@eslint/eslintrc";
import path from "path";
import { fileURLToPath } from "url";

// mimic CommonJS variables -- not needed if using CommonJS
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname
});

export default [

// mimic ESLintRC-style extends
...compat.extends("eslint-config-my-config"),
];
```

This example uses the `FlatCompat#extends()` method to insert the `eslint-config-my-config` the flat config array.
nzakas marked this conversation as resolved.
Show resolved Hide resolved

For more information about the `FlatCompat` class, please see the [package README](https://github.com/eslint/eslintrc#usage).

### Ignoring Files

With eslintrc, you can make ESLint ignore files by creating a separate `.eslintignore` file in the root of your project. The `.eslintignore` file uses the same glob pattern syntax as `.gitignore` files. Alternatively, you can use an `ignorePatterns` property in your eslintrc file.
Expand Down
21 changes: 21 additions & 0 deletions lib/config/flat-config-schema.js
Expand Up @@ -227,6 +227,22 @@ class IncompatibleKeyError extends Error {
}
}

/**
* The error type when there's an eslintrc-style plugins array found.
*/
class IncompatiblePluginsError extends Error {

/**
* Creates a new instance.
* @param {Array<string>} plugins The plugins array.
*/
constructor(plugins) {
super("This appears to be in eslintrc format (array of strings) rather than flat config format (object).");
this.messageTemplate = "eslintrc-plugins";
this.messageData = { plugins };
}
}


//-----------------------------------------------------------------------------
// Low-Level Schemas
Expand Down Expand Up @@ -319,6 +335,11 @@ const pluginsSchema = {
throw new TypeError("Expected an object.");
}

// make sure it's not an array, which would mean eslintrc-style is used
if (Array.isArray(value)) {
throw new IncompatiblePluginsError(value);
}

// second check the keys to make sure they are objects
for (const key of Object.keys(value)) {

Expand Down
22 changes: 22 additions & 0 deletions messages/eslintrc-plugins.js
@@ -0,0 +1,22 @@
"use strict";

module.exports = function({ plugins }) {

return `
A config object has a "plugins" key defined as an array of strings.

Flat config requires "plugins" to be an object in this form:

{
plugins: {
${plugins[0] || "namespace"}: pluginObject
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Flat config schema throws this error on any array (it doesn't check whether it's an array of strings), so if the user specifies plugins like this:

module.exports = [{
    plugins: [somePluginObject]
}];

The printed message will look like this:

Oops! Something went wrong! :(

ESLint: 8.45.0


A config object has a "plugins" key defined as an array of strings.

Flat config requires "plugins" to be an object in this form:

    {
        plugins: {
            [object Object]: 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

Perhaps we should have two templates?

  • plugins is an array of strings (or empty array?) - possible eslintrc config, use this template
  • plugins is an array but not array of strings - use another template.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think for now I'd like to keep things simple and just assume that when people are using an array then they are most likely using an eslintrc-style config. We can always expand later if we find that's not the case.

I've updated to check if the first item in the array is a string to make sure that the message makes sense.


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
`;
};
9 changes: 9 additions & 0 deletions tests/lib/config/flat-config-array.js
Expand Up @@ -1963,6 +1963,15 @@ describe("FlatConfigArray", () => {
});
});

it("should error when plugins is an array", async () => {
await assertInvalidConfig([
{
plugins: ["foo"]
}
], "Key \"plugins\": This appears to be in eslintrc format (array of strings) rather than flat config format (object).");

});


});

Expand Down