dimaMachina / graphql-eslint

ESLint parser, plugin, and rule set for GraphQL (for schema and operations). Easily customizable with custom rules. Integrates with IDEs and modern GraphQL tools.
https://the-guild.dev/graphql/eslint
MIT License
801 stars 104 forks source link

Support "Flat Config" (ESLint 9) #2178

Open JoshuaKGoldberg opened 9 months ago

JoshuaKGoldberg commented 9 months ago

πŸ‘‹ Coming over from https://github.com/eslint/eslint/issues/18093: ESLint is migrating to a new "flat config" format that will be the default in ESLint v9.

It doesn't look like @graphql-eslint/eslint-plugin has support yet. I'm posting this issue here as a reference & cross-linking it to the table in https://github.com/eslint/eslint/issues/18093. If there's anything technical blocking the extension from supporting flat configs, please let us know - we'd be happy to try to help! πŸ’œ

Additional resources:

avaly commented 8 months ago

While this plugin mentions it has support for Flat Config, it does work OK, until you run eslint --cache:

Oops! Something went wrong! :(

ESLint: 8.57.0

Error: Could not serialize processor object (missing 'meta' object).
    at Object.value (/home/user/eslint-bug/node_modules/eslint/lib/config/flat-config-array.js:243:27)
    at stringify (/home/user/eslint-bug/node_modules/json-stable-stringify-without-jsonify/index.js:25:25)
    at module.exports (/home/user/eslint-bug/node_modules/json-stable-stringify-without-jsonify/index.js:68:7)
    at hashOfConfigFor (/home/user/eslint-bug/node_modules/eslint/lib/cli-engine/lint-result-cache.js:50:75)
    at LintResultCache.getCachedLintResults (/home/user/eslint-bug/node_modules/eslint/lib/cli-engine/lint-result-cache.js:116:30)
    at /home/user/eslint-bug/node_modules/eslint/lib/eslint/flat-eslint.js:821:41
    at Array.map (<anonymous>)
    at FlatESLint.lintFiles (/home/user/eslint-bug/node_modules/eslint/lib/eslint/flat-eslint.js:793:23)
    at async Object.execute (/home/user/eslint-bug/node_modules/eslint/lib/cli.js:421:23)
    at async main (/home/user/eslint-bug/node_modules/eslint/bin/eslint.js:152:22)
kamleshFC commented 8 months ago

Is there any update on this? Even I am getting the same error as the above

marceloverdijk commented 7 months ago

I'm also trying to setup the GraphQL eslint but noticing some issues as well...

From the examples repo I e.g. tried:

import * as graphqlESLint from '@graphql-eslint/eslint-plugin';

  {
    files: ['**/*.graphql'],
    ...graphqlESLint.flatConfigs['schema-recommended'],
  },

but that gives:

> eslint .

Oops! Something went wrong! :(

ESLint: 8.57.0

TypeError: Key "rules": Key "@graphql-eslint/description-style": Could not find plugin "@graphql-eslint".

although "@graphql-eslint/eslint-plugin": "^3.20.1", is installed...

and when I try instead:

{
    files: ['**/*.graphql'],
    languageOptions: {
      parser: graphqlESLint,
    },
    plugins: {
      '@graphql-eslint': graphqlESLint,
    },
    // rules: {
    //   'prettier/prettier': 'error',
    // },
  },

but that gives:

> eslint .

Oops! Something went wrong! :(

ESLint: 8.57.0

Error: Error while loading rule '@typescript-eslint/await-thenable': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.
Parser: undefined
Note: detected a parser other than @typescript-eslint/parser. Make sure the parser is configured to forward "parserOptions.project" to @typescript-eslint/parser.

Note my eslint.config.mjs contains more than only graphql lint options:

export default tseslint.config(
  {
    ignores: [
      'dist/',
      'public/',
      'src/**/*.generated.*',
    ],
  },
  eslint.configs.recommended,
  ...tseslint.configs.recommendedTypeChecked,
  {
    languageOptions: {
      parserOptions: {
        project: true,
        tsconfigRootDir: import.meta.dirname,
      },
    },
  },
  stylistic.configs.customize({
    arrowParens: true,
    semi: true,
  }),
  {
    plugins: {
      '@stylistic': stylistic,
    },
    rules: {
      '@stylistic/padding-line-between-statements': [
        'error',
        { blankLine: 'always', prev: 'import', next: '*' },
        { blankLine: 'any', prev: 'import', next: 'import' },
      ],
    },
    .. <the graphql lint config>
  },
benasher44 commented 7 months ago

if you set parser as the following, it seems to work as a workaround:

      parser: {
        ...graphqlEslint,
        meta: {
          name: "@graphql-eslint",
        }
      }
JavaScriptBach commented 6 months ago

I also can't get this working, despite claimed flat config support. Any attempt by me to set parserOptions.documents fails with

error  Parsing error: [graphql-eslint] 
Unable to find any GraphQL type definitions for the following pointers:
- ./src/**/*.{ts}

I also couldn't find any examples or documentation on parserOptions.documents, for example it isn't in https://the-guild.dev/graphql/eslint/docs/getting-started/parser-options

meierw commented 5 months ago

For those interested here is an example of a config that ended up working for our project.

We are only using schema linting at the moment, so no idea how well this works for query linting.

// eslint.config.mjs

import { fixupPluginRules } from "@eslint/compat"

import * as graphqlEslint from "@graphql-eslint/eslint-plugin"

export default [
  // other configs here

  {
    files: ["**/*.gqls"],
    languageOptions: {
      ecmaVersion: 5,
      sourceType: "script",
      parser: { ...graphqlEslint, meta: { name: "@graphql-eslint" } },
      parserOptions: { schema: "./path/to/schema/**/*.gqls" },
    },
    plugins: { "@graphql-eslint": fixupPluginRules(graphqlEslint) },

    rules: {
      ...graphqlEslint.flatConfigs["schema-recommended"].rules,
      // other rule overrides here
    },
  },
]
dimaMachina commented 3 months ago

meta object was added for parser and processor in @graphql-eslint/eslint-plugin@4.0.0-alpha.2 https://github.com/dimaMachina/graphql-eslint/releases/tag/release-1722466173246

And ESLint's --cache flag now works

use examples https://github.com/dimaMachina/graphql-eslint/tree/master/examples as reference

v4 will be released once I polish documentation to include new changes

bmulholland commented 3 months ago

I couldn't find an example of using a set of rules from this plugin, so in case anyone is looking too: it goes in the rules part of the config. You'll need to configure the rest of the eslint config block as if you were doing a manual list of rules.

(HOWEVER, note that I wasn't able to get this working. When I add graphql-eslint, eslint seems to get stuck in an infinite loop or something. Or at least, it goes from taking <10s to over a couple minutes. --debug shows GraphQL template literals cannot be plucked from a Vue template code without having the "@vue/compiler-sfc" package installed. even though I have that in my package.json & installed. I'm out of time for digging into this today.)

Something like:

  {
    // Extract GraphQL from files for linting -- this creates .graphql files that are then linted
    // below
    // While we use the .gql extension for our files, under the hood the graphql-eslint plugin
    // scans all files (using the processor entry, below) and outputs a file with the
    // text that is then parsed and generates the errors we see. That file has a fixed .graphql
    // extension, so we need to include that extension below or .ts etc files won't be linted.
    // NOTE: Order matters! First, Vue processes, then GraphQL parses, THEN the linting can take
    // place.
    files: ["**/*.js", "**/*.ts", "**/*.vue"],
    processor: graphqlESLint.processors.graphql,
  },

  {
    // Setup GraphQL Parser
    files: ["**/*.graphql", "**/*.gql"],
    languageOptions: { parser: graphqlESLint.parser },
    // https://github.com/B2o5T/graphql-eslint/tree/master/packages/plugin/src/configs/operations-recommended.ts
    plugins: { "@graphql-eslint": { rules: graphqlESLint.rules } },
    rules: {
      ...graphqlESLint.configs["flat/operations-recommended"],
      // overrides here, such as
      "@graphql-eslint/naming-convention": ["off"],
    },
  },
michaelfaith commented 2 months ago

@dimaMachina I notice this was re-opened. Is there still more work to do on this? Happy to help.

bmulholland commented 1 month ago

@dimaMachina I dug in a bit more. I got past the compiler-sfc issue using the latest alpha (not sure if I didn't have that installed before?). However, I'm now seeing a bug with the alpha: it's not correctly loading my graphql-config, so it throws e.g. no-deprecated requires a schema.

Specifically, this line seems to pass in the current file's path as the root of the project; graphql-config seems to therefore only look in the subdir the file is in, not the root. If I change the rootDir line to be my project's root dir, the config is correctly picked up. I could be wrong in my exact diagnosis here, but something's off for sure. Note that the graphql-config example in examples dir probably works because all files are in the root.

ALSO, it wasn't apparent from the Vue instructions that it was critical for the GraphQL Vue parsing to happen before other Vue parsers. Without that, all my lint errors in Vue files turned into a message simply saying "clear." I'd call that out specifically, both in the instructions and in the example.

dimaMachina commented 1 month ago

@bmulholland I think the bug with graphql-config is related to this PR merge https://github.com/kamilkisiela/graphql-config/pull/1480 ,can you try to pin graphql-config to this version ? And try if it helps?

bmulholland commented 1 month ago

@dimaMachina Good guess! My above testing was on graphql-config 5.1.2. 5.1.0 works fine; 5.1.1 is the first broken version.

This means:

And so we're back to your last statement :)

v4 will be released once I polish documentation to include new changes

As I've just gone through this, I'm happy to share any parts of my setup process if it would help you with the docs. Let me know if there's a particular blind spot or question you might have.

For starters, as I mentioned, including the importance of ordering -- probably in the form of recommending the graphql parsing of code files to go VERY FIRST in graphql.config.js -- would save others time.

dimaMachina commented 1 month ago

Amazing, would be awesome if you create a PR to point into this base branch https://github.com/dimaMachina/graphql-eslint/tree/more-new-docs with updates your Vue setup πŸ™

here is the file to update https://github.com/dimaMachina/graphql-eslint/blob/54e08ad7723f9b1d240062b40732f7adc899aafc/website/src/pages/docs/usage.mdx?plain=1#L93 (you need to create him)

mat813 commented 1 day ago

I am trying to figure out how to migrate my config to flat file, and, i think I finally figured out how to do this.

Before, I had this in my package.conf:

  "eslintConfig": {
    "overrides": [
      {
        "files": ["*.ts"],
        "extends": ["plugin:@internal/default"],
      },
      {
        "files": ["*.graphql"],
        "extends": "plugin:@graphql-eslint/schema-all",
        "parserOptions": {
          "schema": "./graphql/schema.graphql"
        },
      },
      {
        "files": ["tests/**/*.ts"],
        "processor": "@graphql-eslint/graphql"
      },
      {
        "files": ["tests/**/*.graphql"],
        "extends": "plugin:@graphql-eslint/operations-all",
        "parser": "@graphql-eslint/eslint-plugin",
        "parserOptions": {
          "schema": "./graphql/schema.graphql",
          "operations": "tests/**/*.graphql"
        },
      }
    ]
  }

With flat config, after much hours of head scratching, the important bit is to forcefully add files to all the js/ts configs so that they don't match "everything".

export default [
  ...internalPlugin.configs.default.map((config) => ({ ...config, files: ['**/*.ts'] })),
  {
    files: ['**/*.graphql'],

    plugins: {
      '@graphql-eslint': graphqlPlugin,
    },

    languageOptions: {
      parser: graphqlPlugin.parser,
    },

    rules: {
      ...graphqlPlugin.configs['flat/schema-all'].rules,
    },
  },
  {
    files: ['tests/**/*.spec.ts'],

    processor: graphqlPlugin.processor,
  },
  {
    files: ['tests/**/*.spec.ts/*.graphql'],

    plugins: {
      '@graphql-eslint': graphqlPlugin,
    },

    rules: {
      ...graphqlPlugin.configs['flat/operations-all'].rules,
    },
  },
];

I think it works, I'll edit the comment if something is not quite right.

dimaMachina commented 1 day ago

It’s wrong your operations files are extended with schema rules, which is not needed, there is official example for monorepo https://github.com/dimaMachina/graphql-eslint/blob/master/examples/monorepo/eslint.config.js

Also you specify 2 times

plugins: { '@graphql-eslint': graphqlPlugin, },