eslint / eslint

Find and fix problems in your JavaScript code.
https://eslint.org
MIT License
24.44k stars 4.41k forks source link

Bug: Documentation for "Custom Rule Tutorial" contains invalid config #17775

Closed jamesWalker55 closed 6 months ago

jamesWalker55 commented 6 months ago

Environment

Node version: v21.1.0 npm version: v10.2.0 Local ESLint version: v8.53.0 (Currently used) Global ESLint version: Not found Operating System: win32 10.0.19045

What parser are you using?

Default (Espree)

What did you do?

I've modified the config file to reproduce this in a single config file:

const fooBarRule = {
  meta: {
    type: "problem",
    docs: {
      description:
        "Enforce that a variable named `foo` can only be assigned a value of 'bar'.",
    },
    fixable: "code",
    schema: [],
  },
  create(context) {
    return {
      // Performs action in the function on every variable declarator
      VariableDeclarator(node) {
        // Check if a `const` variable declaration
        if (node.parent.kind === "const") {
          // Check if variable name is `foo`
          if (node.id.type === "Identifier" && node.id.name === "foo") {
            // Check if value of variable is "bar"
            if (
              node.init &&
              node.init.type === "Literal" &&
              node.init.value !== "bar"
            ) {
              /*
               * Report error to ESLint. Error message uses
               * a message placeholder to include the incorrect value
               * in the error message.
               * Also includes a `fix(fixer)` function that replaces
               * any values assigned to `const foo` with "bar".
               */
              context.report({
                node,
                message:
                  'Value other than "bar" assigned to `const foo`. Unexpected value: {{ notBar }}.',
                data: {
                  notBar: node.init.value,
                },
                fix(fixer) {
                  return fixer.replaceText(node.init, '"bar"');
                },
              });
            }
          }
        }
      },
    };
  },
};

const plugin = { rules: { "enforce-foo-bar": fooBarRule } };

module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:vue/vue3-essential",
  ],
  overrides: [
    {
      env: {
        node: true,
      },
      files: [".eslintrc.{js,cjs}"],
      parserOptions: {
        sourceType: "script",
      },
    },
  ],
  parserOptions: {
    ecmaVersion: "latest",
    parser: "@typescript-eslint/parser",
    sourceType: "module",
  },
  plugins: { example: plugin },
  rules: {},
};

What did you expect to happen?

The config plugins: { example: plugin }, is parsed successfully, as per the documentation.

What actually happened?

The config expects only an array in the plugins field, the documentation's object syntax does not work.

Error: ESLint configuration in .eslintrc.cjs is invalid:
    - Property "plugins" is the wrong type (expected array but got `{"example":{"rules":{"enforce-foo-bar":{"meta":{"type":"problem","docs":{"description":"Enforce that a variable named `foo` can only be assigned a value of 'bar'."},"fixable":"code","schema":[]}}}}}`).

    at ConfigValidator.validateConfigSchema (D:\Programming\tag-repo\node_modules\@eslint\eslintrc\dist\eslintrc.cjs:2168:19)
    at ConfigArrayFactory._normalizeConfigData (D:\Programming\tag-repo\node_modules\@eslint\eslintrc\dist\eslintrc.cjs:3010:19)
    at ConfigArrayFactory.loadInDirectory (D:\Programming\tag-repo\node_modules\@eslint\eslintrc\dist\eslintrc.cjs:2878:33)
    at CascadingConfigArrayFactory._loadConfigInAncestors (D:\Programming\tag-repo\node_modules\@eslint\eslintrc\dist\eslintrc.cjs:3860:46)
    at CascadingConfigArrayFactory._loadConfigInAncestors (D:\Programming\tag-repo\node_modules\@eslint\eslintrc\dist\eslintrc.cjs:3879:20)
    at CascadingConfigArrayFactory._loadConfigInAncestors (D:\Programming\tag-repo\node_modules\@eslint\eslintrc\dist\eslintrc.cjs:3879:20)
    at CascadingConfigArrayFactory.getConfigArrayForFile (D:\Programming\tag-repo\node_modules\@eslint\eslintrc\dist\eslintrc.cjs:3781:18)
    at CLIEngine.isPathIgnored (D:\Programming\tag-repo\node_modules\eslint\lib\cli-engine\cli-engine.js:1000:18)
    at ESLint.isPathIgnored (D:\Programming\tag-repo\node_modules\eslint\lib\eslint\eslint.js:681:26)
    at ESLint8Plugin.<anonymous> (C:\Program Files\JetBrains\RustRover 233.10527.39\plugins\javascript-impl\languageService\eslint\bin\eslint8-plugin.js:182:57)

Link to Minimal Reproducible Example

N/A, ESLint playground does not allow custom configurations

Participation

Additional comments

No response

kecrily commented 6 months ago

I think you read the wrong documentation.

Please note that we currently have two config systems. The legacy config system currently used by default (the config file is .eslintrc.*), and the flat config system (the config file is eslint.config.js) that will be implemented in v9.

The plugins: { example: plugin } you mentioned is only supported in the flat config system.

How to use the plugin in the legacy config system, you can check Configure Plugins

adbutterfield commented 4 months ago

I just hit this issue as well. Docs here: https://eslint.org/docs/latest/extend/custom-rule-tutorial Says at the top: Version v8.56.0

I guess a lot of people don't do this, so it's not a major problem... So there's no way to have a local custom rule then? (other than using eslint-plugin-local-rules of course)

A bit annoying, but I guess it'll have to do until v9.

Rec0iL99 commented 4 months ago

but I guess it'll have to do until v9.

You can use the flat config system in v8.56.0, but it's not enabled by default. You don't need to use eslint-plugin-local-rules when using flat config.

// eslint.config.js
const fooBarRule = {
  meta: {
    type: "problem",
    docs: {
      description:
        "Enforce that a variable named `foo` can only be assigned a value of 'bar'.",
    },
    fixable: "code",
    schema: [],
  },
  create(context) {
    return {
      // Performs action in the function on every variable declarator
      VariableDeclarator(node) {
        // Check if a `const` variable declaration
        if (node.parent.kind === "const") {
          // Check if variable name is `foo`
          if (node.id.type === "Identifier" && node.id.name === "foo") {
            // Check if value of variable is "bar"
            if (
              node.init &&
              node.init.type === "Literal" &&
              node.init.value !== "bar"
            ) {
              /*
               * Report error to ESLint. Error message uses
               * a message placeholder to include the incorrect value
               * in the error message.
               * Also includes a `fix(fixer)` function that replaces
               * any values assigned to `const foo` with "bar".
               */
              context.report({
                node,
                message:
                  'Value other than "bar" assigned to `const foo`. Unexpected value: {{ notBar }}.',
                data: {
                  notBar: node.init.value,
                },
                fix(fixer) {
                  return fixer.replaceText(node.init, '"bar"');
                },
              });
            }
          }
        }
      },
    };
  },
};

const plugin = { rules: { "enforce-foo-bar": fooBarRule } };

module.exports = [
  {
    plugins: { example: plugin },
    rules: {
      "example/enforce-foo-bar": "error",
    },
  },
];
Rec0iL99 commented 4 months ago

Our v9.0.0-alpha.1 release has flat config enabled by default and is a pre-release to v9

docs