├── examples
├── prettier
│ ├── valid.graphql
│ ├── prettier.config.js
│ ├── valid.js
│ ├── invalid.graphql
│ ├── invalid.js
│ ├── package.json
│ ├── .eslintrc.cjs
│ └── eslint.config.js
├── monorepo
│ ├── server
│ │ └── types
│ │ │ ├── root.gql
│ │ │ ├── scalar.gql
│ │ │ ├── user.gql
│ │ │ └── post.gql
│ ├── graphql.config.js
│ ├── client
│ │ ├── graphql
│ │ │ └── query.users.gql
│ │ └── pages
│ │ │ └── index.tsx
│ ├── package.json
│ └── eslint.config.js
├── custom-rules
│ ├── test.graphql
│ ├── my-rule.js
│ ├── eslint.config.js
│ └── package.json
├── programmatic
│ ├── fragment.graphql
│ ├── fragment2.graphql
│ ├── schema.graphql
│ ├── query.graphql
│ ├── package.json
│ ├── eslint.config.js
│ └── .eslintrc.cjs
├── code-file
│ ├── not-query.js
│ ├── graphql.config.js
│ ├── schema.graphql
│ ├── query.js
│ ├── package.json
│ ├── eslint.config.js
│ └── .eslintrc.cjs
├── graphql-config
│ ├── operations
│ │ ├── query.graphql
│ │ └── user.fragment.graphql
│ ├── graphql.config.js
│ ├── schema.graphql
│ ├── package.json
│ ├── eslint.config.js
│ └── .eslintrc.cjs
├── multiple-projects-graphql-config
│ ├── schema.first-project.graphql
│ ├── schema.second-project.graphql
│ ├── query.first-project.js
│ ├── query.second-project.js
│ ├── package.json
│ ├── eslint.config.js
│ ├── graphql.config.ts
│ └── .eslintrc.cjs
├── svelte-code-file
│ ├── test.svelte
│ ├── package.json
│ ├── eslint.config.js
│ └── .eslintrc.cjs
└── vue-code-file
│ ├── test.vue
│ ├── package.json
│ └── .eslintrc.cjs
├── .npmignore
├── packages
├── plugin
│ ├── src
│ │ ├── meta.ts
│ │ ├── estree-converter
│ │ │ └── index.ts
│ │ ├── rules
│ │ │ ├── unique-enum-value-names
│ │ │ │ ├── index.test.ts
│ │ │ │ └── snapshot.md
│ │ │ ├── no-anonymous-operations
│ │ │ │ ├── index.test.ts
│ │ │ │ └── snapshot.md
│ │ │ ├── no-one-place-fragments
│ │ │ │ ├── snapshot.md
│ │ │ │ └── index.test.ts
│ │ │ ├── unique-operation-name
│ │ │ │ ├── snapshot.md
│ │ │ │ └── index.test.ts
│ │ │ ├── unique-fragment-name
│ │ │ │ └── snapshot.md
│ │ │ ├── require-nullable-fields-with-oneof
│ │ │ │ ├── index.test.ts
│ │ │ │ └── snapshot.md
│ │ │ ├── require-type-pattern-with-oneof
│ │ │ │ ├── snapshot.md
│ │ │ │ └── index.test.ts
│ │ │ ├── description-style
│ │ │ │ └── index.test.ts
│ │ │ ├── require-nullable-result-in-root
│ │ │ │ └── index.test.ts
│ │ │ ├── no-duplicate-fields
│ │ │ │ └── index.test.ts
│ │ │ ├── no-unused-fields
│ │ │ │ └── snapshot.md
│ │ │ ├── no-typename-prefix
│ │ │ │ └── index.test.ts
│ │ │ ├── require-deprecation-date
│ │ │ │ └── index.test.ts
│ │ │ ├── no-root-type
│ │ │ │ └── index.test.ts
│ │ │ └── require-deprecation-reason
│ │ │ │ └── index.test.ts
│ │ ├── configs
│ │ │ ├── schema-relay.ts
│ │ │ ├── operations-all.ts
│ │ │ ├── schema-all.ts
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ └── cache.ts
│ ├── __tests__
│ │ ├── mocks
│ │ │ ├── using-config
│ │ │ │ ├── nested
│ │ │ │ │ └── test.graphql
│ │ │ │ ├── .graphqlrc
│ │ │ │ └── schema-in-config.graphql
│ │ │ ├── post.graphql
│ │ │ ├── user.graphql
│ │ │ ├── known-fragment-names
│ │ │ │ ├── user.gql
│ │ │ │ ├── operation-with-undefined-fragment.gql
│ │ │ │ └── user-fields.gql
│ │ │ ├── user-fields.graphql
│ │ │ ├── import-fragments
│ │ │ │ ├── fragments
│ │ │ │ │ ├── bar-fragment.gql
│ │ │ │ │ └── foo-fragment.gql
│ │ │ │ ├── missing-import.gql
│ │ │ │ ├── invalid-query-default.gql
│ │ │ │ ├── same-file.gql
│ │ │ │ ├── invalid-query.gql
│ │ │ │ ├── valid-query-default.gql
│ │ │ │ └── valid-query.gql
│ │ │ ├── no-undefined-variables.gql
│ │ │ ├── post-fields.graphql
│ │ │ ├── possible-type-extension
│ │ │ │ ├── separate-graphql-files
│ │ │ │ │ ├── extend-type-user.gql
│ │ │ │ │ └── type-user.gql
│ │ │ │ ├── separate-code-files
│ │ │ │ │ ├── extend-type-user.ts
│ │ │ │ │ └── type-user.ts
│ │ │ │ └── one-graphql-file
│ │ │ │ │ └── type-user.gql
│ │ │ ├── no-unused-variables.gql
│ │ │ ├── test-directives-with-import.graphql
│ │ │ ├── user-fields-with-variables.gql
│ │ │ ├── no-unused-variables-imported.gql
│ │ │ ├── no-one-place-fragments.graphql
│ │ │ ├── unique-fragment.js
│ │ │ ├── known-fragment-names.ts
│ │ │ ├── two-fragments-in-code-file.js
│ │ │ ├── user-schema.graphql
│ │ │ ├── user-schema.ts
│ │ │ └── user-fields-with-nested-fragment.gql
│ │ ├── __snapshots__
│ │ │ ├── executable-definitions.spec.md
│ │ │ ├── known-directives.spec.md
│ │ │ ├── known-fragment-names.spec.md
│ │ │ ├── possible-type-extension.spec.md
│ │ │ ├── unique-type-names.spec.md
│ │ │ ├── no-undefined-variables.spec.md
│ │ │ ├── lone-schema-definition.spec.md
│ │ │ ├── eslint-directives.spec.md
│ │ │ └── fields-on-correct-type.spec.md
│ │ ├── no-unused-variables.spec.ts
│ │ ├── test-utils.ts
│ │ ├── federation.spec.ts
│ │ ├── lone-schema-definition.spec.ts
│ │ ├── no-undefined-variables.spec.ts
│ │ ├── no-unused-fragments.spec.ts
│ │ ├── executable-definitions.spec.ts
│ │ ├── unique-type-names.spec.ts
│ │ ├── fields-on-correct-type.spec.ts
│ │ ├── processor-without-graphql-config.spec.ts
│ │ ├── processor-with-graphql-config.spec.ts
│ │ └── eslint-directives.spec.ts
│ ├── serializer.ts
│ ├── tsconfig.json
│ └── vite.config.ts
└── rule-tester
│ ├── tsup.config.ts
│ ├── tsconfig.json
│ └── package.json
├── pnpm-workspace.yaml
├── .npmrc
├── website
├── postcss.config.js
├── app
│ ├── icon.png
│ ├── icons
│ │ ├── vue.svg
│ │ ├── half.svg
│ │ ├── javascript.svg
│ │ ├── graphql.svg
│ │ ├── stack.svg
│ │ ├── astro.svg
│ │ ├── prettier.svg
│ │ └── svelte.svg
│ ├── graphql-config-info.mdx
│ ├── [[...mdxPath]]
│ │ └── page.tsx
│ └── play
│ │ └── button.tsx
├── public
│ └── demo.mp4
├── content
│ ├── docs
│ │ ├── usage
│ │ │ ├── vue.mdx
│ │ │ ├── programmatic.mdx
│ │ │ ├── svelte.mdx
│ │ │ ├── prettier.mdx
│ │ │ ├── schema-and-operations.mdx
│ │ │ ├── graphql.mdx
│ │ │ ├── multiple-projects.mdx
│ │ │ ├── custom-rules.mdx
│ │ │ ├── astro.mdx
│ │ │ └── js.mdx
│ │ ├── vscode.mdx
│ │ ├── disabling-rules.mdx
│ │ └── index.mdx
│ └── rules
│ │ ├── prettier.md
│ │ ├── unique-type-names.mdx
│ │ ├── unique-operation-types.mdx
│ │ ├── variables-in-allowed-position.mdx
│ │ ├── lone-schema-definition.mdx
│ │ ├── no-fragment-cycles.mdx
│ │ ├── unique-variable-names.mdx
│ │ ├── unique-directive-names.mdx
│ │ ├── one-field-subscriptions.mdx
│ │ ├── deprecated-rules.md
│ │ ├── unique-field-definition-names.mdx
│ │ ├── unique-argument-names.mdx
│ │ ├── unique-input-field-names.mdx
│ │ ├── executable-definitions.mdx
│ │ ├── no-unused-variables.mdx
│ │ ├── variables-are-input-types.mdx
│ │ ├── possible-type-extension.mdx
│ │ ├── no-undefined-variables.mdx
│ │ ├── no-unused-fragments.mdx
│ │ ├── lone-anonymous-operation.mdx
│ │ ├── scalar-leafs.mdx
│ │ ├── provided-required-arguments.mdx
│ │ ├── unique-directive-names-per-location.mdx
│ │ ├── known-argument-names.mdx
│ │ ├── value-literals-of-correct-type.mdx
│ │ ├── overlapping-fields-can-be-merged.mdx
│ │ ├── require-nullable-fields-with-oneof.mdx
│ │ ├── require-type-pattern-with-oneof.mdx
│ │ ├── fields-on-correct-type.mdx
│ │ ├── fragments-on-composite-type.mdx
│ │ ├── possible-fragment-spread.mdx
│ │ ├── no-one-place-fragments.mdx
│ │ ├── require-nullable-result-in-root.mdx
│ │ ├── known-type-names.mdx
│ │ ├── relay-page-info.mdx
│ │ ├── no-scalar-result-type-on-mutation.mdx
│ │ ├── no-typename-prefix.mdx
│ │ ├── no-anonymous-operations.mdx
│ │ ├── unique-operation-name.mdx
│ │ ├── no-unreachable-types.mdx
│ │ ├── unique-fragment-name.mdx
│ │ ├── require-deprecation-reason.mdx
│ │ ├── unique-enum-value-names.mdx
│ │ ├── lone-executable-definition.mdx
│ │ ├── no-root-type.mdx
│ │ ├── known-directives.mdx
│ │ ├── description-style.mdx
│ │ ├── relay-connection-types.mdx
│ │ ├── require-field-of-type-query-in-mutation-result.mdx
│ │ ├── relay-arguments.mdx
│ │ ├── require-deprecation-date.mdx
│ │ └── require-import-fragment.mdx
├── next-sitemap.config.js
├── next-env.d.ts
├── tailwind.config.ts
├── tsconfig.json
└── package.json
├── prettier.config.js
├── .prettierignore
├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── feature-request.md
│ └── bug_report.md
├── workflows
│ ├── release.yml
│ ├── pr.yml
│ └── website.yml
├── renovate.json
└── labels.yml
├── .whitesource
├── .gitignore
├── .changeset
├── README.md
└── config.json
├── .vscode
├── launch.json
└── settings.json
├── tsconfig.json
├── turbo.json
├── README.md
└── LICENSE
/examples/prettier/valid.graphql:
--------------------------------------------------------------------------------
1 | scalar Test
2 |
--------------------------------------------------------------------------------
/examples/monorepo/server/types/root.gql:
--------------------------------------------------------------------------------
1 | type Query
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | tests
3 | node_modules
4 | .bob
5 | temp
6 |
--------------------------------------------------------------------------------
/examples/custom-rules/test.graphql:
--------------------------------------------------------------------------------
1 | query {
2 | foo
3 | }
4 |
--------------------------------------------------------------------------------
/examples/monorepo/server/types/scalar.gql:
--------------------------------------------------------------------------------
1 | scalar DateTime
2 |
--------------------------------------------------------------------------------
/packages/plugin/src/meta.ts:
--------------------------------------------------------------------------------
1 | export const version = process.env.VERSION;
2 |
--------------------------------------------------------------------------------
/examples/programmatic/fragment.graphql:
--------------------------------------------------------------------------------
1 | fragment Test on User {
2 | id
3 | }
4 |
--------------------------------------------------------------------------------
/examples/code-file/not-query.js:
--------------------------------------------------------------------------------
1 | console.log('should report `no-console` error');
2 |
--------------------------------------------------------------------------------
/examples/programmatic/fragment2.graphql:
--------------------------------------------------------------------------------
1 | fragment Test on User {
2 | name
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/using-config/nested/test.graphql:
--------------------------------------------------------------------------------
1 | {
2 | hello
3 | }
4 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - packages/*
3 | - examples/*
4 | - website
5 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | strict-peer-dependencies=false
2 | shell-emulator=true
3 | enable-pre-post-scripts=true
4 |
--------------------------------------------------------------------------------
/examples/code-file/graphql.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | schema: 'schema.graphql',
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/using-config/.graphqlrc:
--------------------------------------------------------------------------------
1 | schema: ./schema-in-config.graphql
2 |
--------------------------------------------------------------------------------
/website/postcss.config.js:
--------------------------------------------------------------------------------
1 | export { default } from '@theguild/tailwind-config/postcss.config';
2 |
--------------------------------------------------------------------------------
/website/app/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/graphql-hive/graphql-eslint/HEAD/website/app/icon.png
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/post.graphql:
--------------------------------------------------------------------------------
1 | query Post {
2 | post {
3 | ...PostFields
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/user.graphql:
--------------------------------------------------------------------------------
1 | query User {
2 | user {
3 | ...UserFields
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/using-config/schema-in-config.graphql:
--------------------------------------------------------------------------------
1 | type Query {
2 | hello: String
3 | }
4 |
--------------------------------------------------------------------------------
/website/public/demo.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/graphql-hive/graphql-eslint/HEAD/website/public/demo.mp4
--------------------------------------------------------------------------------
/examples/prettier/prettier.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | endOfLine: 'auto',
3 | singleQuote: true,
4 | };
5 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/known-fragment-names/user.gql:
--------------------------------------------------------------------------------
1 | {
2 | user {
3 | ...UserFields
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/user-fields.graphql:
--------------------------------------------------------------------------------
1 | fragment UserFields on User {
2 | id
3 | firstName
4 | }
5 |
--------------------------------------------------------------------------------
/examples/graphql-config/operations/query.graphql:
--------------------------------------------------------------------------------
1 | query {
2 | user {
3 | name
4 | ...UserFields
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/import-fragments/fragments/bar-fragment.gql:
--------------------------------------------------------------------------------
1 | fragment BarFields on Bar {
2 | id
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/import-fragments/fragments/foo-fragment.gql:
--------------------------------------------------------------------------------
1 | fragment FooFields on Foo {
2 | id
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/import-fragments/missing-import.gql:
--------------------------------------------------------------------------------
1 | {
2 | foo {
3 | ...FooFields
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/graphql-config/operations/user.fragment.graphql:
--------------------------------------------------------------------------------
1 | fragment UserFields on User {
2 | id
3 | name
4 | name
5 | }
6 |
--------------------------------------------------------------------------------
/examples/prettier/valid.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-unused-vars
2 | const TEST = /* GraphQL */ `
3 | scalar Test
4 | `;
5 |
--------------------------------------------------------------------------------
/examples/code-file/schema.graphql:
--------------------------------------------------------------------------------
1 | type Query {
2 | user: User!
3 | }
4 |
5 | type User {
6 | id: ID!
7 | name: String!
8 | }
9 |
--------------------------------------------------------------------------------
/examples/monorepo/graphql.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | schema: 'server/**/*.gql',
3 | documents: 'client/**/*.{tsx,gql}',
4 | };
5 |
--------------------------------------------------------------------------------
/examples/programmatic/schema.graphql:
--------------------------------------------------------------------------------
1 | type Query {
2 | user: User!
3 | }
4 |
5 | type User {
6 | id: ID!
7 | name: String!
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/no-undefined-variables.gql:
--------------------------------------------------------------------------------
1 | query User {
2 | user {
3 | id
4 | ...UserFields
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/post-fields.graphql:
--------------------------------------------------------------------------------
1 | fragment PostFields on Post {
2 | user {
3 | ...UserFields
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/graphql-config/graphql.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | schema: 'schema.graphql',
3 | documents: 'operations/*.graphql',
4 | };
5 |
--------------------------------------------------------------------------------
/examples/graphql-config/schema.graphql:
--------------------------------------------------------------------------------
1 | type Query {
2 | user: User!
3 | }
4 |
5 | type User {
6 | id: ID!
7 | name: String!
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/known-fragment-names/operation-with-undefined-fragment.gql:
--------------------------------------------------------------------------------
1 | {
2 | user {
3 | ...DoesNotExist
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/plugin/src/estree-converter/index.ts:
--------------------------------------------------------------------------------
1 | export * from './converter.js';
2 | export * from './types.js';
3 | export * from './utils.js';
4 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/possible-type-extension/separate-graphql-files/extend-type-user.gql:
--------------------------------------------------------------------------------
1 | extend type User {
2 | firstName: String
3 | }
4 |
--------------------------------------------------------------------------------
/examples/programmatic/query.graphql:
--------------------------------------------------------------------------------
1 | query {
2 | user {
3 | id
4 | name
5 | ... on User {
6 | name
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/no-unused-variables.gql:
--------------------------------------------------------------------------------
1 | query ($limit: Int!, $offset: Int!) {
2 | user {
3 | id
4 | ...UserFields
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/monorepo/client/graphql/query.users.gql:
--------------------------------------------------------------------------------
1 | query getUsers {
2 | users {
3 | id
4 | firstName
5 | lastName
6 | createdAt
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/test-directives-with-import.graphql:
--------------------------------------------------------------------------------
1 | #import './user-fields.graphql'
2 |
3 | # eslint-disable-next-line
4 | query {
5 | a
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plugin/serializer.ts:
--------------------------------------------------------------------------------
1 | import rawSnapshotSerializer from 'jest-snapshot-serializer-raw/always';
2 |
3 | expect.addSnapshotSerializer(rawSnapshotSerializer);
4 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/possible-type-extension/separate-graphql-files/type-user.gql:
--------------------------------------------------------------------------------
1 | type User {
2 | id: ID!
3 | }
4 |
5 | type Query {
6 | user: User
7 | }
8 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/import-fragments/invalid-query-default.gql:
--------------------------------------------------------------------------------
1 | #import './fragments/bar-fragment.gql'
2 | query {
3 | foo {
4 | ...FooFields
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/import-fragments/same-file.gql:
--------------------------------------------------------------------------------
1 | {
2 | foo {
3 | ...FooFields
4 | }
5 | }
6 |
7 | fragment FooFields on Foo {
8 | id
9 | }
10 |
--------------------------------------------------------------------------------
/website/content/docs/usage/vue.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebarTitle: Vue
3 | icon: VueIcon
4 | ---
5 |
6 | # Usage with `.vue` files
7 |
8 |
9 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/import-fragments/invalid-query.gql:
--------------------------------------------------------------------------------
1 | #import FooFields from "./fragments/bar-fragment.gql"
2 | query {
3 | foo {
4 | ...FooFields
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/user-fields-with-variables.gql:
--------------------------------------------------------------------------------
1 | fragment UserFields on User {
2 | firstName
3 | posts(limit: $limit, offset: $offset) {
4 | id
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/multiple-projects-graphql-config/schema.first-project.graphql:
--------------------------------------------------------------------------------
1 | type User {
2 | firstname: String
3 | lastname: String
4 | }
5 |
6 | type Query {
7 | user: User
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/no-unused-variables-imported.gql:
--------------------------------------------------------------------------------
1 | fragment UserFields on User {
2 | firstName
3 | posts(limit: $limit, offset: $offset) {
4 | id
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/website/content/docs/usage/programmatic.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebarTitle: Programmatic
3 | icon: GearIcon
4 | ---
5 |
6 | # Programmatic usage
7 |
8 |
9 |
--------------------------------------------------------------------------------
/website/content/docs/usage/svelte.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebarTitle: Svelte
3 | icon: SvelteIcon
4 | ---
5 |
6 | # Usage with `.svelte` files
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/multiple-projects-graphql-config/schema.second-project.graphql:
--------------------------------------------------------------------------------
1 | type AnotherUser {
2 | firstName: String
3 | lastName: String
4 | }
5 |
6 | type Query {
7 | users: [AnotherUser]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/possible-type-extension/separate-code-files/extend-type-user.ts:
--------------------------------------------------------------------------------
1 | const EXTEND_USER = /* GraphQL */ `
2 | extend type User {
3 | firstName: String
4 | }
5 | `;
6 |
--------------------------------------------------------------------------------
/website/content/docs/usage/prettier.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebarTitle: Prettier
3 | icon: PrettierIcon
4 | ---
5 |
6 | # Usage with `eslint-plugin-prettier`
7 |
8 |
9 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/no-one-place-fragments.graphql:
--------------------------------------------------------------------------------
1 | fragment UserFields on User {
2 | id
3 | }
4 |
5 | {
6 | user {
7 | ...UserFields
8 | friends {
9 | ...UserFields
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/possible-type-extension/one-graphql-file/type-user.gql:
--------------------------------------------------------------------------------
1 | type User {
2 | id: ID!
3 | }
4 |
5 | extend type User {
6 | firstName: String
7 | }
8 |
9 | type Query {
10 | user: User
11 | }
12 |
--------------------------------------------------------------------------------
/website/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import("next-sitemap").IConfig} */
2 | export default {
3 | siteUrl: process.env.SITE_URL || 'https://the-guild.dev/graphql/eslint',
4 | generateIndexSitemap: false,
5 | output: 'export',
6 | };
7 |
--------------------------------------------------------------------------------
/packages/rule-tester/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup';
2 |
3 | export default defineConfig({
4 | name: 'eslint-rule-tester',
5 | entry: ['src/*.ts'],
6 | clean: true,
7 | format: 'esm',
8 | dts: true,
9 | });
10 |
--------------------------------------------------------------------------------
/website/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
6 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/import-fragments/valid-query-default.gql:
--------------------------------------------------------------------------------
1 | # Imports could have extra whitespace and double/single quotes
2 |
3 | # import './fragments/foo-fragment.gql'
4 |
5 | query {
6 | foo {
7 | ...FooFields
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | import prettierConfig from '@theguild/prettier-config';
2 |
3 | export default {
4 | ...prettierConfig,
5 | plugins: [...prettierConfig.plugins, 'prettier-plugin-tailwindcss'],
6 | tailwindConfig: './website/tailwind.config.ts',
7 | };
8 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | packages/plugin/__tests__/__snapshots__/
2 | packages/plugin/src/rules/*/snapshot.md
3 | examples/prettier/invalid.graphql
4 | examples/prettier/invalid.js
5 | pnpm-lock.yaml
6 |
7 | # prettier ignore doesn't work
8 | website/src/pages/docs/configs.mdx
9 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/import-fragments/valid-query.gql:
--------------------------------------------------------------------------------
1 | # Imports could have extra whitespace and double/single quotes
2 |
3 | # import FooFields from "./fragments/foo-fragment.gql"
4 |
5 | query {
6 | foo {
7 | ...FooFields
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/monorepo/server/types/user.gql:
--------------------------------------------------------------------------------
1 | type User {
2 | id: ID!
3 | firstName: String!
4 | lastName: String!
5 | createdAt: DateTime!
6 | updatedAt: DateTime!
7 | }
8 |
9 | extend type Query {
10 | user(id: ID!): User!
11 | users: [User!]!
12 | }
13 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/possible-type-extension/separate-code-files/type-user.ts:
--------------------------------------------------------------------------------
1 | const USER = /* GraphQL */ `
2 | type User {
3 | id: ID!
4 | }
5 | `;
6 |
7 | const QUERY = /* GraphQL */ `
8 | type Query {
9 | user: User
10 | }
11 | `;
12 |
--------------------------------------------------------------------------------
/website/content/docs/usage/schema-and-operations.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebarTitle: Schema and Operations
3 | icon: HalfIcon
4 | ---
5 |
6 | # Usage to lint both schema/operations
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/monorepo/server/types/post.gql:
--------------------------------------------------------------------------------
1 | type Post {
2 | id: ID!
3 | title: String!
4 | content: String!
5 | author: User!
6 | createdAt: DateTime!
7 | updatedAt: DateTime!
8 | }
9 |
10 | extend type Query {
11 | post(id: ID!): Post!
12 | posts: [Post!]!
13 | }
14 |
--------------------------------------------------------------------------------
/website/content/docs/usage/graphql.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebarTitle: GraphQL Files
3 | icon: GraphQLIcon
4 | ---
5 |
6 | # Usage with `.graphql` files
7 |
8 |
12 |
--------------------------------------------------------------------------------
/website/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import tailwindRadix from 'tailwindcss-radix';
2 | import tailwindConfig from '@theguild/tailwind-config';
3 |
4 | export default {
5 | ...tailwindConfig,
6 | // @ts-expect-error -- fixme
7 | plugins: [...tailwindConfig.plugins, tailwindRadix()],
8 | };
9 |
--------------------------------------------------------------------------------
/website/app/icons/vue.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/unique-fragment.js:
--------------------------------------------------------------------------------
1 | const USER_FIELDS = /* GraphQL */ `
2 | fragment UserFields on User {
3 | id
4 | }
5 | `;
6 |
7 | const GET_USER = /* GraphQL */ `
8 | query User {
9 | user {
10 | ...UserFields
11 | }
12 | }
13 | ${USER_FIELDS}
14 | `;
15 |
--------------------------------------------------------------------------------
/examples/multiple-projects-graphql-config/query.first-project.js:
--------------------------------------------------------------------------------
1 | import { gql } from 'graphql-tag';
2 |
3 | /* GraphQL */ `
4 | fragment UserFields on User {
5 | firstname
6 | lastname
7 | }
8 | `;
9 |
10 | gql`
11 | {
12 | user {
13 | ...UserFields
14 | }
15 | }
16 | `;
17 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/known-fragment-names.ts:
--------------------------------------------------------------------------------
1 | const USER_FIELDS = /* GraphQL */ `
2 | fragment UserFields on User {
3 | id
4 | }
5 | `;
6 |
7 | const GET_USER = /* GraphQL */ `
8 | query User {
9 | user {
10 | ...UserFields
11 | }
12 | }
13 | ${USER_FIELDS}
14 | `;
15 |
--------------------------------------------------------------------------------
/website/content/docs/usage/multiple-projects.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebarTitle: Multiple Projects
3 | icon: StackIcon
4 | ---
5 |
6 | # Usage to lint different schemas
7 |
8 |
12 |
--------------------------------------------------------------------------------
/examples/prettier/invalid.graphql:
--------------------------------------------------------------------------------
1 | query User($userId: ID!) {
2 | user(id: $userId) {
3 | id,
4 | name,
5 | isViewerFriend,
6 | profilePicture(size: 50) {
7 | ...PictureFields
8 | }
9 | }
10 | }
11 |
12 | fragment PictureFields on Picture {
13 | uri,
14 | width,
15 | height
16 | }
17 |
--------------------------------------------------------------------------------
/examples/code-file/query.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 |
3 | const GET_USER = /* GraphQL */ `
4 | query {
5 | user {
6 | name
7 | }
8 | }
9 | `;
10 |
11 | const GET_ANOTHER_USER = /* GraphQL */ `
12 | query UserQuery {
13 | user {
14 | id
15 | name
16 | }
17 | }
18 | `;
19 |
--------------------------------------------------------------------------------
/examples/multiple-projects-graphql-config/query.second-project.js:
--------------------------------------------------------------------------------
1 | import { custom } from 'custom-graphql-tag';
2 |
3 | /* MyGraphQL */ `
4 | fragment UserFields on AnotherUser {
5 | firstName
6 | lastName
7 | }
8 | `;
9 |
10 | custom`
11 | {
12 | users {
13 | ...UserFields
14 | }
15 | }
16 | `;
17 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/two-fragments-in-code-file.js:
--------------------------------------------------------------------------------
1 | const USER_FIELDS = /* GraphQL */ `
2 | fragment UserFields on User {
3 | id
4 | firstName
5 | }
6 | `;
7 |
8 | const ALL_USER_FIELDS = /* GraphQL */ `
9 | fragment UserFields on User {
10 | id
11 | firstName
12 | lastName
13 | }
14 | `;
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Have a question?
4 | url: https://github.com/dimaMachina/graphql-eslint/discussions/new
5 | about:
6 | Not sure about something? need help from the community? have a question to our team? please
7 | ask and answer questions here.
8 |
--------------------------------------------------------------------------------
/.whitesource:
--------------------------------------------------------------------------------
1 | {
2 | "scanSettings": {
3 | "baseBranches": []
4 | },
5 | "checkRunSettings": {
6 | "vulnerableCheckRunConclusionLevel": "failure",
7 | "displayMode": "diff",
8 | "useMendCheckNames": true
9 | },
10 | "issueSettings": {
11 | "minSeverityLevel": "LOW",
12 | "issueType": "DEPENDENCY"
13 | }
14 | }
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/user-schema.graphql:
--------------------------------------------------------------------------------
1 | type User {
2 | id: ID!
3 | firstName: String!
4 | posts(limit: Int = 25, offset: Int = 0): [Post!]!
5 | }
6 |
7 | type Post {
8 | id: ID!
9 | title: String!
10 | content: String!
11 | user: User!
12 | }
13 |
14 | type Query {
15 | user: User
16 | post: Post
17 | }
18 |
--------------------------------------------------------------------------------
/examples/custom-rules/my-rule.js:
--------------------------------------------------------------------------------
1 | export const rule = {
2 | create(context) {
3 | return {
4 | OperationDefinition(node) {
5 | if (!node.name?.value) {
6 | context.report({
7 | node,
8 | message: 'Oops, name is required!',
9 | });
10 | }
11 | },
12 | };
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/examples/svelte-code-file/test.svelte:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/known-fragment-names/user-fields.gql:
--------------------------------------------------------------------------------
1 | fragment UserFields on User {
2 | id
3 | ... on User {
4 | ...AnotherUserFields
5 | }
6 | posts {
7 | ... on Post {
8 | ...PostFields
9 | }
10 | }
11 | }
12 |
13 | fragment AnotherUserFields on User {
14 | id
15 | }
16 |
17 | fragment PostFields on Post {
18 | id
19 | }
20 |
--------------------------------------------------------------------------------
/website/content/docs/usage/custom-rules.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebarTitle: Custom GraphQL Rules
3 | ---
4 |
5 | # Usage with custom GraphQL rules
6 |
7 |
8 |
9 | > [!TIP]
10 | >
11 | > Check out the [custom rules](/docs/custom-rules) guide to learn how to create your own rules.
12 |
--------------------------------------------------------------------------------
/website/content/docs/usage/astro.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebarTitle: Astro
3 | icon: AstroIcon
4 | ---
5 |
6 | # Usage with `.astro` files
7 |
8 | > [!NOTE]
9 | >
10 | > GraphQL-ESLint should work with Astro files as well. Feel free to submit a PR with a
11 | > [new example](https://github.com/dimaMachina/graphql-eslint/tree/master/examples) in the
12 | > GraphQL-ESLint repository.
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 |
3 | # Dependency directories
4 | node_modules/
5 |
6 | # TypeScript cache
7 | *.tsbuildinfo
8 |
9 | # Optional eslint cache
10 | .eslintcache
11 |
12 | # dotenv environment variables file
13 | .env
14 | .next/
15 | dist/
16 | .bob/
17 | .idea/
18 | out/
19 | website/public/_redirects
20 | website/public/sitemap.xml
21 | website/public/robots.txt
22 | .turbo/
23 |
--------------------------------------------------------------------------------
/examples/monorepo/client/pages/index.tsx:
--------------------------------------------------------------------------------
1 | const GET_POSTS = /* GraphQL */ `
2 | query Posts {
3 | posts {
4 | id
5 | title
6 | content
7 | author {
8 | id
9 | firstname
10 | }
11 | }
12 | }
13 | `;
14 |
15 | export default function IndexPage() {
16 | return (
17 |
18 |
Hello world
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/__snapshots__/executable-definitions.spec.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`executable-definitions > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | type Query { t: String }
7 |
8 | #### ❌ Error
9 |
10 | > 1 | type Query { t: String }
11 | | ^^^^ The "Query" definition is not executable.
12 | `;
13 |
--------------------------------------------------------------------------------
/examples/prettier/invalid.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-unused-vars
2 | const GET_USER = /* GraphQL */ `query User($userId: ID!) {
3 | user(id: $userId) {
4 | id,
5 | name,
6 | isViewerFriend,
7 | profilePicture(size: 50) {
8 | ...PictureFields
9 | }
10 | }
11 | }
12 |
13 | fragment PictureFields on Picture {
14 | uri,
15 | width,
16 | height
17 | }
18 | `
19 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/user-schema.ts:
--------------------------------------------------------------------------------
1 | const SCHEMA = /* GraphQL */ `
2 | type User {
3 | id: ID!
4 | firstName: String!
5 | posts(limit: Int = 25, offset: Int = 0): [Post!]!
6 | }
7 |
8 | type Post {
9 | id: ID!
10 | title: String!
11 | content: String!
12 | user: User!
13 | }
14 |
15 | type Query {
16 | user: User
17 | post: Post
18 | }
19 | `;
20 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | branches: [master]
5 |
6 | jobs:
7 | stable:
8 | uses: the-guild-org/shared-config/.github/workflows/release-stable.yml@main
9 | with:
10 | releaseScript: release
11 | nodeVersion: 22
12 | packageManager: pnpm
13 | secrets:
14 | githubToken: ${{ secrets.GITHUB_TOKEN }}
15 | npmToken: ${{ secrets.NPM_TOKEN }}
16 |
--------------------------------------------------------------------------------
/packages/rule-tester/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "module": "Node16",
5 | "moduleResolution": "node16",
6 | "declaration": false,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "strict": true,
10 | "lib": ["ESNext"],
11 | "types": ["vitest/globals"],
12 | "resolveJsonModule": true,
13 | "skipLibCheck": true
14 | },
15 | "exclude": ["dist"]
16 | }
17 |
--------------------------------------------------------------------------------
/website/content/docs/vscode.mdx:
--------------------------------------------------------------------------------
1 | # VSCode Integration
2 |
3 | Use
4 | [ESLint VSCode extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
5 | to integrate ESLint into VSCode.
6 |
7 | For syntax highlighting you need a GraphQL extension (which may potentially have its own linting),
8 | for example
9 | [GraphQL (by GraphQL Foundation)](https://marketplace.visualstudio.com/items?itemName=GraphQL.vscode-graphql).
10 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/unique-enum-value-names/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ruleTester } from '../../../__tests__/test-utils.js';
2 | import { rule } from './index.js';
3 |
4 | ruleTester.run('unique-enum-value-names', rule, {
5 | valid: [],
6 | invalid: [
7 | {
8 | code: 'enum A { TEST TesT }',
9 | errors: 1,
10 | },
11 | {
12 | code: 'extend enum A { TEST TesT }',
13 | errors: 1,
14 | },
15 | ],
16 | });
17 |
--------------------------------------------------------------------------------
/packages/plugin/src/configs/schema-relay.ts:
--------------------------------------------------------------------------------
1 | import { Linter } from 'eslint';
2 |
3 | export default {
4 | parser: '@graphql-eslint/eslint-plugin',
5 | plugins: ['@graphql-eslint'],
6 | rules: {
7 | '@graphql-eslint/relay-arguments': 'error',
8 | '@graphql-eslint/relay-connection-types': 'error',
9 | '@graphql-eslint/relay-edge-types': 'error',
10 | '@graphql-eslint/relay-page-info': 'error',
11 | },
12 | } satisfies Linter.LegacyConfig;
13 |
--------------------------------------------------------------------------------
/examples/vue-code-file/test.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ GET_USER }}
4 | {{ GET_ANOTHER_USER }}
5 |
6 |
23 |
--------------------------------------------------------------------------------
/examples/monorepo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@graphql-eslint/example-monorepo",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "author": "Dimitri POSTOLOV",
6 | "private": true,
7 | "scripts": {
8 | "lint": "eslint --cache ."
9 | },
10 | "dependencies": {
11 | "graphql": "16.10.0"
12 | },
13 | "devDependencies": {
14 | "@eslint/js": "9.22.0",
15 | "@graphql-eslint/eslint-plugin": "workspace:*",
16 | "eslint": "9.22.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/multiple-projects-graphql-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@graphql-eslint/example-multiple-projects-graphql-config",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "author": "Dimitri POSTOLOV",
6 | "private": true,
7 | "scripts": {
8 | "lint": "eslint --cache ."
9 | },
10 | "dependencies": {
11 | "graphql": "16.10.0"
12 | },
13 | "devDependencies": {
14 | "@graphql-eslint/eslint-plugin": "workspace:*",
15 | "eslint": "9.22.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/mocks/user-fields-with-nested-fragment.gql:
--------------------------------------------------------------------------------
1 | fragment UserFields on User {
2 | ...AnotherUserFields
3 | posts {
4 | ...PostFields
5 | }
6 | }
7 |
8 | fragment AnotherUserFields on User {
9 | firstName
10 | }
11 |
12 | fragment PostFields on Post {
13 | id
14 | ...AnotherPostFields
15 | }
16 |
17 | fragment AnotherPostFields on Post {
18 | title
19 | ...YetAnotherPostFields
20 | }
21 |
22 | fragment YetAnotherPostFields on Post {
23 | content
24 | }
25 |
--------------------------------------------------------------------------------
/website/app/icons/half.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/examples/custom-rules/eslint.config.js:
--------------------------------------------------------------------------------
1 | import graphqlPlugin from '@graphql-eslint/eslint-plugin';
2 | import { rule } from './my-rule.js';
3 |
4 | export default [
5 | {
6 | files: ['**/*.graphql'],
7 | languageOptions: {
8 | parser: graphqlPlugin.parser,
9 | },
10 | plugins: {
11 | '@internal': {
12 | rules: {
13 | 'my-rule': rule,
14 | },
15 | },
16 | },
17 | rules: {
18 | '@internal/my-rule': 'error',
19 | },
20 | },
21 | ];
22 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/__snapshots__/known-directives.spec.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`known-directives > invalid > should work only with Kind.FIELD 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | scalar Foo @bad
7 |
8 | #### ⚙️ Options
9 |
10 | {
11 | "ignoreClientDirectives": [
12 | "bad"
13 | ]
14 | }
15 |
16 | #### ❌ Error
17 |
18 | > 1 | scalar Foo @bad
19 | | ^^^ Unknown directive "@bad".
20 | `;
21 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/no-anonymous-operations/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ruleTester } from '../../../__tests__/test-utils.js';
2 | import { rule } from './index.js';
3 |
4 | ruleTester.run('no-anonymous-operations', rule, {
5 | valid: ['query myQuery { a }', 'mutation doSomething { a }', 'subscription myData { a }'],
6 | invalid: [
7 | { code: 'query { a }', errors: 1 },
8 | { code: 'mutation { renamed: a }', errors: 1 },
9 | { code: 'subscription { ...someFragmentSpread }', errors: 1 },
10 | ],
11 | });
12 |
--------------------------------------------------------------------------------
/examples/custom-rules/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@graphql-eslint/example-custom-rules",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "repository": "https://github.com/dimaMachina/graphql-eslint",
6 | "author": "Dimitri POSTOLOV ",
7 | "private": true,
8 | "scripts": {
9 | "lint": "eslint --cache ."
10 | },
11 | "dependencies": {
12 | "graphql": "16.10.0"
13 | },
14 | "devDependencies": {
15 | "@graphql-eslint/eslint-plugin": "workspace:*",
16 | "eslint": "9.22.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/__snapshots__/known-fragment-names.spec.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`known-fragment-names > invalid > should not throw an error on undefined fragment 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | {
7 | 2 | user {
8 | 3 | ...DoesNotExist
9 | 4 | }
10 | 5 | }
11 |
12 | #### ❌ Error
13 |
14 | 2 | user {
15 | > 3 | ...DoesNotExist
16 | | ^^^^^^^^^^^^ Unknown fragment "DoesNotExist".
17 | 4 | }
18 | `;
19 |
--------------------------------------------------------------------------------
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool
4 | that works with multi-package repos, or single-package repos to help you version and publish your
5 | code. You can find the full documentation for it
6 | [in our repository](https://github.com/changesets/changesets)
7 |
8 | We have a quick list of common questions to get you started engaging with this project in
9 | [our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md)
10 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/__snapshots__/possible-type-extension.spec.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`possible-type-extension > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | extend type OtherUser {
7 | 2 | name: String
8 | 3 | }
9 |
10 | #### ❌ Error
11 |
12 | > 1 | extend type OtherUser {
13 | | ^^^^^^^^^ Cannot extend type "OtherUser" because it is not defined.
14 | 2 | name: String
15 | `;
16 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/no-one-place-fragments/snapshot.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`no-one-place-fragments > invalid > should error fragment used in one place 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | fragment UserFields on User {
7 | 2 | id
8 | 3 | firstName
9 | 4 | }
10 |
11 | #### ❌ Error
12 |
13 | > 1 | fragment UserFields on User {
14 | | ^^^^^^^^^^ Fragment \`UserFields\` used only once. Inline him in "146179389.graphql".
15 | 2 | id
16 | `;
17 |
--------------------------------------------------------------------------------
/examples/svelte-code-file/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@graphql-eslint/example-svelte-code-file",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "author": "Dimitri POSTOLOV",
6 | "private": true,
7 | "scripts": {
8 | "lint": "eslint --cache ."
9 | },
10 | "dependencies": {
11 | "graphql": "16.10.0"
12 | },
13 | "devDependencies": {
14 | "@graphql-eslint/eslint-plugin": "workspace:*",
15 | "eslint": "9.22.0",
16 | "svelte": "5.23.2",
17 | "svelte-eslint-parser": "1.3.0",
18 | "svelte2tsx": "0.7.35"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/code-file/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@graphql-eslint/example-code-file",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "repository": "https://github.com/dimaMachina/graphql-eslint",
6 | "author": "Dotan Simha ",
7 | "private": true,
8 | "scripts": {
9 | "lint": "eslint --cache ."
10 | },
11 | "dependencies": {
12 | "graphql": "16.10.0"
13 | },
14 | "devDependencies": {
15 | "@eslint/js": "9.22.0",
16 | "@graphql-eslint/eslint-plugin": "workspace:*",
17 | "eslint": "9.22.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/graphql-config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@graphql-eslint/example-graphql-config",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "repository": "https://github.com/dimaMachina/graphql-eslint",
6 | "author": "Dotan Simha ",
7 | "private": true,
8 | "scripts": {
9 | "lint": "eslint --cache ."
10 | },
11 | "dependencies": {
12 | "graphql": "16.10.0"
13 | },
14 | "devDependencies": {
15 | "@eslint/js": "9.22.0",
16 | "@graphql-eslint/eslint-plugin": "4.4.0",
17 | "eslint": "9.22.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/programmatic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@graphql-eslint/example-programmatic",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "repository": "https://github.com/dimaMachina/graphql-eslint",
6 | "author": "Dotan Simha ",
7 | "private": true,
8 | "scripts": {
9 | "lint": "eslint --cache ."
10 | },
11 | "dependencies": {
12 | "graphql": "16.10.0"
13 | },
14 | "devDependencies": {
15 | "@eslint/js": "9.22.0",
16 | "@graphql-eslint/eslint-plugin": "workspace:*",
17 | "eslint": "9.22.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@2.1.0/schema.json",
3 | "commit": false,
4 | "linked": [],
5 | "access": "restricted",
6 | "baseBranch": "master",
7 | "updateInternalDependencies": "patch",
8 | "ignore": ["@graphql-eslint/example-*", "website"],
9 | "changelog": [
10 | "@changesets/changelog-github",
11 | {
12 | "repo": "dimaMachina/graphql-eslint"
13 | }
14 | ],
15 | "snapshot": {
16 | "useCalculatedVersion": true,
17 | "prereleaseTemplate": "{tag}-{datetime}-{commit}"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/vue-code-file/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@graphql-eslint/example-vue-code-file",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "author": "Dimitri POSTOLOV",
6 | "private": true,
7 | "scripts": {
8 | "lint": "eslint --cache ."
9 | },
10 | "dependencies": {
11 | "graphql": "16.10.0"
12 | },
13 | "devDependencies": {
14 | "@graphql-eslint/eslint-plugin": "workspace:*",
15 | "eslint": "9.22.0",
16 | "eslint-merge-processors": "^2.0.0",
17 | "eslint-plugin-vue": "^10.0.0",
18 | "eslint-processor-vue-blocks": "^2.0.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/plugin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2022",
4 | "module": "nodenext",
5 | "moduleResolution": "node16",
6 | "declaration": false,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "strict": true,
10 | "lib": ["ESNext", "dom"],
11 | "types": ["vitest/globals"],
12 | "strictNullChecks": true,
13 | "resolveJsonModule": true,
14 | "skipLibCheck": true,
15 | "paths": {
16 | "@/*": ["./src/*"],
17 | "@graphql-eslint/eslint-plugin": ["./src/index.ts"]
18 | }
19 | },
20 | "exclude": ["dist"]
21 | }
22 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Debug Tests",
11 | "skipFiles": ["/**"],
12 | "program": "${workspaceFolder}/node_modules/.bin/vitest",
13 | "preLaunchTask": "tsc: build - tsconfig.json",
14 | "outFiles": []
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/__snapshots__/unique-type-names.spec.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`unique-type-names > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | type Query {
7 | 2 | foo: String
8 | 3 | }
9 | 4 |
10 | 5 | type Query {
11 | 6 | bar: Boolean
12 | 7 | }
13 |
14 | #### ❌ Error
15 |
16 | > 1 | type Query {
17 | | ^^^^^ There can be only one type named "Query".
18 | 2 | foo: String
19 | `;
20 |
--------------------------------------------------------------------------------
/examples/graphql-config/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js';
2 | import graphqlPlugin from '@graphql-eslint/eslint-plugin';
3 |
4 | export default [
5 | {
6 | files: ['**/*.js'],
7 | rules: js.configs.recommended.rules,
8 | },
9 | {
10 | files: ['**/*.graphql'],
11 | languageOptions: {
12 | parser: graphqlPlugin.parser,
13 | },
14 | plugins: {
15 | '@graphql-eslint': graphqlPlugin,
16 | },
17 | rules: {
18 | '@graphql-eslint/no-anonymous-operations': 'error',
19 | '@graphql-eslint/no-duplicate-fields': 'error',
20 | },
21 | },
22 | ];
23 |
--------------------------------------------------------------------------------
/.github/workflows/pr.yml:
--------------------------------------------------------------------------------
1 | name: PR
2 | on:
3 | pull_request:
4 | branches: [master]
5 |
6 | jobs:
7 | # dependencies:
8 | # uses: the-guild-org/shared-config/.github/workflows/changesets-dependencies.yaml@main
9 | # secrets:
10 | # githubToken: ${{ secrets.GUILD_BOT_TOKEN }}
11 |
12 | release:
13 | uses: the-guild-org/shared-config/.github/workflows/release-snapshot.yml@main
14 | with:
15 | npmTag: alpha
16 | buildScript: prerelease
17 | nodeVersion: 22
18 | packageManager: pnpm
19 | secrets:
20 | githubToken: ${{ secrets.GITHUB_TOKEN }}
21 | npmToken: ${{ secrets.NPM_TOKEN }}
22 |
--------------------------------------------------------------------------------
/examples/prettier/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@graphql-eslint/example-prettier",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "repository": "https://github.com/dimaMachina/graphql-eslint",
6 | "author": "JounQin ",
7 | "private": true,
8 | "scripts": {
9 | "lint": "eslint --cache ."
10 | },
11 | "dependencies": {
12 | "graphql": "16.10.0"
13 | },
14 | "devDependencies": {
15 | "@eslint/js": "9.22.0",
16 | "@graphql-eslint/eslint-plugin": "workspace:*",
17 | "eslint": "9.22.0",
18 | "eslint-config-prettier": "10.1.1",
19 | "eslint-plugin-prettier": "5.2.3",
20 | "prettier": "3.5.3"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.enable": true,
3 | "files.exclude": {
4 | "**/.git": true,
5 | "**/.DS_Store": true,
6 | "**/node_modules": true,
7 | "test-lib": true,
8 | "lib": true,
9 | "coverage": true,
10 | "npm": true,
11 | "**/dist": true
12 | },
13 | "typescript.tsdk": "node_modules/typescript/lib",
14 | "eslint.workingDirectories": [
15 | {
16 | "mode": "auto",
17 | "changeProcessCWD": true
18 | }
19 | ],
20 | "eslint.validate": [
21 | "javascript",
22 | "javascriptreact",
23 | "typescript",
24 | "typescriptreact",
25 | "graphql",
26 | "vue",
27 | "svelte"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest an idea for the core of this project
4 | ---
5 |
6 | **Is your feature request related to a problem? Please describe.**
7 |
8 |
9 |
10 | **Describe the solution you'd like**
11 |
12 |
13 |
14 | **Describe alternatives you've considered**
15 |
16 |
17 |
18 | **Additional context**
19 |
20 |
21 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/unique-operation-name/snapshot.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`unique-operation-name > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | query test { bar }
7 |
8 | #### ❌ Error
9 |
10 | > 1 | query test { bar }
11 | | ^^^^ Operation named "test" already defined in:
12 | 6844040.graphql
13 | `;
14 |
15 | exports[`unique-operation-name > invalid > Invalid #2 1`] = `
16 | #### ⌨️ Code
17 |
18 | 1 | query test { bar }
19 |
20 | #### ❌ Error
21 |
22 | > 1 | query test { bar }
23 | | ^^^^ Operation named "test" already defined in:
24 | 6844040.graphql
25 | 84823255.graphql
26 | `;
27 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "incremental": true,
4 | "baseUrl": ".",
5 | "strict": true,
6 | "esModuleInterop": true,
7 | "allowSyntheticDefaultImports": true,
8 | "importHelpers": true,
9 | "experimentalDecorators": true,
10 | "module": "Node16",
11 | "target": "es2019",
12 | "lib": ["ESNext"],
13 | "moduleResolution": "node",
14 | "emitDecoratorMetadata": true,
15 | "sourceMap": true,
16 | "declaration": true,
17 | "noImplicitThis": true,
18 | "alwaysStrict": true,
19 | "noImplicitReturns": true,
20 | "noUnusedLocals": true,
21 | "resolveJsonModule": true,
22 | "skipLibCheck": true
23 | },
24 | "include": ["scripts"]
25 | }
26 |
--------------------------------------------------------------------------------
/website/app/icons/javascript.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["github>the-guild-org/shared-config:renovate"],
4 | "automerge": true,
5 | "major": {
6 | "automerge": false
7 | },
8 | "lockFileMaintenance": {
9 | "enabled": true,
10 | "automerge": true
11 | },
12 | "packageRules": [
13 | {
14 | "matchUpdateTypes": ["minor", "patch"],
15 | "groupName": "all non-major dependencies",
16 | "groupSlug": "all-minor-patch",
17 | "matchPackageNames": [
18 | "!/@changesets/*/",
19 | "!/typescript/",
20 | "!/^@theguild//",
21 | "!/next/",
22 | "!/husky/",
23 | "*"
24 | ]
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/__snapshots__/no-undefined-variables.spec.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`no-undefined-variables > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | query User {
7 | 2 | user {
8 | 3 | id
9 | 4 | ...UserFields
10 | 5 | }
11 | 6 | }
12 |
13 | #### ❌ Error 1/2
14 |
15 | 2 | user {
16 | > 3 | id
17 | | ^ Variable "$limit" is not defined by operation "User".
18 | 4 | ...UserFields
19 |
20 | #### ❌ Error 2/2
21 |
22 | 2 | user {
23 | > 3 | id
24 | | ^ Variable "$offset" is not defined by operation "User".
25 | 4 | ...UserFields
26 | `;
27 |
--------------------------------------------------------------------------------
/examples/prettier/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Legacy config example, should be run with `ESLINT_USE_FLAT_CONFIG=false` environment variable in ESLint 9
3 | */
4 |
5 | module.exports = {
6 | root: true,
7 | overrides: [
8 | {
9 | files: ['*.js'],
10 | processor: '@graphql-eslint/graphql',
11 | extends: ['eslint:recommended', 'plugin:prettier/recommended'],
12 | env: {
13 | es2022: true,
14 | },
15 | parserOptions: {
16 | sourceType: 'module',
17 | },
18 | },
19 | {
20 | files: ['*.graphql'],
21 | parser: '@graphql-eslint/eslint-plugin',
22 | plugins: ['@graphql-eslint'],
23 | rules: {
24 | 'prettier/prettier': 'error',
25 | },
26 | },
27 | ],
28 | };
29 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/no-unused-variables.spec.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'node:path';
2 | import { GRAPHQL_JS_VALIDATIONS } from '../src/rules/graphql-js-validation.js';
3 | import { ruleTester } from './test-utils.js';
4 |
5 | ruleTester.run('no-unused-variables', GRAPHQL_JS_VALIDATIONS['no-unused-variables'], {
6 | valid: [
7 | {
8 | filename: join(__dirname, 'mocks/no-unused-variables.gql'),
9 | code: ruleTester.fromMockFile('no-unused-variables.gql'),
10 | parserOptions: {
11 | graphQLConfig: {
12 | schema: join(__dirname, 'mocks/user-schema.graphql'),
13 | documents: join(__dirname, 'mocks/user-fields-with-variables.gql'),
14 | },
15 | },
16 | },
17 | ],
18 | invalid: [],
19 | });
20 |
--------------------------------------------------------------------------------
/website/app/icons/graphql.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/website/content/docs/disabling-rules.mdx:
--------------------------------------------------------------------------------
1 | # Disabling Rules
2 |
3 | The `graphql-eslint` parser looks for GraphQL comments syntax (marked with `#`) and will send it to
4 | ESLint as directives. That means, you can use ESLint directives syntax to hint ESLint, just like in
5 | any other type of files.
6 |
7 | To disable ESLint for a specific line, you can do:
8 |
9 | ```graphql
10 | # eslint-disable-next-line
11 | type Query {
12 | foo: String!
13 | }
14 | ```
15 |
16 | You can also specify specific rules to disable, apply it over the entire file,
17 | `eslint-disable-next-line` or current `eslint-disable-line`.
18 |
19 | You can find a list of
20 | [ESLint directives here](https://eslint.org/docs/latest/user-guide/configuring/rules#using-configuration-comments-1).
21 |
--------------------------------------------------------------------------------
/packages/rule-tester/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@theguild/eslint-rule-tester",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "private": true,
6 | "engines": {
7 | "node": ">=18"
8 | },
9 | "exports": {
10 | "./package.json": "./package.json",
11 | ".": {
12 | "import": "./dist/index.js",
13 | "types": "./dist/index.d.ts"
14 | }
15 | },
16 | "scripts": {
17 | "build": "tsup",
18 | "dev": "tsup --watch",
19 | "typecheck": "tsc --noEmit"
20 | },
21 | "peerDependencies": {
22 | "eslint": "9.22.0"
23 | },
24 | "dependencies": {
25 | "@babel/code-frame": "^7.18.6"
26 | },
27 | "devDependencies": {
28 | "@types/babel__code-frame": "7.0.6",
29 | "@types/node": "22.13.10",
30 | "eslint": "9.22.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/website/content/rules/prettier.md:
--------------------------------------------------------------------------------
1 | # `prettier` Rule
2 |
3 | `eslint-plugin-prettier` supports `.graphql` files, and `v4.1.0` supports `graphql` blocks even
4 | better. You need to do the following:
5 |
6 | ```json filename=".eslintrc.json"
7 | {
8 | "overrides": [
9 | {
10 | "files": ["*.js"],
11 | "processor": "@graphql-eslint/graphql",
12 | "extends": ["plugin:prettier/recommended"]
13 | },
14 | {
15 | "files": ["*.graphql"],
16 | "parser": "@graphql-eslint/eslint-plugin",
17 | "plugins": ["@graphql-eslint"],
18 | "rules": {
19 | "prettier/prettier": "error"
20 | }
21 | }
22 | ]
23 | }
24 | ```
25 |
26 | You can take
27 | [`this repository`](https://github.com/dimaMachina/graphql-eslint/tree/master/examples/prettier) as
28 | example.
29 |
--------------------------------------------------------------------------------
/website/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2019",
4 | "module": "esnext",
5 | "strict": true,
6 | "esModuleInterop": true,
7 | "skipLibCheck": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "lib": ["dom", "dom.iterable", "esnext"],
10 | "allowJs": true,
11 | "noEmit": true,
12 | "moduleResolution": "bundler",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "baseUrl": ".",
18 | "paths": {
19 | "@icons/*": ["app/icons/*"]
20 | },
21 | "plugins": [
22 | {
23 | "name": "next"
24 | }
25 | ]
26 | },
27 | "include": ["**/*.ts", "**/*.tsx", "next-env.d.ts", ".next/types/**/*.ts"],
28 | "exclude": ["node_modules"]
29 | }
30 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/unique-fragment-name/snapshot.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`unique-fragment-name > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | fragment HasIdFields on U { a b c }
7 |
8 | #### ❌ Error
9 |
10 | > 1 | fragment HasIdFields on U { a b c }
11 | | ^^^^^^^^^^^ Fragment named "HasIdFields" already defined in:
12 | -1866344359.graphql
13 | `;
14 |
15 | exports[`unique-fragment-name > invalid > Invalid #2 1`] = `
16 | #### ⌨️ Code
17 |
18 | 1 | fragment HasIdFields on U { a b c }
19 |
20 | #### ❌ Error
21 |
22 | > 1 | fragment HasIdFields on U { a b c }
23 | | ^^^^^^^^^^^ Fragment named "HasIdFields" already defined in:
24 | -1559743294.graphql
25 | -1866344359.graphql
26 | `;
27 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "tasks": {
3 | "build": {
4 | "outputs": ["dist/**"],
5 | "dependsOn": [
6 | // Run `build` in workspaces I depend on first
7 | "^build"
8 | ]
9 | },
10 | "dev": {
11 | "dependsOn": [
12 | // Run `dev` in workspaces I depend on first
13 | "^dev"
14 | ],
15 | // Never cache anything (including logs) emitted by a `dev` task
16 | "cache": false,
17 | "persistent": true
18 | },
19 | "typecheck": {
20 | "dependsOn": [
21 | // Run `build` in workspaces I depend on first
22 | "^build"
23 | ]
24 | },
25 | "test": {
26 | "dependsOn": [
27 | // A workspace's `test` command depends on its own `build` commands first being completed
28 | "build"
29 | ]
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/website/app/graphql-config-info.mdx:
--------------------------------------------------------------------------------
1 | import { compileMdx, MDXRemote } from '@theguild/components/server'
2 |
3 | export async function GraphqlConfigInfo({ name, configKey = name }) {
4 | const rawMdx = `> [!NOTE]
5 | >
6 | > If you have a
7 | > [GraphQL Config configuration file](https://the-guild.dev/graphql/config/docs/user/usage) in your
8 | > project, the GraphQL-ESLint parser will automatically use it to load your GraphQL ${name}.
9 | >
10 | > You should specify a \`${configKey}\` key in the GraphQL Config configuration file to load your
11 | > GraphQL ${name}.
12 | >
13 | > Alternatively, you can pass GraphQL Config options programmatically via
14 | > \`languageOptions.parserOptions.graphQLConfig\` in your \`eslint.config.js\` file.`
15 |
16 | const rawJs = await compileMdx(rawMdx)
17 |
18 | return }
19 |
--------------------------------------------------------------------------------
/packages/plugin/src/index.ts:
--------------------------------------------------------------------------------
1 | import { configs } from './configs/index.js';
2 | import { parseForESLint, parser } from './parser.js';
3 | import { processor } from './processor.js';
4 | import { rules } from './rules/index.js';
5 |
6 | export * from './types.js';
7 | export type { IGraphQLConfig } from 'graphql-config';
8 | export type { GraphQLTagPluckOptions } from '@graphql-tools/graphql-tag-pluck';
9 |
10 | export { requireGraphQLSchema, requireGraphQLOperations } from './utils.js';
11 |
12 | export const processors = { graphql: processor };
13 |
14 | export { rules, configs, parser, parseForESLint };
15 |
16 | // eslint-disable-next-line import/no-default-export -- It's common practice for ESLint plugins that supports Flat config to use the default export
17 | export default {
18 | parser,
19 | processor,
20 | rules,
21 | configs,
22 | };
23 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/__snapshots__/lone-schema-definition.spec.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`lone-schema-definition > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | type Query {
7 | 2 | foo: String
8 | 3 | }
9 | 4 |
10 | 5 | schema {
11 | 6 | query: Query
12 | 7 | }
13 | 8 |
14 | 9 | type RootQuery {
15 | 10 | foo: String
16 | 11 | }
17 | 12 |
18 | 13 | schema {
19 | 14 | query: RootQuery
20 | 15 | }
21 |
22 | #### ❌ Error
23 |
24 | 12 |
25 | > 13 | schema {
26 | | ^^^^^^ Must provide only one schema definition.
27 | 14 | query: RootQuery
28 | `;
29 |
--------------------------------------------------------------------------------
/.github/labels.yml:
--------------------------------------------------------------------------------
1 | - color: 0366d6
2 | description: Pull requests that update a dependency file
3 | name: dependencies
4 | - color: 0075ca
5 | description: Improvements or additions to documentation
6 | name: documentation
7 | - color: cfd3d7
8 | description: This issue or pull request already exists
9 | name: duplicate
10 | - color: 7057ff
11 | description: Good for newcomers
12 | name: good first issue
13 | - color: 008672
14 | description: Extra attention is needed
15 | name: help wanted
16 | - color: e4e669
17 | description: This doesn't seem right
18 | name: invalid
19 | - color: 50e087
20 | name: new rule
21 | - color: d876e3
22 | description: Further information is requested
23 | name: question
24 | - color: f78ff0
25 | name: waiting for release
26 | - color: ffffff
27 | description: This will not be worked on
28 | name: wontfix
29 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/test-utils.ts:
--------------------------------------------------------------------------------
1 | import { Linter } from 'eslint';
2 | import graphqlPlugin, { ParserConfigGraphQLConfig } from '@graphql-eslint/eslint-plugin';
3 | import { RuleTester } from '@theguild/eslint-rule-tester';
4 |
5 | export const DEFAULT_CONFIG: Linter.Config = {
6 | languageOptions: {
7 | parser: graphqlPlugin.parser,
8 | },
9 | };
10 |
11 | export type ParserOptionsForTests = {
12 | graphQLConfig: Partial;
13 | };
14 |
15 | export const ruleTester = new RuleTester(DEFAULT_CONFIG);
16 |
17 | export function withSchema({ code, ...rest }: T) {
18 | return {
19 | code,
20 | parserOptions: {
21 | graphQLConfig: {
22 | schema: code,
23 | },
24 | } satisfies ParserOptionsForTests,
25 | ...rest,
26 | };
27 | }
28 |
--------------------------------------------------------------------------------
/website/app/icons/stack.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/federation.spec.ts:
--------------------------------------------------------------------------------
1 | import { GRAPHQL_JS_VALIDATIONS } from '../src/rules/graphql-js-validation.js';
2 | import { ruleTester, withSchema } from './test-utils.js';
3 |
4 | ruleTester.run('federation', GRAPHQL_JS_VALIDATIONS['known-directives'], {
5 | valid: [
6 | withSchema({
7 | name: 'should parse federation directive without errors',
8 | code: /* GraphQL */ `
9 | scalar DateTime
10 |
11 | type Post @key(fields: "id") {
12 | id: ID!
13 | title: String
14 | createdAt: DateTime
15 | modifiedAt: DateTime
16 | }
17 |
18 | type Query {
19 | post: Post!
20 | posts: [Post!]
21 | }
22 |
23 | extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key"])
24 | `,
25 | }),
26 | ],
27 | invalid: [],
28 | });
29 |
--------------------------------------------------------------------------------
/examples/multiple-projects-graphql-config/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js';
2 | import graphqlPlugin from '@graphql-eslint/eslint-plugin';
3 |
4 | export default [
5 | {
6 | files: ['**/*.js'],
7 | processor: graphqlPlugin.processor,
8 | rules: js.configs.recommended.rules,
9 | },
10 | {
11 | // Setup GraphQL Parser
12 | files: ['**/*.graphql'],
13 | languageOptions: {
14 | parser: graphqlPlugin.parser,
15 | },
16 | plugins: {
17 | '@graphql-eslint': graphqlPlugin,
18 | },
19 | },
20 | {
21 | files: ['schema.*.graphql'],
22 | rules: {
23 | ...graphqlPlugin.configs['flat/schema-recommended'].rules,
24 | '@graphql-eslint/require-description': 'off',
25 | },
26 | },
27 | {
28 | files: ['**/*.js/*.graphql'],
29 | rules: graphqlPlugin.configs['flat/operations-recommended'].rules,
30 | },
31 | ];
32 |
--------------------------------------------------------------------------------
/examples/prettier/eslint.config.js:
--------------------------------------------------------------------------------
1 | import prettierConfig from 'eslint-config-prettier';
2 | import prettierPlugin from 'eslint-plugin-prettier';
3 | import js from '@eslint/js';
4 | import graphqlPlugin from '@graphql-eslint/eslint-plugin';
5 |
6 | export default [
7 | {
8 | plugins: {
9 | prettier: { rules: prettierPlugin.rules },
10 | },
11 | },
12 | {
13 | files: ['**/*.js'],
14 | processor: graphqlPlugin.processor,
15 | rules: {
16 | ...js.configs.recommended.rules,
17 | ...prettierConfig.rules,
18 | ...prettierPlugin.configs.recommended.rules,
19 | },
20 | },
21 | {
22 | files: ['**/*.graphql'],
23 | languageOptions: {
24 | parser: graphqlPlugin.parser,
25 | },
26 | plugins: {
27 | '@graphql-eslint': graphqlPlugin,
28 | },
29 | rules: {
30 | 'prettier/prettier': 'error',
31 | },
32 | },
33 | ];
34 |
--------------------------------------------------------------------------------
/examples/multiple-projects-graphql-config/graphql.config.ts:
--------------------------------------------------------------------------------
1 | import type { GraphQLTagPluckOptions, IGraphQLConfig } from '@graphql-eslint/eslint-plugin';
2 |
3 | const config: IGraphQLConfig = {
4 | projects: {
5 | firstProject: {
6 | schema: 'schema.first-project.graphql',
7 | documents: 'query.first-project.js',
8 | },
9 | secondProject: {
10 | schema: 'schema.second-project.graphql',
11 | documents: 'query.second-project.js',
12 | extensions: {
13 | // in case you want to use different names for magic comment and module identifier
14 | pluckConfig: {
15 | modules: [{ name: 'custom-graphql-tag', identifier: 'custom' }],
16 | globalGqlIdentifierName: 'custom',
17 | gqlMagicComment: 'MyGraphQL',
18 | } satisfies GraphQLTagPluckOptions,
19 | },
20 | },
21 | },
22 | };
23 |
24 | export default config;
25 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/lone-schema-definition.spec.ts:
--------------------------------------------------------------------------------
1 | import { GRAPHQL_JS_VALIDATIONS } from '../src/rules/graphql-js-validation.js';
2 | import { ruleTester } from './test-utils.js';
3 |
4 | ruleTester.run('lone-schema-definition', GRAPHQL_JS_VALIDATIONS['lone-schema-definition'], {
5 | valid: [
6 | /* GraphQL */ `
7 | type Query {
8 | foo: String
9 | }
10 |
11 | schema {
12 | query: Query
13 | }
14 | `,
15 | ],
16 | invalid: [
17 | {
18 | code: /* GraphQL */ `
19 | type Query {
20 | foo: String
21 | }
22 |
23 | schema {
24 | query: Query
25 | }
26 |
27 | type RootQuery {
28 | foo: String
29 | }
30 |
31 | schema {
32 | query: RootQuery
33 | }
34 | `,
35 | errors: [{ message: 'Must provide only one schema definition.' }],
36 | },
37 | ],
38 | });
39 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/no-undefined-variables.spec.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'node:path';
2 | import { GRAPHQL_JS_VALIDATIONS } from '../src/rules/graphql-js-validation.js';
3 | import { ruleTester } from './test-utils.js';
4 |
5 | ruleTester.run('no-undefined-variables', GRAPHQL_JS_VALIDATIONS['no-undefined-variables'], {
6 | valid: [],
7 | invalid: [
8 | {
9 | filename: join(__dirname, 'mocks/no-undefined-variables.gql'),
10 | code: ruleTester.fromMockFile('no-undefined-variables.gql'),
11 | parserOptions: {
12 | graphQLConfig: {
13 | schema: join(__dirname, 'mocks/user-schema.graphql'),
14 | documents: join(__dirname, 'mocks/user-fields-with-variables.gql'),
15 | },
16 | },
17 | errors: [
18 | { message: 'Variable "$limit" is not defined by operation "User".' },
19 | { message: 'Variable "$offset" is not defined by operation "User".' },
20 | ],
21 | },
22 | ],
23 | });
24 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/unique-enum-value-names/snapshot.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`unique-enum-value-names > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | enum A { TEST TesT }
7 |
8 | #### ❌ Error
9 |
10 | > 1 | enum A { TEST TesT }
11 | | ^^^^ Unexpected case-insensitive enum values duplicates for enum value "TesT" in enum "A"
12 |
13 | #### 💡 Suggestion: Remove \`TesT\` enum value
14 |
15 | 1 | enum A { TEST }
16 | `;
17 |
18 | exports[`unique-enum-value-names > invalid > Invalid #2 1`] = `
19 | #### ⌨️ Code
20 |
21 | 1 | extend enum A { TEST TesT }
22 |
23 | #### ❌ Error
24 |
25 | > 1 | extend enum A { TEST TesT }
26 | | ^^^^ Unexpected case-insensitive enum values duplicates for enum value "TesT" in enum "A"
27 |
28 | #### 💡 Suggestion: Remove \`TesT\` enum value
29 |
30 | 1 | extend enum A { TEST }
31 | `;
32 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/no-unused-fragments.spec.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'node:path';
2 | import { GRAPHQL_JS_VALIDATIONS } from '../src/rules/graphql-js-validation.js';
3 | import { ruleTester } from './test-utils.js';
4 |
5 | ruleTester.run('no-unused-fragments', GRAPHQL_JS_VALIDATIONS['no-unused-fragments'], {
6 | valid: [
7 | {
8 | name: 'should find file with operation definition that import current fragment',
9 | filename: join(__dirname, 'mocks/user-fields.graphql'),
10 | code: ruleTester.fromMockFile('user-fields.graphql'),
11 | parserOptions: {
12 | graphQLConfig: {
13 | schema: join(__dirname, 'mocks/user-schema.graphql'),
14 | documents: [
15 | join(__dirname, 'mocks/user-fields.graphql'),
16 | join(__dirname, 'mocks/post-fields.graphql'),
17 | join(__dirname, 'mocks/post.graphql'),
18 | ],
19 | },
20 | },
21 | },
22 | ],
23 | invalid: [],
24 | });
25 |
--------------------------------------------------------------------------------
/examples/code-file/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js';
2 | import graphqlPlugin from '@graphql-eslint/eslint-plugin';
3 |
4 | export default [
5 | {
6 | files: ['**/*.js'],
7 | processor: graphqlPlugin.processor,
8 | rules: {
9 | ...js.configs.recommended.rules,
10 | 'no-console': 'error',
11 | },
12 | },
13 | {
14 | files: ['**/*.graphql'],
15 | languageOptions: {
16 | parser: graphqlPlugin.parser,
17 | },
18 | plugins: {
19 | '@graphql-eslint': graphqlPlugin,
20 | },
21 | rules: {
22 | '@graphql-eslint/no-anonymous-operations': 'error',
23 | '@graphql-eslint/naming-convention': [
24 | 'error',
25 | {
26 | OperationDefinition: {
27 | style: 'PascalCase',
28 | forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
29 | forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
30 | },
31 | },
32 | ],
33 | },
34 | },
35 | ];
36 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/require-nullable-fields-with-oneof/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ruleTester } from '../../../__tests__/test-utils.js';
2 | import { rule } from './index.js';
3 |
4 | ruleTester.run('require-nullable-fields-with-oneof', rule, {
5 | valid: [
6 | /* GraphQL */ `
7 | input Input @oneOf {
8 | foo: [String]
9 | bar: Int
10 | }
11 | `,
12 | /* GraphQL */ `
13 | type User @oneOf {
14 | foo: String
15 | bar: [Int!]
16 | }
17 | `,
18 | ],
19 | invalid: [
20 | {
21 | name: 'should validate `input`',
22 | code: /* GraphQL */ `
23 | input Input @oneOf {
24 | foo: String!
25 | bar: [Int]!
26 | }
27 | `,
28 | errors: 2,
29 | },
30 | {
31 | name: 'should validate `type`',
32 | code: /* GraphQL */ `
33 | type Type @oneOf {
34 | foo: String!
35 | bar: Int
36 | }
37 | `,
38 | errors: 1,
39 | },
40 | ],
41 | });
42 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/__snapshots__/eslint-directives.spec.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`test-directives > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | # eslint-disable-next-line non-existing-rule
7 | 2 | {
8 | 3 | a
9 | 4 | }
10 |
11 | #### ❌ Error 1/2
12 |
13 | > 1 | # eslint-disable-next-line non-existing-rule
14 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Definition for rule 'non-existing-rule' was not found.
15 | 2 | {
16 |
17 | #### ❌ Error 2/2
18 |
19 | 1 | # eslint-disable-next-line non-existing-rule
20 | > 2 | {
21 | | ^^^^^ Anonymous GraphQL operations are forbidden. Make sure to name your query!
22 | 3 | a
23 |
24 | #### 💡 Suggestion: Rename to \`a\`
25 |
26 | 1 | # eslint-disable-next-line non-existing-rule
27 | 2 | query a {
28 | 3 | a
29 | 4 | }
30 | `;
31 |
--------------------------------------------------------------------------------
/website/content/rules/unique-type-names.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL document is only valid if all defined types have unique names.'
3 | ---
4 |
5 | # `unique-type-names`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Schema`
11 | - Rule name: `@graphql-eslint/unique-type-names`
12 | - Requires GraphQL Schema: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | A GraphQL document is only valid if all defined types have unique names.
18 |
19 | > This rule is a wrapper around a `graphql-js` validation function.
20 |
21 | ## Resources
22 |
23 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueTypeNamesRule.ts)
24 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueTypeNamesRule-test.ts)
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # GraphQL-ESLint
4 |
5 | [](https://badge.fury.io/js/%40graphql-eslint%2Feslint-plugin)
6 |
7 | ## Documentation
8 |
9 | [https://the-guild.dev/graphql/eslint](https://the-guild.dev/graphql/eslint)
10 |
11 | ## Contributions
12 |
13 | Contributions, issues and feature requests are very welcome. If you are using this package and fixed
14 | a bug for yourself, please consider submitting a PR!
15 |
16 | And if this is your first time contributing to this project, please do read our
17 | [Contributor Workflow Guide](https://github.com/the-guild-org/Stack/blob/master/CONTRIBUTING.md)
18 | before you get started off.
19 |
20 | ## Code of Conduct
21 |
22 | Help us keep GraphQL ESLint open and inclusive. Please read and follow our
23 | [Code of Conduct](https://github.com/the-guild-org/Stack/blob/master/CODE_OF_CONDUCT.md) as adopted
24 | from [Contributor Covenant](https://contributor-covenant.org).
25 |
26 | ## License
27 |
28 | Released under the [MIT license](./LICENSE).
29 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/require-type-pattern-with-oneof/snapshot.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`require-type-pattern-with-oneof > invalid > should validate \`error\` field 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | type T @oneOf {
7 | 2 | ok: Ok
8 | 3 | err: Error
9 | 4 | }
10 |
11 | #### ❌ Error
12 |
13 | > 1 | type T @oneOf {
14 | | ^ type "T" is defined as output with "@oneOf" and must be defined with "error" field
15 | 2 | ok: Ok
16 | `;
17 |
18 | exports[`require-type-pattern-with-oneof > invalid > should validate \`ok\` field 1`] = `
19 | #### ⌨️ Code
20 |
21 | 1 | type T @oneOf {
22 | 2 | notok: Ok
23 | 3 | error: Error
24 | 4 | }
25 |
26 | #### ❌ Error
27 |
28 | > 1 | type T @oneOf {
29 | | ^ type "T" is defined as output with "@oneOf" and must be defined with "ok" field
30 | 2 | notok: Ok
31 | `;
32 |
--------------------------------------------------------------------------------
/website/content/rules/unique-operation-types.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL document is only valid if it has only one type per operation.'
3 | ---
4 |
5 | # `unique-operation-types`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Schema`
11 | - Rule name: `@graphql-eslint/unique-operation-types`
12 | - Requires GraphQL Schema: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | A GraphQL document is only valid if it has only one type per operation.
18 |
19 | > This rule is a wrapper around a `graphql-js` validation function.
20 |
21 | ## Resources
22 |
23 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueOperationTypesRule.ts)
24 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueOperationTypesRule-test.ts)
25 |
--------------------------------------------------------------------------------
/website/content/rules/variables-in-allowed-position.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Variables passed to field arguments conform to type.'
3 | ---
4 |
5 | # `variables-in-allowed-position`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Operations`
11 | - Rule name: `@graphql-eslint/variables-in-allowed-position`
12 | - Requires GraphQL Schema: `true`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | Variables passed to field arguments conform to type.
18 |
19 | > This rule is a wrapper around a `graphql-js` validation function.
20 |
21 | ## Resources
22 |
23 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesInAllowedPositionRule.ts)
24 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/VariablesInAllowedPositionRule-test.ts)
25 |
--------------------------------------------------------------------------------
/packages/plugin/src/configs/operations-all.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * 🚨 IMPORTANT! Do not manually modify this file. Run: `pnpm generate:configs`
3 | */
4 |
5 | import { Linter } from 'eslint';
6 |
7 | export default {
8 | extends: './configs/operations-recommended',
9 | rules: {
10 | '@graphql-eslint/alphabetize': [
11 | 'error',
12 | {
13 | definitions: true,
14 | selections: ['OperationDefinition', 'FragmentDefinition'],
15 | variables: true,
16 | arguments: ['Field', 'Directive'],
17 | groups: ['...', 'id', '*', '{'],
18 | },
19 | ],
20 | '@graphql-eslint/lone-executable-definition': 'error',
21 | '@graphql-eslint/match-document-filename': [
22 | 'error',
23 | {
24 | query: 'kebab-case',
25 | mutation: 'kebab-case',
26 | subscription: 'kebab-case',
27 | fragment: 'kebab-case',
28 | },
29 | ],
30 | '@graphql-eslint/no-one-place-fragments': 'error',
31 | '@graphql-eslint/require-import-fragment': 'error',
32 | },
33 | } satisfies Linter.LegacyConfig;
34 |
--------------------------------------------------------------------------------
/website/content/rules/lone-schema-definition.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL document is only valid if it contains only one schema definition.'
3 | ---
4 |
5 | # `lone-schema-definition`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Schema`
11 | - Rule name: `@graphql-eslint/lone-schema-definition`
12 | - Requires GraphQL Schema: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | A GraphQL document is only valid if it contains only one schema definition.
18 |
19 | > This rule is a wrapper around a `graphql-js` validation function.
20 |
21 | ## Resources
22 |
23 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/LoneSchemaDefinitionRule.ts)
24 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/LoneSchemaDefinitionRule-test.ts)
25 |
--------------------------------------------------------------------------------
/website/content/rules/no-fragment-cycles.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL fragment is only valid when it does not have cycles in fragments usage.'
3 | ---
4 |
5 | # `no-fragment-cycles`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Operations`
11 | - Rule name: `@graphql-eslint/no-fragment-cycles`
12 | - Requires GraphQL Schema: `true`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | A GraphQL fragment is only valid when it does not have cycles in fragments usage.
18 |
19 | > This rule is a wrapper around a `graphql-js` validation function.
20 |
21 | ## Resources
22 |
23 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/NoFragmentCyclesRule.ts)
24 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/NoFragmentCyclesRule-test.ts)
25 |
--------------------------------------------------------------------------------
/website/content/rules/unique-variable-names.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL operation is only valid if all its variables are uniquely named.'
3 | ---
4 |
5 | # `unique-variable-names`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Operations`
11 | - Rule name: `@graphql-eslint/unique-variable-names`
12 | - Requires GraphQL Schema: `true`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | A GraphQL operation is only valid if all its variables are uniquely named.
18 |
19 | > This rule is a wrapper around a `graphql-js` validation function.
20 |
21 | ## Resources
22 |
23 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueVariableNamesRule.ts)
24 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueVariableNamesRule-test.ts)
25 |
--------------------------------------------------------------------------------
/website/app/[[...mdxPath]]/page.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/rules-of-hooks -- false positive, useMDXComponents are not react hooks */
2 |
3 | import { generateStaticParamsFor, importPage } from '@theguild/components/pages';
4 | import { useMDXComponents } from '../../mdx-components';
5 |
6 | export const generateStaticParams = generateStaticParamsFor('mdxPath');
7 |
8 | export async function generateMetadata(props: Props) {
9 | const params = await props.params;
10 | const { metadata } = await importPage(params.mdxPath);
11 | return metadata;
12 | }
13 |
14 | const Wrapper = useMDXComponents().wrapper;
15 |
16 | type Props = {
17 | params: Promise<{
18 | mdxPath: string[];
19 | }>;
20 | };
21 |
22 | export default async function Page(props: Props) {
23 | const params = await props.params;
24 | const result = await importPage(params.mdxPath);
25 | const { default: MDXContent, toc, metadata } = result;
26 | return (
27 |
28 |
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/website/content/rules/unique-directive-names.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL document is only valid if all defined directives have unique names.'
3 | ---
4 |
5 | # `unique-directive-names`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Schema`
11 | - Rule name: `@graphql-eslint/unique-directive-names`
12 | - Requires GraphQL Schema: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | A GraphQL document is only valid if all defined directives have unique names.
18 |
19 | > This rule is a wrapper around a `graphql-js` validation function.
20 |
21 | ## Resources
22 |
23 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueDirectiveNamesRule.ts)
24 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueDirectiveNamesRule-test.ts)
25 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/description-style/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ruleTester } from '../../../__tests__/test-utils.js';
2 | import { rule, RuleOptions } from './index.js';
3 |
4 | const INLINE_SDL = /* GraphQL */ `
5 | " Test "
6 | type CreateOneUserPayload {
7 | "Created document ID"
8 | recordId: MongoID
9 |
10 | "Created document"
11 | record: User
12 | }
13 | `;
14 |
15 | export const BLOCK_SDL = /* GraphQL */ `
16 | enum EnumUserLanguagesSkill {
17 | """
18 | basic
19 | """
20 | basic
21 | """
22 | fluent
23 | """
24 | fluent
25 | """
26 | native
27 | """
28 | native
29 | }
30 | `;
31 |
32 | ruleTester.run('description-style', rule, {
33 | valid: [
34 | BLOCK_SDL,
35 | {
36 | code: INLINE_SDL,
37 | options: [{ style: 'inline' }],
38 | },
39 | ],
40 | invalid: [
41 | {
42 | code: BLOCK_SDL,
43 | options: [{ style: 'inline' }],
44 | errors: 3,
45 | },
46 | {
47 | code: INLINE_SDL,
48 | errors: 3,
49 | },
50 | ],
51 | });
52 |
--------------------------------------------------------------------------------
/website/app/icons/astro.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/website/app/play/button.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentProps, forwardRef } from 'react';
2 | import { clsx } from 'clsx';
3 |
4 | export const Button = forwardRef>(
5 | ({ children, className, ...props }, ref) => (
6 |
23 | ),
24 | );
25 |
26 | Button.displayName = 'Button';
27 |
--------------------------------------------------------------------------------
/website/content/rules/one-field-subscriptions.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL subscription is valid only if it contains a single root field.'
3 | ---
4 |
5 | # `one-field-subscriptions`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Operations`
11 | - Rule name: `@graphql-eslint/one-field-subscriptions`
12 | - Requires GraphQL Schema: `true`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | A GraphQL subscription is valid only if it contains a single root field.
18 |
19 | > This rule is a wrapper around a `graphql-js` validation function.
20 |
21 | ## Resources
22 |
23 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/SingleFieldSubscriptionsRule.ts)
24 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/SingleFieldSubscriptionsRule-test.ts)
25 |
--------------------------------------------------------------------------------
/website/content/rules/deprecated-rules.md:
--------------------------------------------------------------------------------
1 | # Deprecated Rules
2 |
3 | ## `avoid-duplicate-fields`
4 |
5 | This rule was renamed to [`no-duplicate-fields`](/rules/no-duplicate-fields).
6 |
7 | ## `avoid-scalar-result-type-on-mutation`
8 |
9 | This rule was renamed to
10 | [`no-scalar-result-type-on-mutation`](/rules/no-scalar-result-type-on-mutation).
11 |
12 | ## `avoid-typename-prefix`
13 |
14 | This rule was renamed to [`no-typename-prefix`](/rules/no-typename-prefix).
15 |
16 | ## `avoid-operation-name-prefix`
17 |
18 | This rule was removed because the same things can be validated using
19 | [`naming-convention`](/rules/naming-convention).
20 |
21 | ## `no-operation-name-suffix`
22 |
23 | This rule was removed because the same things can be validated using
24 | [`naming-convention`](/rules/naming-convention).
25 |
26 | ## `require-id-when-available`
27 |
28 | This rule was renamed to [`require-selections`](/rules/require-selections).
29 |
30 | ## `no-case-insensitive-enum-values-duplicates`
31 |
32 | This rule was renamed to [`unique-enum-value-names`](/rules/unique-enum-value-names).
33 |
--------------------------------------------------------------------------------
/website/content/rules/unique-field-definition-names.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL complex type is only valid if all its fields are uniquely named.'
3 | ---
4 |
5 | # `unique-field-definition-names`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Schema`
11 | - Rule name: `@graphql-eslint/unique-field-definition-names`
12 | - Requires GraphQL Schema: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | A GraphQL complex type is only valid if all its fields are uniquely named.
18 |
19 | > This rule is a wrapper around a `graphql-js` validation function.
20 |
21 | ## Resources
22 |
23 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueFieldDefinitionNamesRule.ts)
24 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueFieldDefinitionNamesRule-test.ts)
25 |
--------------------------------------------------------------------------------
/website/content/rules/unique-argument-names.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL field or directive is only valid if all supplied arguments are uniquely named.'
4 | ---
5 |
6 | # `unique-argument-names`
7 |
8 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
9 | enables this rule.
10 |
11 | - Category: `Operations`
12 | - Rule name: `@graphql-eslint/unique-argument-names`
13 | - Requires GraphQL Schema: `true`
14 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
15 | - Requires GraphQL Operations: `false`
16 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
17 |
18 | A GraphQL field or directive is only valid if all supplied arguments are uniquely named.
19 |
20 | > This rule is a wrapper around a `graphql-js` validation function.
21 |
22 | ## Resources
23 |
24 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueArgumentNamesRule.ts)
25 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueArgumentNamesRule-test.ts)
26 |
--------------------------------------------------------------------------------
/website/content/rules/unique-input-field-names.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL input object value is only valid if all supplied fields are uniquely named.'
3 | ---
4 |
5 | # `unique-input-field-names`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Operations`
11 | - Rule name: `@graphql-eslint/unique-input-field-names`
12 | - Requires GraphQL Schema: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | A GraphQL input object value is only valid if all supplied fields are uniquely named.
18 |
19 | > This rule is a wrapper around a `graphql-js` validation function.
20 |
21 | ## Resources
22 |
23 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueInputFieldNamesRule.ts)
24 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueInputFieldNamesRule-test.ts)
25 |
--------------------------------------------------------------------------------
/examples/graphql-config/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Legacy config example, should be run with `ESLINT_USE_FLAT_CONFIG=false` environment variable in ESLint 9
3 | */
4 |
5 | module.exports = {
6 | root: true,
7 | // ❗️ It's very important that you don't have any rules configured at the top-level config,
8 | // and to move all configurations into the overrides section. Since JavaScript rules
9 | // can't run on GraphQL files and vice versa, if you have rules configured at the top level,
10 | // they will try to also execute for all overrides, as ESLint's configs cascade
11 | overrides: [
12 | {
13 | files: ['*.js'],
14 | extends: ['eslint:recommended'],
15 | env: {
16 | es2022: true,
17 | },
18 | parserOptions: {
19 | sourceType: 'module',
20 | },
21 | },
22 | {
23 | files: ['*.graphql'],
24 | parser: '@graphql-eslint/eslint-plugin',
25 | plugins: ['@graphql-eslint'],
26 | rules: {
27 | '@graphql-eslint/no-anonymous-operations': 'error',
28 | '@graphql-eslint/no-duplicate-fields': 'error',
29 | },
30 | },
31 | ],
32 | };
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Dimitri POSTOLOV
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/__snapshots__/fields-on-correct-type.spec.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`fields-on-correct-type > invalid > should highlight selection on multi line 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | {
7 | 2 | user {
8 | 3 | id
9 | 4 | veryBad
10 | 5 | age
11 | 6 | }
12 | 7 | }
13 |
14 | #### ❌ Error
15 |
16 | 3 | id
17 | > 4 | veryBad
18 | | ^^^^^^^ Cannot query field "veryBad" on type "User".
19 | 5 | age
20 | `;
21 |
22 | exports[`fields-on-correct-type > invalid > should highlight selection on single line 1`] = `
23 | #### ⌨️ Code
24 |
25 | 1 | fragment UserFields on User { id bad age }
26 |
27 | #### ❌ Error
28 |
29 | > 1 | fragment UserFields on User { id bad age }
30 | | ^^^ Cannot query field "bad" on type "User". Did you mean "id"?
31 |
32 | #### 💡 Suggestion: Rename to \`id\`
33 |
34 | 1 | fragment UserFields on User { id id age }
35 | `;
36 |
--------------------------------------------------------------------------------
/website/content/rules/executable-definitions.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL document is only valid for execution if all definitions are either operation or
4 | fragment definitions.'
5 | ---
6 |
7 | # `executable-definitions`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | - Category: `Operations`
13 | - Rule name: `@graphql-eslint/executable-definitions`
14 | - Requires GraphQL Schema: `true`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
16 | - Requires GraphQL Operations: `false`
17 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
18 |
19 | A GraphQL document is only valid for execution if all definitions are either operation or fragment
20 | definitions.
21 |
22 | > This rule is a wrapper around a `graphql-js` validation function.
23 |
24 | ## Resources
25 |
26 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/ExecutableDefinitionsRule.ts)
27 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/ExecutableDefinitionsRule-test.ts)
28 |
--------------------------------------------------------------------------------
/website/content/rules/no-unused-variables.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL operation is only valid if all variables defined by an operation are used, either
4 | directly or within a spread fragment.'
5 | ---
6 |
7 | # `no-unused-variables`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | - Category: `Operations`
13 | - Rule name: `@graphql-eslint/no-unused-variables`
14 | - Requires GraphQL Schema: `true`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
16 | - Requires GraphQL Operations: `true`
17 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
18 |
19 | A GraphQL operation is only valid if all variables defined by an operation are used, either directly
20 | or within a spread fragment.
21 |
22 | > This rule is a wrapper around a `graphql-js` validation function.
23 |
24 | ## Resources
25 |
26 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/NoUnusedVariablesRule.ts)
27 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/NoUnusedVariablesRule-test.ts)
28 |
--------------------------------------------------------------------------------
/examples/monorepo/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js';
2 | import graphqlPlugin from '@graphql-eslint/eslint-plugin';
3 |
4 | export default [
5 | {
6 | files: ['**/*.{js,tsx}'],
7 | rules: js.configs.recommended.rules,
8 | },
9 | {
10 | files: ['client/**/*.tsx'],
11 | // Setup processor for operations/fragments definitions on code-files
12 | processor: graphqlPlugin.processor,
13 | languageOptions: {
14 | parserOptions: {
15 | ecmaFeatures: {
16 | jsx: true,
17 | },
18 | },
19 | },
20 | },
21 | {
22 | // Setup GraphQL Parser
23 | files: ['**/*.{graphql,gql}'],
24 | languageOptions: {
25 | parser: graphqlPlugin.parser,
26 | },
27 | plugins: {
28 | '@graphql-eslint': graphqlPlugin,
29 | },
30 | },
31 | {
32 | // Setup recommended config for schema files
33 | files: ['server/**/*.gql'],
34 | rules: graphqlPlugin.configs['flat/schema-recommended'].rules,
35 | },
36 | {
37 | // Setup recommended config for operations files
38 | files: ['client/**/*.{graphql,gql}'],
39 | rules: graphqlPlugin.configs['flat/operations-recommended'].rules,
40 | },
41 | ];
42 |
--------------------------------------------------------------------------------
/website/content/rules/variables-are-input-types.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL operation is only valid if all the variables it defines are of input types (scalar,
4 | enum, or input object).'
5 | ---
6 |
7 | # `variables-are-input-types`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | - Category: `Operations`
13 | - Rule name: `@graphql-eslint/variables-are-input-types`
14 | - Requires GraphQL Schema: `true`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
16 | - Requires GraphQL Operations: `false`
17 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
18 |
19 | A GraphQL operation is only valid if all the variables it defines are of input types (scalar, enum,
20 | or input object).
21 |
22 | > This rule is a wrapper around a `graphql-js` validation function.
23 |
24 | ## Resources
25 |
26 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesAreInputTypesRule.ts)
27 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/VariablesAreInputTypesRule-test.ts)
28 |
--------------------------------------------------------------------------------
/examples/svelte-code-file/eslint.config.js:
--------------------------------------------------------------------------------
1 | import svelteParser from 'svelte-eslint-parser';
2 | import js from '@eslint/js';
3 | import graphqlPlugin from '@graphql-eslint/eslint-plugin';
4 |
5 | export default [
6 | {
7 | files: ['**/*.js', '**/*.svelte'],
8 | processor: graphqlPlugin.processor,
9 | rules: js.configs.recommended.rules,
10 | },
11 | {
12 | files: ['**/*.svelte'],
13 | languageOptions: {
14 | parser: svelteParser,
15 | },
16 | },
17 | {
18 | files: ['**/*.graphql'],
19 | languageOptions: {
20 | parser: graphqlPlugin.parser,
21 | },
22 | plugins: {
23 | '@graphql-eslint': graphqlPlugin,
24 | },
25 | rules: {
26 | '@graphql-eslint/no-anonymous-operations': 'error',
27 | '@graphql-eslint/no-duplicate-fields': 'error',
28 | '@graphql-eslint/naming-convention': [
29 | 'error',
30 | {
31 | OperationDefinition: {
32 | style: 'PascalCase',
33 | forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
34 | forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
35 | },
36 | },
37 | ],
38 | },
39 | },
40 | ];
41 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/no-one-place-fragments/index.test.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'node:path';
2 | import { CWD } from '@/utils.js';
3 | import { ruleTester } from '../../../__tests__/test-utils.js';
4 | import { rule } from './index.js';
5 |
6 | ruleTester.run('no-one-place-fragments', rule, {
7 | valid: [
8 | {
9 | name: 'ok when spread 2 times',
10 | code: ruleTester.fromMockFile('no-one-place-fragments.graphql'),
11 | parserOptions: {
12 | graphQLConfig: {
13 | documents: join(CWD, '__tests__/mocks/no-one-place-fragments.graphql'),
14 | },
15 | },
16 | },
17 | ],
18 | invalid: [
19 | {
20 | name: 'should error fragment used in one place',
21 | code: ruleTester.fromMockFile('user-fields.graphql'),
22 | errors: [
23 | { message: 'Fragment `UserFields` used only once. Inline him in "146179389.graphql".' },
24 | ],
25 | parserOptions: {
26 | graphQLConfig: {
27 | documents: /* GraphQL */ `
28 | {
29 | user {
30 | ...UserFields
31 | }
32 | }
33 | `,
34 | },
35 | },
36 | },
37 | ],
38 | });
39 |
--------------------------------------------------------------------------------
/website/content/rules/possible-type-extension.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A type extension is only valid if the type is defined and has the same kind.'
3 | ---
4 |
5 | # `possible-type-extension`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | 💡 This rule provides
11 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
12 |
13 | - Category: `Schema`
14 | - Rule name: `@graphql-eslint/possible-type-extension`
15 | - Requires GraphQL Schema: `true`
16 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
17 | - Requires GraphQL Operations: `false`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
19 |
20 | A type extension is only valid if the type is defined and has the same kind.
21 |
22 | > This rule is a wrapper around a `graphql-js` validation function.
23 |
24 | ## Resources
25 |
26 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/PossibleTypeExtensionsRule.ts)
27 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/PossibleTypeExtensionsRule-test.ts)
28 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/executable-definitions.spec.ts:
--------------------------------------------------------------------------------
1 | import { GRAPHQL_JS_VALIDATIONS } from '../src/rules/graphql-js-validation.js';
2 | import { ParserOptionsForTests, ruleTester } from './test-utils.js';
3 |
4 | const TEST_SCHEMA = /* GraphQL */ `
5 | type Query {
6 | foo: String!
7 | bar: String!
8 | }
9 |
10 | type Mutation {
11 | foo: String!
12 | }
13 |
14 | type T {
15 | foo: String!
16 | }
17 | `;
18 |
19 | const WITH_SCHEMA = {
20 | parserOptions: {
21 | graphQLConfig: {
22 | schema: TEST_SCHEMA,
23 | },
24 | } satisfies ParserOptionsForTests,
25 | };
26 |
27 | ruleTester.run('executable-definitions', GRAPHQL_JS_VALIDATIONS['executable-definitions'], {
28 | valid: [
29 | {
30 | ...WITH_SCHEMA,
31 | code: 'query test2 { foo }',
32 | },
33 | {
34 | ...WITH_SCHEMA,
35 | code: 'mutation test { foo }',
36 | },
37 | {
38 | ...WITH_SCHEMA,
39 | code: 'fragment Test on T { foo }',
40 | },
41 | ],
42 | invalid: [
43 | {
44 | ...WITH_SCHEMA,
45 | code: 'type Query { t: String }',
46 | errors: [{ message: 'The "Query" definition is not executable.' }],
47 | },
48 | ],
49 | });
50 |
--------------------------------------------------------------------------------
/website/content/rules/no-undefined-variables.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL operation is only valid if all variables encountered, both directly and via fragment
4 | spreads, are defined by that operation.'
5 | ---
6 |
7 | # `no-undefined-variables`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | - Category: `Operations`
13 | - Rule name: `@graphql-eslint/no-undefined-variables`
14 | - Requires GraphQL Schema: `true`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
16 | - Requires GraphQL Operations: `true`
17 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
18 |
19 | A GraphQL operation is only valid if all variables encountered, both directly and via fragment
20 | spreads, are defined by that operation.
21 |
22 | > This rule is a wrapper around a `graphql-js` validation function.
23 |
24 | ## Resources
25 |
26 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/NoUndefinedVariablesRule.ts)
27 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/NoUndefinedVariablesRule-test.ts)
28 |
--------------------------------------------------------------------------------
/website/content/rules/no-unused-fragments.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL document is only valid if all fragment definitions are spread within operations, or
4 | spread within other fragments spread within operations.'
5 | ---
6 |
7 | # `no-unused-fragments`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | - Category: `Operations`
13 | - Rule name: `@graphql-eslint/no-unused-fragments`
14 | - Requires GraphQL Schema: `true`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
16 | - Requires GraphQL Operations: `true`
17 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
18 |
19 | A GraphQL document is only valid if all fragment definitions are spread within operations, or spread
20 | within other fragments spread within operations.
21 |
22 | > This rule is a wrapper around a `graphql-js` validation function.
23 |
24 | ## Resources
25 |
26 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/NoUnusedFragmentsRule.ts)
27 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/NoUnusedFragmentsRule-test.ts)
28 |
--------------------------------------------------------------------------------
/packages/plugin/vite.config.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import tsconfigPaths from 'vite-tsconfig-paths';
3 | import { defineConfig } from 'vitest/config';
4 |
5 | const GRAPHQL_PATH = path.join(__dirname, 'node_modules', 'graphql');
6 |
7 | export default defineConfig({
8 | test: {
9 | globals: true,
10 | resolveSnapshotPath(testPath) {
11 | if (testPath.endsWith('/index.test.ts')) {
12 | return testPath.replace('/index.test.ts', '/snapshot.md');
13 | }
14 |
15 | return testPath.replace('__tests__/', '__tests__/__snapshots__/').replace(/\.ts$/, '.md');
16 | },
17 | setupFiles: ['./serializer.ts'],
18 | alias: {
19 | // fixes Duplicate "graphql" modules cannot be used at the same time since different
20 | 'graphql/validation/index.js': path.join(GRAPHQL_PATH, 'validation', 'index.js'),
21 | 'graphql/validation/validate.js': path.join(GRAPHQL_PATH, 'validation', 'validate.js'),
22 | 'graphql/utilities/valueFromASTUntyped.js': path.join(
23 | GRAPHQL_PATH,
24 | 'utilities',
25 | 'valueFromASTUntyped.js',
26 | ),
27 | graphql: path.join(GRAPHQL_PATH, 'index.js'),
28 | },
29 | },
30 | plugins: [tsconfigPaths()],
31 | });
32 |
--------------------------------------------------------------------------------
/examples/multiple-projects-graphql-config/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Legacy config example, should be run with `ESLINT_USE_FLAT_CONFIG=false` environment variable in ESLint 9
3 | */
4 |
5 | module.exports = {
6 | root: true,
7 | // ❗️ It's very important that you don't have any rules configured at the top-level config,
8 | // and to move all configurations into the overrides section. Since JavaScript rules
9 | // can't run on GraphQL files and vice versa, if you have rules configured at the top level,
10 | // they will try to also execute for all overrides, as ESLint's configs cascade
11 | overrides: [
12 | {
13 | files: ['*.js'],
14 | processor: '@graphql-eslint/graphql',
15 | extends: ['eslint:recommended'],
16 | env: {
17 | es2022: true,
18 | },
19 | parserOptions: {
20 | sourceType: 'module',
21 | },
22 | },
23 | {
24 | files: ['schema.*.graphql'],
25 | extends: ['plugin:@graphql-eslint/schema-recommended'],
26 | rules: {
27 | '@graphql-eslint/require-description': 'off',
28 | },
29 | },
30 | {
31 | files: ['*.js/*.graphql'],
32 | extends: ['plugin:@graphql-eslint/operations-recommended'],
33 | },
34 | ],
35 | };
36 |
--------------------------------------------------------------------------------
/website/content/rules/lone-anonymous-operation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL document that contains an anonymous operation (the `query` short-hand) is only valid if
4 | it contains only that one operation definition.'
5 | ---
6 |
7 | # `lone-anonymous-operation`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | - Category: `Operations`
13 | - Rule name: `@graphql-eslint/lone-anonymous-operation`
14 | - Requires GraphQL Schema: `true`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
16 | - Requires GraphQL Operations: `false`
17 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
18 |
19 | A GraphQL document that contains an anonymous operation (the `query` short-hand) is only valid if it
20 | contains only that one operation definition.
21 |
22 | > This rule is a wrapper around a `graphql-js` validation function.
23 |
24 | ## Resources
25 |
26 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/LoneAnonymousOperationRule.ts)
27 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/LoneAnonymousOperationRule-test.ts)
28 |
--------------------------------------------------------------------------------
/packages/plugin/src/cache.ts:
--------------------------------------------------------------------------------
1 | // Based on the `eslint-plugin-import`'s cache
2 | // https://github.com/import-js/eslint-plugin-import/blob/main/utils/ModuleCache.js
3 | import debugFactory from 'debug';
4 |
5 | const log = debugFactory('graphql-eslint:ModuleCache');
6 |
7 | export class ModuleCache {
8 | map = new Map();
9 |
10 | set(cacheKey: K, result: T): void {
11 | // Remove server-side cache code in browser
12 | if (typeof window !== 'undefined') return;
13 |
14 | this.map.set(cacheKey, { lastSeen: process.hrtime(), result });
15 | log('setting entry for', cacheKey);
16 | }
17 |
18 | get(cacheKey: K, settings = { lifetime: 10 /* seconds */ }): T | void {
19 | // Remove server-side cache code in browser
20 | if (typeof window !== 'undefined') return;
21 |
22 | const value = this.map.get(cacheKey);
23 | if (!value) {
24 | log('cache miss for', cacheKey);
25 | return;
26 | }
27 | const { lastSeen, result } = value;
28 | // check freshness
29 | if (
30 | process.env.NODE /* don't check for ESLint CLI */ ||
31 | process.hrtime(lastSeen)[0] < settings.lifetime
32 | ) {
33 | return result;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/website/content/rules/scalar-leafs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL document is valid only if all leaf fields (fields without sub selections) are of scalar
4 | or enum types.'
5 | ---
6 |
7 | # `scalar-leafs`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | 💡 This rule provides
13 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
14 |
15 | - Category: `Operations`
16 | - Rule name: `@graphql-eslint/scalar-leafs`
17 | - Requires GraphQL Schema: `true`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
19 | - Requires GraphQL Operations: `false`
20 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
21 |
22 | A GraphQL document is valid only if all leaf fields (fields without sub selections) are of scalar or
23 | enum types.
24 |
25 | > This rule is a wrapper around a `graphql-js` validation function.
26 |
27 | ## Resources
28 |
29 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/ScalarLeafsRule.ts)
30 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/ScalarLeafsRule-test.ts)
31 |
--------------------------------------------------------------------------------
/packages/plugin/src/configs/schema-all.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * 🚨 IMPORTANT! Do not manually modify this file. Run: `pnpm generate:configs`
3 | */
4 |
5 | import { Linter } from 'eslint';
6 |
7 | export default {
8 | extends: './configs/schema-recommended',
9 | rules: {
10 | '@graphql-eslint/alphabetize': [
11 | 'error',
12 | {
13 | definitions: true,
14 | fields: ['ObjectTypeDefinition', 'InterfaceTypeDefinition', 'InputObjectTypeDefinition'],
15 | values: true,
16 | arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
17 | groups: ['id', '*', 'createdAt', 'updatedAt'],
18 | },
19 | ],
20 | '@graphql-eslint/input-name': 'error',
21 | '@graphql-eslint/no-root-type': ['error', { disallow: ['mutation', 'subscription'] }],
22 | '@graphql-eslint/no-scalar-result-type-on-mutation': 'error',
23 | '@graphql-eslint/require-deprecation-date': 'error',
24 | '@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
25 | '@graphql-eslint/require-nullable-fields-with-oneof': 'error',
26 | '@graphql-eslint/require-nullable-result-in-root': 'error',
27 | '@graphql-eslint/require-type-pattern-with-oneof': 'error',
28 | },
29 | } satisfies Linter.LegacyConfig;
30 |
--------------------------------------------------------------------------------
/website/content/rules/provided-required-arguments.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A field or directive is only valid if all required (non-null without a default value) field
4 | arguments have been provided.'
5 | ---
6 |
7 | # `provided-required-arguments`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` and
10 | `"plugin:@graphql-eslint/operations-recommended"` property in a configuration file enables this
11 | rule.
12 |
13 | - Category: `Schema & Operations`
14 | - Rule name: `@graphql-eslint/provided-required-arguments`
15 | - Requires GraphQL Schema: `true`
16 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
17 | - Requires GraphQL Operations: `false`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
19 |
20 | A field or directive is only valid if all required (non-null without a default value) field
21 | arguments have been provided.
22 |
23 | > This rule is a wrapper around a `graphql-js` validation function.
24 |
25 | ## Resources
26 |
27 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/ProvidedRequiredArgumentsRule.ts)
28 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/ProvidedRequiredArgumentsRule-test.ts)
29 |
--------------------------------------------------------------------------------
/website/content/rules/unique-directive-names-per-location.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL document is only valid if all non-repeatable directives at a given location are
4 | uniquely named.'
5 | ---
6 |
7 | # `unique-directive-names-per-location`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` and
10 | `"plugin:@graphql-eslint/operations-recommended"` property in a configuration file enables this
11 | rule.
12 |
13 | - Category: `Schema & Operations`
14 | - Rule name: `@graphql-eslint/unique-directive-names-per-location`
15 | - Requires GraphQL Schema: `true`
16 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
17 | - Requires GraphQL Operations: `false`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
19 |
20 | A GraphQL document is only valid if all non-repeatable directives at a given location are uniquely
21 | named.
22 |
23 | > This rule is a wrapper around a `graphql-js` validation function.
24 |
25 | ## Resources
26 |
27 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueDirectivesPerLocationRule.ts)
28 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueDirectivesPerLocationRule-test.ts)
29 |
--------------------------------------------------------------------------------
/website/content/rules/known-argument-names.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL field is only valid if all supplied arguments are defined by that field.'
3 | ---
4 |
5 | # `known-argument-names`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` and
8 | `"plugin:@graphql-eslint/operations-recommended"` property in a configuration file enables this
9 | rule.
10 |
11 | 💡 This rule provides
12 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
13 |
14 | - Category: `Schema & Operations`
15 | - Rule name: `@graphql-eslint/known-argument-names`
16 | - Requires GraphQL Schema: `true`
17 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
18 | - Requires GraphQL Operations: `false`
19 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
20 |
21 | A GraphQL field is only valid if all supplied arguments are defined by that field.
22 |
23 | > This rule is a wrapper around a `graphql-js` validation function.
24 |
25 | ## Resources
26 |
27 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/KnownArgumentNamesRule.ts)
28 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/KnownArgumentNamesRule-test.ts)
29 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/unique-type-names.spec.ts:
--------------------------------------------------------------------------------
1 | import { GRAPHQL_JS_VALIDATIONS } from '../src/rules/graphql-js-validation.js';
2 | import { ParserOptionsForTests, ruleTester } from './test-utils.js';
3 |
4 | const TEST_SCHEMA = /* GraphQL */ `
5 | type Query {
6 | foo: String
7 | bar: Boolean
8 | }
9 | `;
10 |
11 | const WITH_SCHEMA = {
12 | languageOptions: {
13 | parserOptions: {
14 | graphQLConfig: {
15 | schema: TEST_SCHEMA,
16 | },
17 | } satisfies ParserOptionsForTests,
18 | },
19 | };
20 |
21 | ruleTester.run('unique-type-names', GRAPHQL_JS_VALIDATIONS['unique-type-names'], {
22 | valid: [
23 | { ...WITH_SCHEMA, code: TEST_SCHEMA },
24 | {
25 | ...WITH_SCHEMA,
26 | code: /* GraphQL */ `
27 | type Query {
28 | foo: String
29 | }
30 |
31 | extend type Query {
32 | bar: Boolean
33 | }
34 | `,
35 | },
36 | ],
37 | invalid: [
38 | {
39 | ...WITH_SCHEMA,
40 | code: /* GraphQL */ `
41 | type Query {
42 | foo: String
43 | }
44 |
45 | type Query {
46 | bar: Boolean
47 | }
48 | `,
49 | errors: [{ message: 'There can be only one type named "Query".' }],
50 | },
51 | ],
52 | });
53 |
--------------------------------------------------------------------------------
/website/content/rules/value-literals-of-correct-type.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL document is only valid if all value literals are of the type expected at their
4 | position.'
5 | ---
6 |
7 | # `value-literals-of-correct-type`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | 💡 This rule provides
13 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
14 |
15 | - Category: `Operations`
16 | - Rule name: `@graphql-eslint/value-literals-of-correct-type`
17 | - Requires GraphQL Schema: `true`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
19 | - Requires GraphQL Operations: `false`
20 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
21 |
22 | A GraphQL document is only valid if all value literals are of the type expected at their position.
23 |
24 | > This rule is a wrapper around a `graphql-js` validation function.
25 |
26 | ## Resources
27 |
28 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/ValuesOfCorrectTypeRule.ts)
29 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/ValuesOfCorrectTypeRule-test.ts)
30 |
--------------------------------------------------------------------------------
/website/content/rules/overlapping-fields-can-be-merged.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A selection set is only valid if all fields (including spreading any fragments) either correspond
4 | to distinct response names or can be merged without ambiguity.'
5 | ---
6 |
7 | # `overlapping-fields-can-be-merged`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | - Category: `Operations`
13 | - Rule name: `@graphql-eslint/overlapping-fields-can-be-merged`
14 | - Requires GraphQL Schema: `true`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
16 | - Requires GraphQL Operations: `false`
17 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
18 |
19 | A selection set is only valid if all fields (including spreading any fragments) either correspond to
20 | distinct response names or can be merged without ambiguity.
21 |
22 | > This rule is a wrapper around a `graphql-js` validation function.
23 |
24 | ## Resources
25 |
26 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/OverlappingFieldsCanBeMergedRule.ts)
27 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/OverlappingFieldsCanBeMergedRule-test.ts)
28 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/require-type-pattern-with-oneof/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ruleTester } from '../../../__tests__/test-utils.js';
2 | import { rule } from './index.js';
3 |
4 | ruleTester.run('require-type-pattern-with-oneof', rule, {
5 | valid: [
6 | /* GraphQL */ `
7 | type T @oneOf {
8 | ok: Ok
9 | error: Error
10 | }
11 | `,
12 | {
13 | name: 'should ignore types without `@oneOf` directive',
14 | code: /* GraphQL */ `
15 | type T {
16 | notok: Ok
17 | err: Error
18 | }
19 | `,
20 | },
21 | {
22 | name: 'should validate only `type` with `@oneOf` directive',
23 | code: /* GraphQL */ `
24 | input I {
25 | notok: Ok
26 | err: Error
27 | }
28 | `,
29 | },
30 | ],
31 | invalid: [
32 | {
33 | name: 'should validate `ok` field',
34 | code: /* GraphQL */ `
35 | type T @oneOf {
36 | notok: Ok
37 | error: Error
38 | }
39 | `,
40 | errors: 1,
41 | },
42 | {
43 | name: 'should validate `error` field',
44 | code: /* GraphQL */ `
45 | type T @oneOf {
46 | ok: Ok
47 | err: Error
48 | }
49 | `,
50 | errors: 1,
51 | },
52 | ],
53 | });
54 |
--------------------------------------------------------------------------------
/website/content/rules/require-nullable-fields-with-oneof.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Require `input` or `type` fields to be non-nullable with `@oneOf` directive.'
3 | ---
4 |
5 | # `require-nullable-fields-with-oneof`
6 |
7 | - Category: `Schema`
8 | - Rule name: `@graphql-eslint/require-nullable-fields-with-oneof`
9 | - Requires GraphQL Schema: `false`
10 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
11 | - Requires GraphQL Operations: `false`
12 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
13 |
14 | {metadata.description}
15 |
16 | ## Usage Examples
17 |
18 | ### Incorrect
19 |
20 | ```graphql
21 | # eslint @graphql-eslint/require-nullable-fields-with-oneof: 'error'
22 |
23 | input Input @oneOf {
24 | foo: String!
25 | b: Int
26 | }
27 | ```
28 |
29 | ### Correct
30 |
31 | ```graphql
32 | # eslint @graphql-eslint/require-nullable-fields-with-oneof: 'error'
33 |
34 | input Input @oneOf {
35 | foo: String
36 | bar: Int
37 | }
38 | ```
39 |
40 | ## Resources
41 |
42 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/require-nullable-fields-with-oneof.ts)
43 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/require-nullable-fields-with-oneof.spec.ts)
44 |
--------------------------------------------------------------------------------
/website/content/rules/require-type-pattern-with-oneof.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Enforce types with `@oneOf` directive have `error` and `ok` fields.'
3 | ---
4 |
5 | # `require-type-pattern-with-oneof`
6 |
7 | - Category: `Schema`
8 | - Rule name: `@graphql-eslint/require-type-pattern-with-oneof`
9 | - Requires GraphQL Schema: `false`
10 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
11 | - Requires GraphQL Operations: `false`
12 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
13 |
14 | {metadata.description}
15 |
16 | ## Usage Examples
17 |
18 | ### Correct
19 |
20 | ```graphql
21 | # eslint @graphql-eslint/require-type-pattern-with-oneof: 'error'
22 |
23 | type Mutation {
24 | doSomething: DoSomethingMutationResult!
25 | }
26 |
27 | interface Error {
28 | message: String!
29 | }
30 |
31 | type DoSomethingMutationResult @oneOf {
32 | ok: DoSomethingSuccess
33 | error: Error
34 | }
35 |
36 | type DoSomethingSuccess {
37 | # ...
38 | }
39 | ```
40 |
41 | ## Resources
42 |
43 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/require-type-pattern-with-oneof.ts)
44 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/require-type-pattern-with-oneof.spec.ts)
45 |
--------------------------------------------------------------------------------
/packages/plugin/src/configs/index.ts:
--------------------------------------------------------------------------------
1 | import { Linter } from 'eslint';
2 | import { ConfigName } from '../types.js';
3 | import operationsAllConfig from './operations-all.js';
4 | import operationsRecommendedConfig from './operations-recommended.js';
5 | import schemaAllConfig from './schema-all.js';
6 | import schemaRecommendedConfig from './schema-recommended.js';
7 | import relayConfig from './schema-relay.js';
8 |
9 | export const configs = {
10 | 'schema-recommended': schemaRecommendedConfig,
11 | 'schema-all': schemaAllConfig,
12 | 'schema-relay': relayConfig,
13 | 'operations-recommended': operationsRecommendedConfig,
14 | 'operations-all': operationsAllConfig,
15 | 'flat/schema-recommended': {
16 | rules: schemaRecommendedConfig.rules,
17 | },
18 | 'flat/schema-all': {
19 | rules: {
20 | ...schemaRecommendedConfig.rules,
21 | ...schemaAllConfig.rules,
22 | },
23 | },
24 | 'flat/schema-relay': {
25 | rules: relayConfig.rules,
26 | },
27 | 'flat/operations-recommended': {
28 | rules: operationsRecommendedConfig.rules,
29 | },
30 | 'flat/operations-all': {
31 | rules: {
32 | ...operationsRecommendedConfig.rules,
33 | ...operationsAllConfig.rules,
34 | },
35 | },
36 | } satisfies Record & Record<`flat/${ConfigName}`, Linter.Config>;
37 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/require-nullable-result-in-root/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ruleTester, withSchema } from '../../../__tests__/test-utils.js';
2 | import { rule } from './index.js';
3 |
4 | ruleTester.run('require-nullable-result-in-root', rule, {
5 | valid: [
6 | withSchema({
7 | code: /* GraphQL */ `
8 | type Query {
9 | foo: User
10 | baz: [User]!
11 | bar: [User!]!
12 | }
13 | type User {
14 | id: ID!
15 | }
16 | `,
17 | }),
18 | ],
19 | invalid: [
20 | withSchema({
21 | code: /* GraphQL */ `
22 | type Query {
23 | user: User!
24 | }
25 | type User {
26 | id: ID!
27 | }
28 | `,
29 | errors: 1,
30 | }),
31 | withSchema({
32 | name: 'should work with extend query',
33 | code: /* GraphQL */ `
34 | type MyMutation
35 | extend type MyMutation {
36 | user: User!
37 | }
38 | interface User {
39 | id: ID!
40 | }
41 | schema {
42 | mutation: MyMutation
43 | }
44 | `,
45 | errors: 1,
46 | }),
47 | withSchema({
48 | name: 'should work with default scalars',
49 | code: 'type Mutation { foo: Boolean! }',
50 | errors: 1,
51 | }),
52 | ],
53 | });
54 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/no-anonymous-operations/snapshot.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`no-anonymous-operations > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | query { a }
7 |
8 | #### ❌ Error
9 |
10 | > 1 | query { a }
11 | | ^^^^^ Anonymous GraphQL operations are forbidden. Make sure to name your query!
12 |
13 | #### 💡 Suggestion: Rename to \`a\`
14 |
15 | 1 | query a { a }
16 | `;
17 |
18 | exports[`no-anonymous-operations > invalid > Invalid #2 1`] = `
19 | #### ⌨️ Code
20 |
21 | 1 | mutation { renamed: a }
22 |
23 | #### ❌ Error
24 |
25 | > 1 | mutation { renamed: a }
26 | | ^^^^^^^^ Anonymous GraphQL operations are forbidden. Make sure to name your mutation!
27 |
28 | #### 💡 Suggestion: Rename to \`renamed\`
29 |
30 | 1 | mutation renamed { renamed: a }
31 | `;
32 |
33 | exports[`no-anonymous-operations > invalid > Invalid #3 1`] = `
34 | #### ⌨️ Code
35 |
36 | 1 | subscription { ...someFragmentSpread }
37 |
38 | #### ❌ Error
39 |
40 | > 1 | subscription { ...someFragmentSpread }
41 | | ^^^^^^^^^^^^ Anonymous GraphQL operations are forbidden. Make sure to name your subscription!
42 |
43 | #### 💡 Suggestion: Rename to \`subscription\`
44 |
45 | 1 | subscription subscription { ...someFragmentSpread }
46 | `;
47 |
--------------------------------------------------------------------------------
/website/content/rules/fields-on-correct-type.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL document is only valid if all fields selected are defined by the parent type, or are an
4 | allowed meta field such as `__typename`.'
5 | ---
6 |
7 | # `fields-on-correct-type`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | 💡 This rule provides
13 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
14 |
15 | - Category: `Operations`
16 | - Rule name: `@graphql-eslint/fields-on-correct-type`
17 | - Requires GraphQL Schema: `true`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
19 | - Requires GraphQL Operations: `false`
20 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
21 |
22 | A GraphQL document is only valid if all fields selected are defined by the parent type, or are an
23 | allowed meta field such as `__typename`.
24 |
25 | > This rule is a wrapper around a `graphql-js` validation function.
26 |
27 | ## Resources
28 |
29 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/FieldsOnCorrectTypeRule.ts)
30 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/FieldsOnCorrectTypeRule-test.ts)
31 |
--------------------------------------------------------------------------------
/website/content/rules/fragments-on-composite-type.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'Fragments use a type condition to determine if they apply, since fragments can only be spread
4 | into a composite type (object, interface, or union), the type condition must also be a composite
5 | type.'
6 | ---
7 |
8 | # `fragments-on-composite-type`
9 |
10 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
11 | enables this rule.
12 |
13 | - Category: `Operations`
14 | - Rule name: `@graphql-eslint/fragments-on-composite-type`
15 | - Requires GraphQL Schema: `true`
16 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
17 | - Requires GraphQL Operations: `false`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
19 |
20 | Fragments use a type condition to determine if they apply, since fragments can only be spread into a
21 | composite type (object, interface, or union), the type condition must also be a composite type.
22 |
23 | > This rule is a wrapper around a `graphql-js` validation function.
24 |
25 | ## Resources
26 |
27 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/FragmentsOnCompositeTypesRule.ts)
28 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/FragmentsOnCompositeTypesRule-test.ts)
29 |
--------------------------------------------------------------------------------
/website/content/rules/possible-fragment-spread.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A fragment spread is only valid if the type condition could ever possibly be true: if there is a
4 | non-empty intersection of the possible parent types, and possible types which pass the type
5 | condition.'
6 | ---
7 |
8 | # `possible-fragment-spread`
9 |
10 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
11 | enables this rule.
12 |
13 | - Category: `Operations`
14 | - Rule name: `@graphql-eslint/possible-fragment-spread`
15 | - Requires GraphQL Schema: `true`
16 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
17 | - Requires GraphQL Operations: `false`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
19 |
20 | A fragment spread is only valid if the type condition could ever possibly be true: if there is a
21 | non-empty intersection of the possible parent types, and possible types which pass the type
22 | condition.
23 |
24 | > This rule is a wrapper around a `graphql-js` validation function.
25 |
26 | ## Resources
27 |
28 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/PossibleFragmentSpreadsRule.ts)
29 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/PossibleFragmentSpreadsRule-test.ts)
30 |
--------------------------------------------------------------------------------
/website/content/docs/usage/js.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebarTitle: Code Files
3 | icon: JSIcon
4 | ---
5 |
6 | # Usage with code files `.js`/`.jsx`
7 |
8 | You need to add a new
9 | [configuration object](https://eslint.org/docs/latest/use/configure/configuration-files#configuration-objects)
10 | in your `eslint.config.js` to setup GraphQL-ESLint processor for `.js` files.
11 |
12 | ```diff filename="eslint.config.js"
13 | import graphqlPlugin from '@graphql-eslint/eslint-plugin';
14 |
15 | export default [
16 | + {
17 | + files: ['**/*.js'],
18 | + processor: graphqlPlugin.processor
19 | + },
20 | {
21 | files: ['**/*.graphql'],
22 | languageOptions: {
23 | parser: graphqlPlugin.parser
24 | },
25 | plugins: {
26 | '@graphql-eslint': graphqlPlugin
27 | },
28 | rules: {
29 | '@graphql-eslint/known-type-names': 'error'
30 | }
31 | }
32 | ]
33 | ```
34 |
35 | > [!TIP]
36 | >
37 | > Under the hood, the processor extracts schema and operation files from `files: ['**/*.js']` and
38 | > treats them as virtual GraphQL documents with `.graphql` extensions. This enables the overrides
39 | > you define for `.graphql` files, under `files: ['**/*.graphql']`, to be applied to the definitions
40 | > within your code files.
41 |
42 | ---
43 |
44 |
45 |
--------------------------------------------------------------------------------
/website/content/rules/no-one-place-fragments.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Disallow fragments that are used only in one place.'
3 | ---
4 |
5 | # `no-one-place-fragments`
6 |
7 | - Category: `Operations`
8 | - Rule name: `@graphql-eslint/no-one-place-fragments`
9 | - Requires GraphQL Schema: `false`
10 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
11 | - Requires GraphQL Operations: `true`
12 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
13 |
14 | {metadata.description}
15 |
16 | ## Usage Examples
17 |
18 | ### Incorrect
19 |
20 | ```graphql
21 | # eslint @graphql-eslint/no-one-place-fragments: 'error'
22 |
23 | fragment UserFields on User {
24 | id
25 | }
26 |
27 | {
28 | user {
29 | ...UserFields
30 | }
31 | }
32 | ```
33 |
34 | ### Correct
35 |
36 | ```graphql
37 | # eslint @graphql-eslint/no-one-place-fragments: 'error'
38 |
39 | fragment UserFields on User {
40 | id
41 | }
42 |
43 | {
44 | user {
45 | ...UserFields
46 | friends {
47 | ...UserFields
48 | }
49 | }
50 | }
51 | ```
52 |
53 | ## Resources
54 |
55 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/no-one-place-fragments.ts)
56 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/no-one-place-fragments.spec.ts)
57 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/require-nullable-fields-with-oneof/snapshot.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`require-nullable-fields-with-oneof > invalid > should validate \`input\` 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | input Input @oneOf {
7 | 2 | foo: String!
8 | 3 | bar: [Int]!
9 | 4 | }
10 |
11 | #### ❌ Error 1/2
12 |
13 | 1 | input Input @oneOf {
14 | > 2 | foo: String!
15 | | ^^^ input value "foo" in input "Input" must be nullable when "@oneOf" is in use
16 | 3 | bar: [Int]!
17 |
18 | #### ❌ Error 2/2
19 |
20 | 2 | foo: String!
21 | > 3 | bar: [Int]!
22 | | ^^^ input value "bar" in input "Input" must be nullable when "@oneOf" is in use
23 | 4 | }
24 | `;
25 |
26 | exports[`require-nullable-fields-with-oneof > invalid > should validate \`type\` 1`] = `
27 | #### ⌨️ Code
28 |
29 | 1 | type Type @oneOf {
30 | 2 | foo: String!
31 | 3 | bar: Int
32 | 4 | }
33 |
34 | #### ❌ Error
35 |
36 | 1 | type Type @oneOf {
37 | > 2 | foo: String!
38 | | ^^^ field "foo" in type "Type" must be nullable when "@oneOf" is in use
39 | 3 | bar: Int
40 | `;
41 |
--------------------------------------------------------------------------------
/website/content/rules/require-nullable-result-in-root.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Require nullable fields in root types.'
3 | ---
4 |
5 | # `require-nullable-result-in-root`
6 |
7 | 💡 This rule provides
8 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
9 |
10 | - Category: `Schema`
11 | - Rule name: `@graphql-eslint/require-nullable-result-in-root`
12 | - Requires GraphQL Schema: `true`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | {metadata.description}
18 |
19 | ## Usage Examples
20 |
21 | ### Incorrect
22 |
23 | ```graphql
24 | # eslint @graphql-eslint/require-nullable-result-in-root: 'error'
25 |
26 | type Query {
27 | user: User!
28 | }
29 | ```
30 |
31 | ### Correct
32 |
33 | ```graphql
34 | # eslint @graphql-eslint/require-nullable-result-in-root: 'error'
35 |
36 | type Query {
37 | foo: User
38 | baz: [User]!
39 | bar: [User!]!
40 | }
41 | ```
42 |
43 | ## Resources
44 |
45 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/require-nullable-result-in-root.ts)
46 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/require-nullable-result-in-root.spec.ts)
47 |
--------------------------------------------------------------------------------
/website/content/rules/known-type-names.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL document is only valid if referenced types (specifically variable definitions and
4 | fragment conditions) are defined by the type schema.'
5 | ---
6 |
7 | # `known-type-names`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` and
10 | `"plugin:@graphql-eslint/operations-recommended"` property in a configuration file enables this
11 | rule.
12 |
13 | 💡 This rule provides
14 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
15 |
16 | - Category: `Schema & Operations`
17 | - Rule name: `@graphql-eslint/known-type-names`
18 | - Requires GraphQL Schema: `true`
19 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
20 | - Requires GraphQL Operations: `false`
21 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
22 |
23 | A GraphQL document is only valid if referenced types (specifically variable definitions and fragment
24 | conditions) are defined by the type schema.
25 |
26 | > This rule is a wrapper around a `graphql-js` validation function.
27 |
28 | ## Resources
29 |
30 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/KnownTypeNamesRule.ts)
31 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/KnownTypeNamesRule-test.ts)
32 |
--------------------------------------------------------------------------------
/examples/programmatic/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js';
2 | import graphqlPlugin from '@graphql-eslint/eslint-plugin';
3 |
4 | export default [
5 | {
6 | files: ['**/*.js'],
7 | rules: js.configs.recommended.rules,
8 | },
9 | {
10 | files: ['**/*.graphql'],
11 | languageOptions: {
12 | parser: graphqlPlugin.parser,
13 | parserOptions: {
14 | graphQLConfig: {
15 | schema: 'schema.graphql',
16 | documents: ['query.graphql', 'fragment.graphql', 'fragment2.graphql'],
17 | },
18 | },
19 | },
20 | plugins: {
21 | '@graphql-eslint': graphqlPlugin,
22 | },
23 | rules: {
24 | '@graphql-eslint/require-selections': ['error', { fieldName: '_id' }],
25 | '@graphql-eslint/unique-fragment-name': 'error',
26 | '@graphql-eslint/no-anonymous-operations': 'error',
27 | '@graphql-eslint/naming-convention': [
28 | 'error',
29 | {
30 | OperationDefinition: {
31 | style: 'PascalCase',
32 | forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
33 | forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
34 | },
35 | },
36 | ],
37 | '@graphql-eslint/unique-enum-value-names': 'error',
38 | '@graphql-eslint/require-description': ['error', { FieldDefinition: true }],
39 | },
40 | },
41 | ];
42 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/no-duplicate-fields/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ruleTester } from '../../../__tests__/test-utils.js';
2 | import { rule } from './index.js';
3 |
4 | ruleTester.run('no-duplicate-fields', rule, {
5 | valid: [],
6 | invalid: [
7 | {
8 | code: /* GraphQL */ `
9 | query test($v: String, $t: String, $v: String) {
10 | id
11 | }
12 | `,
13 | errors: [{ message: 'Variable `v` defined multiple times.' }],
14 | },
15 | {
16 | code: /* GraphQL */ `
17 | query test {
18 | users(first: 100, after: 10, filter: "test", first: 50) {
19 | id
20 | }
21 | }
22 | `,
23 | errors: [{ message: 'Argument `first` defined multiple times.' }],
24 | },
25 | {
26 | code: /* GraphQL */ `
27 | query test {
28 | users {
29 | id
30 | name
31 | email
32 | name
33 | }
34 | }
35 | `,
36 | errors: [{ message: 'Field `name` defined multiple times.' }],
37 | },
38 | {
39 | code: /* GraphQL */ `
40 | query test {
41 | users {
42 | id
43 | name
44 | email
45 | email: somethingElse
46 | }
47 | }
48 | `,
49 | errors: [{ message: 'Field `email` defined multiple times.' }],
50 | },
51 | ],
52 | });
53 |
--------------------------------------------------------------------------------
/website/content/rules/relay-page-info.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Set of rules to follow Relay specification for `PageInfo` object.'
3 | ---
4 |
5 | # `relay-page-info`
6 |
7 | - Category: `Schema`
8 | - Rule name: `@graphql-eslint/relay-page-info`
9 | - Requires GraphQL Schema: `true`
10 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
11 | - Requires GraphQL Operations: `false`
12 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
13 |
14 | Set of rules to follow Relay specification for `PageInfo` object.
15 |
16 | - `PageInfo` must be an Object type
17 | - `PageInfo` must contain fields `hasPreviousPage` and `hasNextPage`, that return non-null Boolean
18 | - `PageInfo` must contain fields `startCursor` and `endCursor`, that return either String or Scalar,
19 | which can be null if there are no results
20 |
21 | ## Usage Examples
22 |
23 | ### Correct
24 |
25 | ```graphql
26 | # eslint @graphql-eslint/relay-page-info: 'error'
27 |
28 | type PageInfo {
29 | hasPreviousPage: Boolean!
30 | hasNextPage: Boolean!
31 | startCursor: String
32 | endCursor: String
33 | }
34 | ```
35 |
36 | ## Resources
37 |
38 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/relay-page-info.ts)
39 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/relay-page-info.spec.ts)
40 |
--------------------------------------------------------------------------------
/website/content/rules/no-scalar-result-type-on-mutation.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Avoid scalar result type on mutation type to make sure to return a valid state.'
3 | ---
4 |
5 | # `no-scalar-result-type-on-mutation`
6 |
7 | 💡 This rule provides
8 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
9 |
10 | - Category: `Schema`
11 | - Rule name: `@graphql-eslint/no-scalar-result-type-on-mutation`
12 | - Requires GraphQL Schema: `true`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | {metadata.description}
18 |
19 | ## Usage Examples
20 |
21 | ### Incorrect
22 |
23 | ```graphql
24 | # eslint @graphql-eslint/no-scalar-result-type-on-mutation: 'error'
25 |
26 | type Mutation {
27 | createUser: Boolean
28 | }
29 | ```
30 |
31 | ### Correct
32 |
33 | ```graphql
34 | # eslint @graphql-eslint/no-scalar-result-type-on-mutation: 'error'
35 |
36 | type Mutation {
37 | createUser: User!
38 | }
39 | ```
40 |
41 | ## Resources
42 |
43 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/no-scalar-result-type-on-mutation.ts)
44 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/no-scalar-result-type-on-mutation.spec.ts)
45 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/no-unused-fields/snapshot.md:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`no-unused-fields > invalid > Invalid #1 1`] = `
4 | #### ⌨️ Code
5 |
6 | 1 | type User {
7 | 2 | id: ID!
8 | 3 | firstName: String
9 | 4 | }
10 |
11 | #### ❌ Error
12 |
13 | 2 | id: ID!
14 | > 3 | firstName: String
15 | | ^^^^^^^^^ Field "firstName" is unused
16 | 4 | }
17 |
18 | #### 💡 Suggestion: Remove \`firstName\` field
19 |
20 | 1 | type User {
21 | 2 | id: ID!
22 | 3 |
23 | 4 | }
24 | `;
25 |
26 | exports[`no-unused-fields > invalid > Invalid #2 1`] = `
27 | #### ⌨️ Code
28 |
29 | 1 | type Query {
30 | 2 | user(id: ID!): User
31 | 3 | }
32 | 4 |
33 | 5 | type Mutation {
34 | 6 | deleteUser(id: ID!): User
35 | 7 | }
36 |
37 | #### ❌ Error
38 |
39 | 5 | type Mutation {
40 | > 6 | deleteUser(id: ID!): User
41 | | ^^^^^^^^^^ Field "deleteUser" is unused
42 | 7 | }
43 |
44 | #### 💡 Suggestion: Remove \`deleteUser\` field
45 |
46 | 1 | type Query {
47 | 2 | user(id: ID!): User
48 | 3 | }
49 | 4 |
50 | 5 |
51 | `;
52 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/no-typename-prefix/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ruleTester } from '../../../__tests__/test-utils.js';
2 | import { rule } from './index.js';
3 |
4 | ruleTester.run('no-typename-prefix', rule, {
5 | valid: [
6 | /* GraphQL */ `
7 | type User {
8 | id: ID!
9 | }
10 | `,
11 | /* GraphQL */ `
12 | interface Node {
13 | id: ID!
14 | }
15 | `,
16 | /* GraphQL */ `
17 | type User {
18 | # eslint-disable-next-line
19 | userId: ID!
20 | }
21 | `,
22 | ],
23 | invalid: [
24 | {
25 | code: /* GraphQL */ `
26 | type User {
27 | userId: ID!
28 | }
29 | `,
30 | errors: [{ message: 'Field "userId" starts with the name of the parent type "User"' }],
31 | },
32 | {
33 | code: /* GraphQL */ `
34 | type User {
35 | userId: ID!
36 | userName: String!
37 | }
38 | `,
39 | errors: [
40 | { message: 'Field "userId" starts with the name of the parent type "User"' },
41 | { message: 'Field "userName" starts with the name of the parent type "User"' },
42 | ],
43 | },
44 | {
45 | code: /* GraphQL */ `
46 | interface Node {
47 | nodeId: ID!
48 | }
49 | `,
50 | errors: [{ message: 'Field "nodeId" starts with the name of the parent type "Node"' }],
51 | },
52 | ],
53 | });
54 |
--------------------------------------------------------------------------------
/website/content/rules/no-typename-prefix.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'Enforces users to avoid using the type name in a field name while defining your schema.'
4 | ---
5 |
6 | # `no-typename-prefix`
7 |
8 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
9 | enables this rule.
10 |
11 | 💡 This rule provides
12 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
13 |
14 | - Category: `Schema`
15 | - Rule name: `@graphql-eslint/no-typename-prefix`
16 | - Requires GraphQL Schema: `false`
17 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
18 | - Requires GraphQL Operations: `false`
19 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
20 |
21 | {metadata.description}
22 |
23 | ## Usage Examples
24 |
25 | ### Incorrect
26 |
27 | ```graphql
28 | # eslint @graphql-eslint/no-typename-prefix: 'error'
29 |
30 | type User {
31 | userId: ID!
32 | }
33 | ```
34 |
35 | ### Correct
36 |
37 | ```graphql
38 | # eslint @graphql-eslint/no-typename-prefix: 'error'
39 |
40 | type User {
41 | id: ID!
42 | }
43 | ```
44 |
45 | ## Resources
46 |
47 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/no-typename-prefix.ts)
48 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/no-typename-prefix.spec.ts)
49 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/fields-on-correct-type.spec.ts:
--------------------------------------------------------------------------------
1 | import { RuleTester } from '@theguild/eslint-rule-tester';
2 | import { GRAPHQL_JS_VALIDATIONS } from '../src/rules/graphql-js-validation.js';
3 | import { DEFAULT_CONFIG, ParserOptionsForTests } from './test-utils.js';
4 |
5 | const ruleTester = new RuleTester({
6 | languageOptions: {
7 | ...DEFAULT_CONFIG.languageOptions,
8 | parserOptions: {
9 | graphQLConfig: {
10 | schema: /* GraphQL */ `
11 | type User {
12 | id: ID
13 | age: Int
14 | }
15 |
16 | type Query {
17 | user: User
18 | }
19 | `,
20 | },
21 | },
22 | },
23 | });
24 |
25 | ruleTester.run('fields-on-correct-type', GRAPHQL_JS_VALIDATIONS['fields-on-correct-type'], {
26 | valid: [],
27 | invalid: [
28 | {
29 | name: 'should highlight selection on single line',
30 | code: 'fragment UserFields on User { id bad age }',
31 | errors: [{ message: 'Cannot query field "bad" on type "User". Did you mean "id"?' }],
32 | },
33 | {
34 | name: 'should highlight selection on multi line',
35 | code: /* GraphQL */ `
36 | {
37 | user {
38 | id
39 | veryBad
40 | age
41 | }
42 | }
43 | `,
44 | errors: [{ message: 'Cannot query field "veryBad" on type "User".' }],
45 | },
46 | ],
47 | });
48 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/processor-without-graphql-config.spec.ts:
--------------------------------------------------------------------------------
1 | import { Block, processor } from '../src/processor.js';
2 |
3 | describe('processor.preprocess() without graphql-config', () => {
4 | const QUERY = 'query users { id }';
5 | it('should find "gql" tag', () => {
6 | const code = `
7 | import { gql } from 'graphql'
8 | const fooQuery = gql\`${QUERY}\`
9 | `;
10 | const blocks = processor.preprocess(code, 'test.js') as Block[];
11 |
12 | expect(blocks[0].text).toBe(QUERY);
13 | expect(blocks).toMatchInlineSnapshot(`
14 | [
15 | {
16 | filename: document.graphql,
17 | lineOffset: 2,
18 | offset: 64,
19 | text: query users { id },
20 | },
21 |
22 | import { gql } from 'graphql'
23 | const fooQuery = gql\`query users { id }\`
24 | ,
25 | ]
26 | `);
27 | });
28 |
29 | it('should find /* GraphQL */ magic comment', () => {
30 | const code = `/* GraphQL */ \`${QUERY}\``;
31 | const blocks = processor.preprocess(code, 'test.js') as Block[];
32 |
33 | expect(blocks[0].text).toBe(QUERY);
34 | expect(blocks).toMatchInlineSnapshot(`
35 | [
36 | {
37 | filename: document.graphql,
38 | lineOffset: 0,
39 | offset: 17,
40 | text: query users { id },
41 | },
42 | /* GraphQL */ \`query users { id }\`,
43 | ]
44 | `);
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a bug report to help us improve
4 | ---
5 |
6 | ### Issue workflow progress
7 |
8 |
9 |
10 | _Progress of the issue based on the
11 | [Contributor Workflow](https://github.com/the-guild-org/Stack/blob/master/CONTRIBUTING.md#a-typical-contributor-workflow)_
12 |
13 | - [ ] 1. The issue provides a reproduction available on GitHub, Stackblitz or CodeSandbox
14 |
15 | > For example, you can start off by editing the
16 | > ['basic' example on Stackblitz](https://stackblitz.com/github/dimaMachina/graphql-eslint/tree/master/examples/graphql-config).
17 |
18 | > Please make sure the graphql-eslint version under `package.json` matches yours.
19 |
20 | - [ ] 2. A failing test has been provided
21 | - [ ] 3. A local solution has been provided
22 | - [ ] 4. A pull request is pending review
23 |
24 | ---
25 |
26 | **Describe the bug**
27 |
28 |
29 |
30 | **To Reproduce** Steps to reproduce the behavior:
31 |
32 |
33 |
34 | **Expected behavior**
35 |
36 |
37 |
38 | **Environment:**
39 |
40 | - OS:
41 | - `@graphql-eslint/eslint-plugin`:
42 | - Node.js:
43 |
44 | **Additional context**
45 |
46 |
47 |
--------------------------------------------------------------------------------
/.github/workflows/website.yml:
--------------------------------------------------------------------------------
1 | name: Website
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | jobs:
10 | deployment:
11 | runs-on: ubuntu-latest
12 | if:
13 | github.event.pull_request.head.repo.full_name == github.repository || github.event_name ==
14 | 'push'
15 | steps:
16 | - name: checkout
17 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
18 | with:
19 | fetch-depth: 0
20 |
21 | - uses: the-guild-org/shared-config/setup@main
22 | name: setup env
23 | with:
24 | nodeVersion: 22
25 | packageManager: pnpm
26 |
27 | - uses: the-guild-org/shared-config/website-cf@main
28 | name: build and deploy website
29 | env:
30 | NEXT_BASE_PATH: ${{ github.ref == 'refs/heads/master' && '/graphql/eslint' || '' }}
31 | SITE_URL:
32 | ${{ github.ref == 'refs/heads/master' && 'https://the-guild.dev/graphql/eslint' || '' }}
33 | with:
34 | cloudflareApiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
35 | cloudflareAccountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
36 | githubToken: ${{ secrets.GITHUB_TOKEN }}
37 | projectName: graphql-eslint
38 | prId: ${{ github.event.pull_request.number }}
39 | websiteDirectory: ./
40 | buildScript: pnpm run build && pnpm --filter website run build
41 | artifactDir: website/out
42 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/unique-operation-name/index.test.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'node:path';
2 | import { CWD } from '@/utils.js';
3 | import { ParserOptionsForTests, ruleTester } from '../../../__tests__/test-utils.js';
4 | import { rule } from './index.js';
5 |
6 | const TEST_OPERATION = 'query test { foo }';
7 |
8 | const SIBLING_OPERATIONS = (...documents: string[]) => ({
9 | parserOptions: {
10 | graphQLConfig: {
11 | documents,
12 | },
13 | } satisfies ParserOptionsForTests,
14 | });
15 |
16 | ruleTester.run('unique-operation-name', rule, {
17 | valid: [
18 | {
19 | ...SIBLING_OPERATIONS(TEST_OPERATION),
20 | code: 'query test2 { foo }',
21 | },
22 | {
23 | // Compare filepath of code as real instead of virtual with siblings
24 | ...SIBLING_OPERATIONS(join(CWD, '__tests__/mocks/unique-fragment.js')),
25 | filename: join(CWD, '__tests__/mocks/unique-fragment.js/1_document.graphql'),
26 | code: /* GraphQL */ `
27 | query User {
28 | user {
29 | ...UserFields
30 | }
31 | }
32 | `,
33 | },
34 | ],
35 | invalid: [
36 | {
37 | ...SIBLING_OPERATIONS(TEST_OPERATION),
38 | code: 'query test { bar }',
39 | errors: [{ messageId: 'unique-operation-name' }],
40 | },
41 | {
42 | ...SIBLING_OPERATIONS(TEST_OPERATION, 'query test { bar2 }'),
43 | code: 'query test { bar }',
44 | errors: [{ messageId: 'unique-operation-name' }],
45 | },
46 | ],
47 | });
48 |
--------------------------------------------------------------------------------
/website/content/docs/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: What's GraphQL-ESLint, key features and helpful resources.
3 | ---
4 |
5 | # Introduction
6 |
7 | This project integrates GraphQL and ESLint, for a better developer experience.
8 |
9 |
10 |
13 | Demo GraphQL-ESLint in VSCode
14 |
15 |
16 | ## Features
17 |
18 | - Integrates with ESLint core (as a ESTree parser)
19 | - Works on `.graphql` files, `gql` usages and `/* GraphQL */` magic comments
20 | - Lints both GraphQL schema and GraphQL operations
21 | - Extended type info for more advanced usages
22 | - Supports ESLint directives (for example: `eslint-disable-next-line`)
23 | - Easily extendable - supports custom rules based on GraphQL's AST and ESLint API
24 | - Validates, lints, prettifies and checks for best practices across GraphQL schema and GraphQL
25 | operations
26 | - Integrates with [GraphQL Config](https://the-guild.dev/graphql/config)
27 | - Integrates and visualizes lint issues in popular IDEs (VSCode / WebStorm)
28 |
29 | ## Resources
30 |
31 | - [Shared Schema Policies and Automatic Standards Across Your Company’s Teams](https://youtube.com/watch?v=tjuVrOhdyGY)
32 |
33 | - [Introducing GraphQL-ESLint!](https://the-guild.dev/blog/introducing-graphql-eslint)
34 |
35 | - [GraphQL-ESLint v3.14 - What's New?](https://the-guild.dev/blog/graphql-eslint-3.14)
36 |
--------------------------------------------------------------------------------
/website/content/rules/no-anonymous-operations.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'Require name for your GraphQL operations. This is useful since most GraphQL client libraries are
4 | using the operation name for caching purposes.'
5 | ---
6 |
7 | # `no-anonymous-operations`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
10 | enables this rule.
11 |
12 | 💡 This rule provides
13 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
14 |
15 | - Category: `Operations`
16 | - Rule name: `@graphql-eslint/no-anonymous-operations`
17 | - Requires GraphQL Schema: `false`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
19 | - Requires GraphQL Operations: `false`
20 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
21 |
22 | {metadata.description}
23 |
24 | ## Usage Examples
25 |
26 | ### Incorrect
27 |
28 | ```graphql
29 | # eslint @graphql-eslint/no-anonymous-operations: 'error'
30 |
31 | query {
32 | # ...
33 | }
34 | ```
35 |
36 | ### Correct
37 |
38 | ```graphql
39 | # eslint @graphql-eslint/no-anonymous-operations: 'error'
40 |
41 | query user {
42 | # ...
43 | }
44 | ```
45 |
46 | ## Resources
47 |
48 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/no-anonymous-operations.ts)
49 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/no-anonymous-operations.spec.ts)
50 |
--------------------------------------------------------------------------------
/examples/vue-code-file/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Legacy config example, should be run with `ESLINT_USE_FLAT_CONFIG=false` environment variable in ESLint 9
3 | */
4 |
5 | module.exports = {
6 | root: true,
7 | // ❗️ It's very important that you don't have any rules configured at the top-level config,
8 | // and to move all configurations into the overrides section. Since JavaScript rules
9 | // can't run on GraphQL files and vice versa, if you have rules configured at the top level,
10 | // they will try to also execute for all overrides, as ESLint's configs cascade
11 | overrides: [
12 | {
13 | files: ['*.js', '*.vue'],
14 | parser: 'vue-eslint-parser',
15 | processor: '@graphql-eslint/graphql',
16 | extends: ['eslint:recommended'],
17 | env: {
18 | es2022: true,
19 | },
20 | },
21 | {
22 | files: ['*.graphql'],
23 | parser: '@graphql-eslint/eslint-plugin',
24 | plugins: ['@graphql-eslint'],
25 | rules: {
26 | '@graphql-eslint/no-anonymous-operations': 'error',
27 | '@graphql-eslint/no-duplicate-fields': 'error',
28 | '@graphql-eslint/naming-convention': [
29 | 'error',
30 | {
31 | OperationDefinition: {
32 | style: 'PascalCase',
33 | forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
34 | forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
35 | },
36 | },
37 | ],
38 | },
39 | },
40 | ],
41 | };
42 |
--------------------------------------------------------------------------------
/examples/svelte-code-file/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Legacy config example, should be run with `ESLINT_USE_FLAT_CONFIG=false` environment variable in ESLint 9
3 | */
4 |
5 | module.exports = {
6 | root: true,
7 | // ❗️ It's very important that you don't have any rules configured at the top-level config,
8 | // and to move all configurations into the overrides section. Since JavaScript rules
9 | // can't run on GraphQL files and vice versa, if you have rules configured at the top level,
10 | // they will try to also execute for all overrides, as ESLint's configs cascade
11 | overrides: [
12 | {
13 | files: ['*.js', '*.svelte'],
14 | parser: 'svelte-eslint-parser',
15 | processor: '@graphql-eslint/graphql',
16 | extends: ['eslint:recommended'],
17 | env: {
18 | es2022: true,
19 | },
20 | },
21 | {
22 | files: ['*.graphql'],
23 | parser: '@graphql-eslint/eslint-plugin',
24 | plugins: ['@graphql-eslint'],
25 | rules: {
26 | '@graphql-eslint/no-anonymous-operations': 'error',
27 | '@graphql-eslint/no-duplicate-fields': 'error',
28 | '@graphql-eslint/naming-convention': [
29 | 'error',
30 | {
31 | OperationDefinition: {
32 | style: 'PascalCase',
33 | forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
34 | forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
35 | },
36 | },
37 | ],
38 | },
39 | },
40 | ],
41 | };
42 |
--------------------------------------------------------------------------------
/website/content/rules/unique-operation-name.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Enforce unique operation names across your project.'
3 | ---
4 |
5 | # `unique-operation-name`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Operations`
11 | - Rule name: `@graphql-eslint/unique-operation-name`
12 | - Requires GraphQL Schema: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `true`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | {metadata.description}
18 |
19 | ## Usage Examples
20 |
21 | ### Incorrect
22 |
23 | ```graphql
24 | # eslint @graphql-eslint/unique-operation-name: 'error'
25 |
26 | # foo.query.graphql
27 | query user {
28 | user {
29 | id
30 | }
31 | }
32 |
33 | # bar.query.graphql
34 | query user {
35 | me {
36 | id
37 | }
38 | }
39 | ```
40 |
41 | ### Correct
42 |
43 | ```graphql
44 | # eslint @graphql-eslint/unique-operation-name: 'error'
45 |
46 | # foo.query.graphql
47 | query user {
48 | user {
49 | id
50 | }
51 | }
52 |
53 | # bar.query.graphql
54 | query me {
55 | me {
56 | id
57 | }
58 | }
59 | ```
60 |
61 | ## Resources
62 |
63 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/unique-operation-name.ts)
64 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/unique-operation-name.spec.ts)
65 |
--------------------------------------------------------------------------------
/website/content/rules/no-unreachable-types.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Requires all types to be reachable at some level by root level fields.'
3 | ---
4 |
5 | # `no-unreachable-types`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | 💡 This rule provides
11 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
12 |
13 | - Category: `Schema`
14 | - Rule name: `@graphql-eslint/no-unreachable-types`
15 | - Requires GraphQL Schema: `true`
16 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
17 | - Requires GraphQL Operations: `false`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
19 |
20 | {metadata.description}
21 |
22 | ## Usage Examples
23 |
24 | ### Incorrect
25 |
26 | ```graphql
27 | # eslint @graphql-eslint/no-unreachable-types: 'error'
28 |
29 | type User {
30 | id: ID!
31 | name: String
32 | }
33 |
34 | type Query {
35 | me: String
36 | }
37 | ```
38 |
39 | ### Correct
40 |
41 | ```graphql
42 | # eslint @graphql-eslint/no-unreachable-types: 'error'
43 |
44 | type User {
45 | id: ID!
46 | name: String
47 | }
48 |
49 | type Query {
50 | me: User
51 | }
52 | ```
53 |
54 | ## Resources
55 |
56 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/no-unreachable-types.ts)
57 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/no-unreachable-types.spec.ts)
58 |
--------------------------------------------------------------------------------
/examples/code-file/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Legacy config example, should be run with `ESLINT_USE_FLAT_CONFIG=false` environment variable in ESLint 9
3 | */
4 |
5 | module.exports = {
6 | root: true,
7 | // ❗️ It's very important that you don't have any rules configured at the top-level config,
8 | // and to move all configurations into the overrides section. Since JavaScript rules
9 | // can't run on GraphQL files and vice versa, if you have rules configured at the top level,
10 | // they will try to also execute for all overrides, as ESLint's configs cascade
11 | overrides: [
12 | {
13 | files: ['*.js'],
14 | processor: '@graphql-eslint/graphql',
15 | extends: ['eslint:recommended'],
16 | env: {
17 | es2022: true,
18 | },
19 | parserOptions: {
20 | sourceType: 'module',
21 | },
22 | rules: {
23 | 'no-console': 'error',
24 | },
25 | },
26 | {
27 | files: ['*.graphql'],
28 | parser: '@graphql-eslint/eslint-plugin',
29 | plugins: ['@graphql-eslint'],
30 | rules: {
31 | '@graphql-eslint/no-anonymous-operations': 'error',
32 | '@graphql-eslint/naming-convention': [
33 | 'error',
34 | {
35 | OperationDefinition: {
36 | style: 'PascalCase',
37 | forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
38 | forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
39 | },
40 | },
41 | ],
42 | },
43 | },
44 | ],
45 | };
46 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "private": true,
6 | "scripts": {
7 | "analyze": "ANALYZE=true yarn build",
8 | "build": "next build && next-sitemap",
9 | "dev": "next",
10 | "postbuild": "pagefind --site .next/server/app --output-path out/_pagefind",
11 | "prebuild": "tsx ../scripts/generate-docs.ts",
12 | "start": "next start"
13 | },
14 | "dependencies": {
15 | "@graphql-eslint/eslint-plugin": "workspace:*",
16 | "@monaco-editor/react": "^4.6.0",
17 | "@radix-ui/react-icons": "^1.3.0",
18 | "@radix-ui/react-select": "^2.0.0",
19 | "@theguild/components": "8.0.0-alpha-20241206200036-57d75dbef3b4deb9b1f5dc238935dedaa0922382",
20 | "clsx": "^2.0.0",
21 | "graphql": "^16.9.0",
22 | "lodash.uniqwith": "^4.5.0",
23 | "next": "15.4.7",
24 | "next-sitemap": "4.2.3",
25 | "react": "^18.3.1",
26 | "react-dom": "^18.3.1"
27 | },
28 | "devDependencies": {
29 | "@svgr/webpack": "^8.1.0",
30 | "@theguild/tailwind-config": "0.6.1",
31 | "@types/lodash.uniqwith": "4.5.9",
32 | "@types/node": "22.13.10",
33 | "@types/react": "18.3.19",
34 | "pagefind": "1.3.0",
35 | "tailwindcss-radix": "4.0.2",
36 | "webpack": "^5.88.2"
37 | },
38 | "browserslist": {
39 | "production": [
40 | ">0.2%",
41 | "not dead",
42 | "not op_mini all"
43 | ],
44 | "development": [
45 | "last 1 chrome version",
46 | "last 1 firefox version",
47 | "last 1 safari version"
48 | ]
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/website/content/rules/unique-fragment-name.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Enforce unique fragment names across your project.'
3 | ---
4 |
5 | # `unique-fragment-name`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/operations-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Operations`
11 | - Rule name: `@graphql-eslint/unique-fragment-name`
12 | - Requires GraphQL Schema: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `true`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | {metadata.description}
18 |
19 | ## Usage Examples
20 |
21 | ### Incorrect
22 |
23 | ```graphql
24 | # eslint @graphql-eslint/unique-fragment-name: 'error'
25 |
26 | # user.fragment.graphql
27 | fragment UserFields on User {
28 | id
29 | name
30 | fullName
31 | }
32 |
33 | # user-fields.graphql
34 | fragment UserFields on User {
35 | id
36 | }
37 | ```
38 |
39 | ### Correct
40 |
41 | ```graphql
42 | # eslint @graphql-eslint/unique-fragment-name: 'error'
43 |
44 | # user.fragment.graphql
45 | fragment AllUserFields on User {
46 | id
47 | name
48 | fullName
49 | }
50 |
51 | # user-fields.graphql
52 | fragment UserFields on User {
53 | id
54 | }
55 | ```
56 |
57 | ## Resources
58 |
59 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/unique-fragment-name.ts)
60 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/unique-fragment-name.spec.ts)
61 |
--------------------------------------------------------------------------------
/website/content/rules/require-deprecation-reason.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Require all deprecation directives to specify a reason.'
3 | ---
4 |
5 | # `require-deprecation-reason`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | - Category: `Schema`
11 | - Rule name: `@graphql-eslint/require-deprecation-reason`
12 | - Requires GraphQL Schema: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | {metadata.description}
18 |
19 | ## Usage Examples
20 |
21 | ### Incorrect
22 |
23 | ```graphql
24 | # eslint @graphql-eslint/require-deprecation-reason: 'error'
25 |
26 | type MyType {
27 | name: String @deprecated
28 | }
29 | ```
30 |
31 | ### Incorrect
32 |
33 | ```graphql
34 | # eslint @graphql-eslint/require-deprecation-reason: 'error'
35 |
36 | type MyType {
37 | name: String @deprecated(reason: "")
38 | }
39 | ```
40 |
41 | ### Correct
42 |
43 | ```graphql
44 | # eslint @graphql-eslint/require-deprecation-reason: 'error'
45 |
46 | type MyType {
47 | name: String @deprecated(reason: "no longer relevant, please use fullName field")
48 | }
49 | ```
50 |
51 | ## Resources
52 |
53 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/require-deprecation-reason.ts)
54 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/require-deprecation-reason.spec.ts)
55 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/require-deprecation-date/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ruleTester } from '../../../__tests__/test-utils.js';
2 | import { rule, RuleOptions } from './index.js';
3 |
4 | const now = new Date();
5 | now.setDate(now.getDate() + 1);
6 |
7 | const day = now.getDate().toString().padStart(2, '0');
8 | const month = (now.getMonth() + 1).toString().padStart(2, '0');
9 | const year = now.getFullYear();
10 |
11 | const tomorrow = `${day}/${month}/${year}`;
12 |
13 | ruleTester.run('require-deprecation-date', rule, {
14 | valid: [
15 | 'type User { firstName: String }',
16 | `scalar Old @deprecated(deletionDate: "${tomorrow}")`,
17 | {
18 | code: `scalar Old @deprecated(untilDate: "${tomorrow}")`,
19 | options: [{ argumentName: 'untilDate' }],
20 | },
21 | /* GraphQL */ `
22 | type User {
23 | firstname: String @deprecated(deletionDate: "22/08/2031")
24 | firstName: String
25 | }
26 | `,
27 | ],
28 | invalid: [
29 | {
30 | code: 'scalar Old @deprecated(deletionDate: "22/08/2021")',
31 | errors: 1,
32 | },
33 | {
34 | code: 'scalar Old @deprecated(untilDate: "22/08/2021")',
35 | options: [{ argumentName: 'untilDate' }],
36 | errors: 1,
37 | },
38 | {
39 | code: 'scalar Old @deprecated(deletionDate: "bad")',
40 | errors: 1,
41 | },
42 | {
43 | code: 'scalar Old @deprecated(deletionDate: "32/08/2021")',
44 | errors: 1,
45 | },
46 | {
47 | code: 'type Old { oldField: ID @deprecated }',
48 | errors: 1,
49 | },
50 | ],
51 | });
52 |
--------------------------------------------------------------------------------
/website/content/rules/unique-enum-value-names.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'A GraphQL enum type is only valid if all its values are uniquely named.'
3 | ---
4 |
5 | # `unique-enum-value-names`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | 💡 This rule provides
11 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
12 |
13 | - Category: `Schema`
14 | - Rule name: `@graphql-eslint/unique-enum-value-names`
15 | - Requires GraphQL Schema: `false`
16 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
17 | - Requires GraphQL Operations: `false`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
19 |
20 | A GraphQL enum type is only valid if all its values are uniquely named.
21 |
22 | > This rule disallows case-insensitive enum values duplicates too.
23 |
24 | ## Usage Examples
25 |
26 | ### Incorrect
27 |
28 | ```graphql
29 | # eslint @graphql-eslint/unique-enum-value-names: 'error'
30 |
31 | enum MyEnum {
32 | Value
33 | VALUE
34 | ValuE
35 | }
36 | ```
37 |
38 | ### Correct
39 |
40 | ```graphql
41 | # eslint @graphql-eslint/unique-enum-value-names: 'error'
42 |
43 | enum MyEnum {
44 | Value1
45 | Value2
46 | Value3
47 | }
48 | ```
49 |
50 | ## Resources
51 |
52 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/unique-enum-value-names.ts)
53 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/unique-enum-value-names.spec.ts)
54 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/no-root-type/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ParserOptionsForTests, ruleTester } from '../../../__tests__/test-utils.js';
2 | import { rule, RuleOptions } from './index.js';
3 |
4 | const useSchema = (code: string, schema = '') => ({
5 | code,
6 | parserOptions: {
7 | graphQLConfig: {
8 | schema: schema + code,
9 | },
10 | } satisfies ParserOptionsForTests,
11 | });
12 |
13 | ruleTester.run('no-root-type', rule, {
14 | valid: [
15 | {
16 | ...useSchema('type Query'),
17 | options: [{ disallow: ['mutation', 'subscription'] }],
18 | },
19 | ],
20 | invalid: [
21 | {
22 | ...useSchema('type Mutation'),
23 | name: 'disallow mutation',
24 | options: [{ disallow: ['mutation'] }],
25 | errors: [{ message: 'Root type `Mutation` is forbidden.' }],
26 | },
27 | {
28 | ...useSchema('type Subscription'),
29 | name: 'disallow subscription',
30 | options: [{ disallow: ['subscription'] }],
31 | errors: [{ message: 'Root type `Subscription` is forbidden.' }],
32 | },
33 | {
34 | ...useSchema('extend type Mutation { foo: ID }', 'type Mutation'),
35 | name: 'disallow with extend',
36 | options: [{ disallow: ['mutation'] }],
37 | errors: [{ message: 'Root type `Mutation` is forbidden.' }],
38 | },
39 | {
40 | ...useSchema('type MyMutation', 'schema { mutation: MyMutation }'),
41 | name: 'disallow when root type name is renamed',
42 | options: [{ disallow: ['mutation'] }],
43 | errors: [{ message: 'Root type `MyMutation` is forbidden.' }],
44 | },
45 | ],
46 | });
47 |
--------------------------------------------------------------------------------
/website/content/rules/lone-executable-definition.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'Require queries, mutations, subscriptions or fragments to be located in separate files.'
4 | ---
5 |
6 | # `lone-executable-definition`
7 |
8 | - Category: `Operations`
9 | - Rule name: `@graphql-eslint/lone-executable-definition`
10 | - Requires GraphQL Schema: `false`
11 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
12 | - Requires GraphQL Operations: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
14 |
15 | {metadata.description}
16 |
17 | ## Usage Examples
18 |
19 | ### Incorrect
20 |
21 | ```graphql
22 | # eslint @graphql-eslint/lone-executable-definition: 'error'
23 |
24 | query Foo {
25 | id
26 | }
27 | fragment Bar on Baz {
28 | id
29 | }
30 | ```
31 |
32 | ### Correct
33 |
34 | ```graphql
35 | # eslint @graphql-eslint/lone-executable-definition: 'error'
36 |
37 | query Foo {
38 | id
39 | }
40 | ```
41 |
42 | ## Config Schema
43 |
44 | The schema defines the following properties:
45 |
46 | ### `ignore` (array)
47 |
48 | Allow certain definitions to be placed alongside others.
49 |
50 | The elements of the array can contain the following enum values:
51 |
52 | - `fragment`
53 | - `query`
54 | - `mutation`
55 | - `subscription`
56 |
57 | Additional restrictions:
58 |
59 | - Minimum items: `1`
60 | - Unique items: `true`
61 |
62 | ## Resources
63 |
64 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/lone-executable-definition.ts)
65 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/lone-executable-definition.spec.ts)
66 |
--------------------------------------------------------------------------------
/website/content/rules/no-root-type.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Disallow using root types `mutation` and/or `subscription`.'
3 | ---
4 |
5 | # `no-root-type`
6 |
7 | 💡 This rule provides
8 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
9 |
10 | - Category: `Schema`
11 | - Rule name: `@graphql-eslint/no-root-type`
12 | - Requires GraphQL Schema: `true`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | {metadata.description}
18 |
19 | ## Usage Examples
20 |
21 | ### Incorrect
22 |
23 | ```graphql
24 | # eslint @graphql-eslint/no-root-type: ['error', { disallow: ['mutation', 'subscription'] }]
25 |
26 | type Mutation {
27 | createUser(input: CreateUserInput!): User!
28 | }
29 | ```
30 |
31 | ### Correct
32 |
33 | ```graphql
34 | # eslint @graphql-eslint/no-root-type: ['error', { disallow: ['mutation', 'subscription'] }]
35 |
36 | type Query {
37 | users: [User!]!
38 | }
39 | ```
40 |
41 | ## Config Schema
42 |
43 | The schema defines the following properties:
44 |
45 | ### `disallow` (array, required)
46 |
47 | The elements of the array can contain the following enum values:
48 |
49 | - `mutation`
50 | - `subscription`
51 |
52 | Additional restrictions:
53 |
54 | - Minimum items: `1`
55 | - Unique items: `true`
56 |
57 | ## Resources
58 |
59 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/no-root-type.ts)
60 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/no-root-type.spec.ts)
61 |
--------------------------------------------------------------------------------
/website/content/rules/known-directives.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'A GraphQL document is only valid if all `@directive`s are known by the schema and legally
4 | positioned.'
5 | ---
6 |
7 | # `known-directives`
8 |
9 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` and
10 | `"plugin:@graphql-eslint/operations-recommended"` property in a configuration file enables this
11 | rule.
12 |
13 | - Category: `Schema & Operations`
14 | - Rule name: `@graphql-eslint/known-directives`
15 | - Requires GraphQL Schema: `true`
16 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
17 | - Requires GraphQL Operations: `false`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
19 |
20 | A GraphQL document is only valid if all `@directive`s are known by the schema and legally
21 | positioned.
22 |
23 | > This rule is a wrapper around a `graphql-js` validation function.
24 |
25 | ## Usage Examples
26 |
27 | ### Valid
28 |
29 | ```graphql
30 | # eslint @graphql-eslint/known-directives: ['error', { ignoreClientDirectives: ['client'] }]
31 |
32 | {
33 | product {
34 | someClientField @client
35 | }
36 | }
37 | ```
38 |
39 | ## Config Schema
40 |
41 | The schema defines the following properties:
42 |
43 | ### `ignoreClientDirectives` (array, required)
44 |
45 | The object is an array with all elements of the type `string`.
46 |
47 | Additional restrictions:
48 |
49 | - Minimum items: `1`
50 | - Unique items: `true`
51 |
52 | ## Resources
53 |
54 | - [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/KnownDirectivesRule.ts)
55 | - [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/KnownDirectivesRule-test.ts)
56 |
--------------------------------------------------------------------------------
/packages/plugin/src/rules/require-deprecation-reason/index.test.ts:
--------------------------------------------------------------------------------
1 | import { ruleTester } from '../../../__tests__/test-utils.js';
2 | import { rule } from './index.js';
3 |
4 | ruleTester.run('require-deprecation-reason', rule, {
5 | valid: [
6 | /* GraphQL */ `
7 | query getUser {
8 | f
9 | a
10 | b
11 | }
12 | `,
13 | /* GraphQL */ `
14 | type test {
15 | field1: String @authorized
16 | field2: Number
17 | field4: String @deprecated(reason: "Reason")
18 | }
19 |
20 | enum testEnum {
21 | item1 @authorized
22 | item2 @deprecated(reason: 0)
23 | item3
24 | }
25 |
26 | interface testInterface {
27 | field1: String @authorized
28 | field2: Number
29 | field3: String @deprecated(reason: 1.5)
30 | }
31 | `,
32 | ],
33 | invalid: [
34 | {
35 | code: /* GraphQL */ `
36 | type A {
37 | deprecatedWithoutReason: String @deprecated
38 | deprecatedWithReason: String @deprecated(reason: "Reason")
39 | notDeprecated: String
40 | }
41 |
42 | enum TestEnum {
43 | item1 @deprecated
44 | item2 @deprecated(reason: "Reason")
45 | }
46 |
47 | interface TestInterface {
48 | item1: String @deprecated
49 | item2: Number @deprecated(reason: "Reason")
50 | item3: String
51 | item4: String @deprecated(reason: "")
52 | item5: String @deprecated(reason: " ")
53 | }
54 |
55 | type MyQuery @deprecated
56 |
57 | input MyInput {
58 | foo: String! @deprecated
59 | }
60 | `,
61 | errors: 7,
62 | },
63 | ],
64 | });
65 |
--------------------------------------------------------------------------------
/website/content/rules/description-style.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Require all comments to follow the same style (either block or inline).'
3 | ---
4 |
5 | # `description-style`
6 |
7 | ✅ The `"extends": "plugin:@graphql-eslint/schema-recommended"` property in a configuration file
8 | enables this rule.
9 |
10 | 💡 This rule provides
11 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
12 |
13 | - Category: `Schema`
14 | - Rule name: `@graphql-eslint/description-style`
15 | - Requires GraphQL Schema: `false`
16 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
17 | - Requires GraphQL Operations: `false`
18 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
19 |
20 | {metadata.description}
21 |
22 | ## Usage Examples
23 |
24 | ### Incorrect
25 |
26 | ```graphql
27 | # eslint @graphql-eslint/description-style: ['error', { style: 'inline' }]
28 |
29 | """ Description """
30 | type someTypeName {
31 | # ...
32 | }
33 | ```
34 |
35 | ### Correct
36 |
37 | ```graphql
38 | # eslint @graphql-eslint/description-style: ['error', { style: 'inline' }]
39 |
40 | " Description "
41 | type someTypeName {
42 | # ...
43 | }
44 | ```
45 |
46 | ## Config Schema
47 |
48 | The schema defines the following properties:
49 |
50 | ### `style` (enum)
51 |
52 | This element must be one of the following enum values:
53 |
54 | - `block`
55 | - `inline`
56 |
57 | Default: `"block"`
58 |
59 | ## Resources
60 |
61 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/description-style.ts)
62 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/description-style.spec.ts)
63 |
--------------------------------------------------------------------------------
/website/content/rules/relay-connection-types.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Set of rules to follow Relay specification for Connection types.'
3 | ---
4 |
5 | # `relay-connection-types`
6 |
7 | - Category: `Schema`
8 | - Rule name: `@graphql-eslint/relay-connection-types`
9 | - Requires GraphQL Schema: `false`
10 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
11 | - Requires GraphQL Operations: `false`
12 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
13 |
14 | Set of rules to follow Relay specification for Connection types.
15 |
16 | - Any type whose name ends in "Connection" is considered by spec to be a `Connection type`
17 | - Connection type must be an Object type
18 | - Connection type must contain a field `edges` that return a list type that wraps an edge type
19 | - Connection type must contain a field `pageInfo` that return a non-null `PageInfo` Object type
20 |
21 | ## Usage Examples
22 |
23 | ### Incorrect
24 |
25 | ```graphql
26 | # eslint @graphql-eslint/relay-connection-types: 'error'
27 |
28 | type UserPayload { # should be an Object type with `Connection` suffix
29 | edges: UserEdge! # should return a list type
30 | pageInfo: PageInfo # should return a non-null `PageInfo` Object type
31 | }
32 | ```
33 |
34 | ### Correct
35 |
36 | ```graphql
37 | # eslint @graphql-eslint/relay-connection-types: 'error'
38 |
39 | type UserConnection {
40 | edges: [UserEdge]
41 | pageInfo: PageInfo!
42 | }
43 | ```
44 |
45 | ## Resources
46 |
47 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/relay-connection-types.ts)
48 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/relay-connection-types.spec.ts)
49 |
--------------------------------------------------------------------------------
/website/app/icons/prettier.svg:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/website/content/rules/require-field-of-type-query-in-mutation-result.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'Allow the client in one round-trip not only to call mutation but also to get a wagon of data to
4 | update their application.'
5 | ---
6 |
7 | # `require-field-of-type-query-in-mutation-result`
8 |
9 | - Category: `Schema`
10 | - Rule name: `@graphql-eslint/require-field-of-type-query-in-mutation-result`
11 | - Requires GraphQL Schema: `true`
12 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
13 | - Requires GraphQL Operations: `false`
14 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
15 |
16 | Allow the client in one round-trip not only to call mutation but also to get a wagon of data to
17 | update their application.
18 |
19 | > Currently, no errors are reported for result type `union`, `interface` and `scalar`.
20 |
21 | ## Usage Examples
22 |
23 | ### Incorrect
24 |
25 | ```graphql
26 | # eslint @graphql-eslint/require-field-of-type-query-in-mutation-result: 'error'
27 |
28 | type User { ... }
29 |
30 | type Mutation {
31 | createUser: User!
32 | }
33 | ```
34 |
35 | ### Correct
36 |
37 | ```graphql
38 | # eslint @graphql-eslint/require-field-of-type-query-in-mutation-result: 'error'
39 |
40 | type User { ... }
41 |
42 | type Query { ... }
43 |
44 | type CreateUserPayload {
45 | user: User!
46 | query: Query!
47 | }
48 |
49 | type Mutation {
50 | createUser: CreateUserPayload!
51 | }
52 | ```
53 |
54 | ## Resources
55 |
56 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/require-field-of-type-query-in-mutation-result.ts)
57 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/require-field-of-type-query-in-mutation-result.spec.ts)
58 |
--------------------------------------------------------------------------------
/examples/programmatic/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Legacy config example, should be run with `ESLINT_USE_FLAT_CONFIG=false` environment variable in ESLint 9
3 | */
4 |
5 | module.exports = {
6 | root: true,
7 | // ❗️ It's very important that you don't have any rules configured at the top-level config,
8 | // and to move all configurations into the overrides section. Since JavaScript rules
9 | // can't run on GraphQL files and vice versa, if you have rules configured at the top level,
10 | // they will try to also execute for all overrides, as ESLint's configs cascade
11 | overrides: [
12 | {
13 | files: ['*.js'],
14 | extends: ['eslint:recommended'],
15 | },
16 | {
17 | files: ['*.graphql'],
18 | parser: '@graphql-eslint/eslint-plugin',
19 | parserOptions: {
20 | graphQLConfig: {
21 | schema: 'schema.graphql',
22 | documents: ['query.graphql', 'fragment.graphql', 'fragment2.graphql'],
23 | },
24 | },
25 | plugins: ['@graphql-eslint'],
26 | rules: {
27 | '@graphql-eslint/require-selections': ['error', { fieldName: '_id' }],
28 | '@graphql-eslint/unique-fragment-name': 'error',
29 | '@graphql-eslint/no-anonymous-operations': 'error',
30 | '@graphql-eslint/naming-convention': [
31 | 'error',
32 | {
33 | OperationDefinition: {
34 | style: 'PascalCase',
35 | forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
36 | forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
37 | },
38 | },
39 | ],
40 | '@graphql-eslint/unique-enum-value-names': 'error',
41 | '@graphql-eslint/require-description': ['error', { FieldDefinition: true }],
42 | },
43 | },
44 | ],
45 | };
46 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/processor-with-graphql-config.spec.ts:
--------------------------------------------------------------------------------
1 | import { Block, processor } from '../src/processor.js';
2 |
3 | vi.mock('../src/graphql-config', () => ({
4 | loadOnDiskGraphQLConfig: vi.fn(() => ({
5 | getProjectForFile: () => ({
6 | extensions: {
7 | pluckConfig: {
8 | modules: [{ name: 'custom-gql-tag', identifier: 'custom' }],
9 | gqlMagicComment: 'CustoM',
10 | },
11 | },
12 | }),
13 | })),
14 | }));
15 |
16 | describe('processor.preprocess() with graphql-config', () => {
17 | const QUERY = 'query users { id }';
18 | it('should find "custom" tag', () => {
19 | const code = `
20 | import { custom } from 'custom-gql-tag'
21 | const fooQuery = custom\`${QUERY}\`
22 | `;
23 | const blocks = processor.preprocess(code, 'test.js') as Block[];
24 |
25 | expect(blocks[0].text).toBe(QUERY);
26 | expect(blocks).toMatchInlineSnapshot(`
27 | [
28 | {
29 | filename: document.graphql,
30 | lineOffset: 2,
31 | offset: 77,
32 | text: query users { id },
33 | },
34 |
35 | import { custom } from 'custom-gql-tag'
36 | const fooQuery = custom\`query users { id }\`
37 | ,
38 | ]
39 | `);
40 | });
41 |
42 | it('should find /* CustoM */ magic comment', () => {
43 | const code = `/* CustoM */ \`${QUERY}\``;
44 | const blocks = processor.preprocess(code, 'test.js') as Block[];
45 |
46 | expect(blocks[0].text).toBe(QUERY);
47 | expect(blocks).toMatchInlineSnapshot(`
48 | [
49 | {
50 | filename: document.graphql,
51 | lineOffset: 0,
52 | offset: 16,
53 | text: query users { id },
54 | },
55 | /* CustoM */ \`query users { id }\`,
56 | ]
57 | `);
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/packages/plugin/__tests__/eslint-directives.spec.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'node:path';
2 | import { rule as noAnonymousOperations } from '@/rules/no-anonymous-operations/index.js';
3 | import { rule as noTypenamePrefix } from '@/rules/no-typename-prefix/index.js';
4 | import { ruleTester } from './test-utils.js';
5 |
6 | ruleTester.run('no-typename-prefix', noTypenamePrefix, {
7 | valid: [
8 | {
9 | name: 'should work with descriptions #942',
10 | code: /* GraphQL */ `
11 | type Type {
12 | "Some description"
13 | typeName: String! # eslint-disable-line rule-to-test/no-typename-prefix
14 | }
15 | `,
16 | },
17 | ],
18 | invalid: [],
19 | });
20 |
21 | ruleTester.run('test-directives', noAnonymousOperations, {
22 | valid: [
23 | /* GraphQL */ `
24 | # eslint-disable-next-line
25 | {
26 | a
27 | }
28 | `,
29 | /* GraphQL */ `
30 | # eslint-disable-next-line rule-to-test/test-directives
31 | {
32 | a
33 | }
34 | `,
35 | '{ a } # eslint-disable-line rule-to-test/test-directives',
36 | '{ a } # eslint-disable-line',
37 | /* GraphQL */ `
38 | # eslint-disable
39 | {
40 | a
41 | }
42 | `,
43 | {
44 | filename: join(__dirname, 'mocks/test-directives-with-import.graphql'),
45 | code: ruleTester.fromMockFile('test-directives-with-import.graphql'),
46 | },
47 | ],
48 | invalid: [
49 | {
50 | code: /* GraphQL */ `
51 | # eslint-disable-next-line non-existing-rule
52 | {
53 | a
54 | }
55 | `,
56 | errors: [
57 | { message: "Definition for rule 'non-existing-rule' was not found." },
58 | { message: 'Anonymous GraphQL operations are forbidden. Make sure to name your query!' },
59 | ],
60 | },
61 | ],
62 | });
63 |
--------------------------------------------------------------------------------
/website/app/icons/svelte.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/website/content/rules/relay-arguments.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Set of rules to follow Relay specification for Arguments.'
3 | ---
4 |
5 | # `relay-arguments`
6 |
7 | - Category: `Schema`
8 | - Rule name: `@graphql-eslint/relay-arguments`
9 | - Requires GraphQL Schema: `false`
10 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
11 | - Requires GraphQL Operations: `false`
12 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
13 |
14 | Set of rules to follow Relay specification for Arguments.
15 |
16 | - A field that returns a Connection type must include forward pagination arguments (`first` and
17 | `after`), backward pagination arguments (`last` and `before`), or both
18 |
19 | Forward pagination arguments
20 |
21 | - `first` takes a non-negative integer
22 | - `after` takes the Cursor type
23 |
24 | Backward pagination arguments
25 |
26 | - `last` takes a non-negative integer
27 | - `before` takes the Cursor type
28 |
29 | ## Usage Examples
30 |
31 | ### Incorrect
32 |
33 | ```graphql
34 | # eslint @graphql-eslint/relay-arguments: 'error'
35 |
36 | type User {
37 | posts: PostConnection
38 | }
39 | ```
40 |
41 | ### Correct
42 |
43 | ```graphql
44 | # eslint @graphql-eslint/relay-arguments: 'error'
45 |
46 | type User {
47 | posts(after: String, first: Int, before: String, last: Int): PostConnection
48 | }
49 | ```
50 |
51 | ## Config Schema
52 |
53 | The schema defines the following properties:
54 |
55 | ### `includeBoth` (boolean)
56 |
57 | Enforce including both forward and backward pagination arguments
58 |
59 | Default: `true`
60 |
61 | ## Resources
62 |
63 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/relay-arguments.ts)
64 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/relay-arguments.spec.ts)
65 |
--------------------------------------------------------------------------------
/website/content/rules/require-deprecation-date.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description:
3 | 'Require deletion date on `@deprecated` directive. Suggest removing deprecated things after
4 | deprecated date.'
5 | ---
6 |
7 | # `require-deprecation-date`
8 |
9 | 💡 This rule provides
10 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
11 |
12 | - Category: `Schema`
13 | - Rule name: `@graphql-eslint/require-deprecation-date`
14 | - Requires GraphQL Schema: `false`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
16 | - Requires GraphQL Operations: `false`
17 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
18 |
19 | {metadata.description}
20 |
21 | ## Usage Examples
22 |
23 | ### Incorrect
24 |
25 | ```graphql
26 | # eslint @graphql-eslint/require-deprecation-date: 'error'
27 |
28 | type User {
29 | firstname: String @deprecated
30 | firstName: String
31 | }
32 | ```
33 |
34 | ### Incorrect
35 |
36 | ```graphql
37 | # eslint @graphql-eslint/require-deprecation-date: 'error'
38 |
39 | type User {
40 | firstname: String @deprecated(reason: "Use 'firstName' instead")
41 | firstName: String
42 | }
43 | ```
44 |
45 | ### Correct
46 |
47 | ```graphql
48 | # eslint @graphql-eslint/require-deprecation-date: 'error'
49 |
50 | type User {
51 | firstname: String @deprecated(reason: "Use 'firstName' instead", deletionDate: "25/12/2022")
52 | firstName: String
53 | }
54 | ```
55 |
56 | ## Config Schema
57 |
58 | The schema defines the following properties:
59 |
60 | ### `argumentName` (string)
61 |
62 | ## Resources
63 |
64 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/require-deprecation-date.ts)
65 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/require-deprecation-date.spec.ts)
66 |
--------------------------------------------------------------------------------
/website/content/rules/require-import-fragment.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: 'Require fragments to be imported via an import expression.'
3 | ---
4 |
5 | # `require-import-fragment`
6 |
7 | 💡 This rule provides
8 | [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions)
9 |
10 | - Category: `Operations`
11 | - Rule name: `@graphql-eslint/require-import-fragment`
12 | - Requires GraphQL Schema: `false`
13 | [ℹ️](/docs/getting-started#extended-linting-rules-with-graphql-schema)
14 | - Requires GraphQL Operations: `true`
15 | [ℹ️](/docs/getting-started#extended-linting-rules-with-siblings-operations)
16 |
17 | {metadata.description}
18 |
19 | ## Usage Examples
20 |
21 | ### Incorrect
22 |
23 | ```graphql
24 | # eslint @graphql-eslint/require-import-fragment: 'error'
25 |
26 | query {
27 | user {
28 | ...UserFields
29 | }
30 | }
31 | ```
32 |
33 | ### Incorrect
34 |
35 | ```graphql
36 | # eslint @graphql-eslint/require-import-fragment: 'error'
37 |
38 | # import 'post-fields.fragment.graphql'
39 | query {
40 | user {
41 | ...UserFields
42 | }
43 | }
44 | ```
45 |
46 | ### Incorrect
47 |
48 | ```graphql
49 | # eslint @graphql-eslint/require-import-fragment: 'error'
50 |
51 | # import UserFields from 'post-fields.fragment.graphql'
52 | query {
53 | user {
54 | ...UserFields
55 | }
56 | }
57 | ```
58 |
59 | ### Correct
60 |
61 | ```graphql
62 | # eslint @graphql-eslint/require-import-fragment: 'error'
63 |
64 | # import UserFields from 'user-fields.fragment.graphql'
65 | query {
66 | user {
67 | ...UserFields
68 | }
69 | }
70 | ```
71 |
72 | ## Resources
73 |
74 | - [Rule source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/src/rules/require-import-fragment.ts)
75 | - [Test source](https://github.com/dimaMachina/graphql-eslint/tree/master/packages/plugin/__tests__/require-import-fragment.spec.ts)
76 |
--------------------------------------------------------------------------------