dotansimha / graphql-code-generator

A tool for generating code based on a GraphQL schema and GraphQL operations (query/mutation/subscription), with flexible support for custom plugins.
https://the-guild.dev/graphql/codegen/
MIT License
10.85k stars 1.33k forks source link

Got a duplicate identifier issue #8123

Closed MatteoGauthier closed 2 years ago

MatteoGauthier commented 2 years ago

Describe the bug

The graphql codegen generate duplicate identifier. cf the codesandbox


export type NavigationItemsQueryVariables = Exact<{ [key: string]: never; }>;

export type NavigationItemsQuery = { __typename?: 'Query', homePage?: { __typename?: 'HomePage', id: string, navigation: Array<{ __typename?: 'NavigationItem', id: string, slug?: string | null, title?: string | null }> } | null };

      export interface PossibleTypesResultData {
        possibleTypes: {
          [key: string]: string[]
        }
      }
      const result: PossibleTypesResultData = {
  "possibleTypes": {
    "HomePageNavigationItems": [
      "NavigationItem"
    ],
    "Node": [
      "Article",
      "Asset",
      "FieldOfWork",
      "HomePage",
      "NavigationItem",
      "ScheduledOperation",
      "ScheduledRelease",
      "User"
    ],
    "ScheduledOperationAffectedDocument": [
      "Article",
      "Asset",
      "FieldOfWork",
      "HomePage",
      "NavigationItem"
    ]
  }
};
      export default result;

export type NavigationItemsQueryVariables = Exact<{ [key: string]: never; }>;

export type NavigationItemsQuery = { __typename?: 'Query', homePage?: { __typename?: 'HomePage', id: string, navigation: Array<{ __typename?: 'NavigationItem', id: string, slug?: string | null, title?: string | null }> } | null };

Your Example Website or App

https://stackblitz.com/edit/github-mvhfsr?file=graphql/generated/index.ts

Steps to Reproduce the Bug or Issue

  1. Run yarn codegen
  2. Check the graphql/generated/index.ts file

Expected behavior

No duplicate identifier

Screenshots or Videos

No response

Platform

Codegen Config File

overwrite: true schema: "https://api-eu-central-1.hygraph.com/v2/cl335cxsw434p01z87rv1ba3t/master" documents: "graphql/queries/*.{ts,tsx,gql,graphql}" generates: ./graphql/generated/index.ts: plugins:

Additional context

No response

KenzoBenzo commented 2 years ago

I get the same issue on trying to configure things. It seems the culprit is typescript-graphql-request although I've not gotten any further in figuring out why 🤔

TrevorDMartin commented 2 years ago

I get the same issue coming from the typescript plugin

nleborgne commented 2 years ago

I encountered the same issue here, with plugins typescript, typescript-operations and typescript-urql

charlypoly commented 2 years ago

Hi,

Your config lists the typescript-operations plugin twice, is it intended?

plugins:
- typescript
- typescript-operations <----
- fragment-matcher
- typescript-operations <----
- typescript-graphql-request

This might be the root cause of your issue.

RomanBaiocco commented 2 years ago

I'm running into this issue as well, but it only happens when I use a codegen.ts file as my config.

With this as my config, I run into the duplicate identifier issue if any of those plugins are present.

// codegen.ts
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  overwrite: true,
  schema: 'schema.graphql',
  documents: 'app/javascript/**/*',
  generates: {
    'app/javascript/generated/graphql': {
      preset: 'client',
      plugins: ['typescript', 'typescript-operations', 'typescript-react-apollo'],
    },
  }
};

export default config;

Whereas if I use this codegen.yml instead, there are no duplicate identifiers

# codegen.yml
overwrite: true
schema: "schema.graphql"
documents: "app/javascript/**/*"
generates:
  app/javascript/generated/graphql.tsx:
    plugins:
      - "typescript"
      - "typescript-operations"
      - "typescript-react-apollo"

The only noticeable difference between the configs is the preset: 'client' in the typescript config, but without that I run into a vague Plugin "typescript-react-apollo" validation failed error

ardatan commented 2 years ago

@RomanBaiocco You get duplicate error because client preset is defined with plugins. You get validation error because you don't have tsx extension as output in TS Config.

RomanBaiocco commented 2 years ago

@ardatan Thank you so much for the response! I'll remove the client preset and see if that fixes it

Edit: It did! You're amazing, thank you again!

For anyone who runs into this in the future, in codegen.ts I changed

generates: {
    'app/javascript/generated/graphql': {
      preset: 'client',
      plugins: ['typescript', 'typescript-operations', 'typescript-react-apollo'],
    },
  }

to

  generates: {
    "app/javascript/generated/graphql.tsx": {
      plugins: ['typescript', 'typescript-operations', 'typescript-react-apollo'],
    },
  }
ardatan commented 2 years ago

@RomanBaiocco you need to add ".tsx" extension in order to use react-apollo plugi.

charlypoly commented 2 years ago

@RomanBaiocco You get duplicate error because client preset is defined with plugins. You get validation error because you don't have tsx extension as output in TS Config.

This issue won't be reproducible with newer version of codegen that raises an error when plugins are listed along with preset: "client"

melMass commented 1 year ago

This issue won't be reproducible with newer version of codegen that raises an error when plugins are listed along with preset: "client"

Still happening

marcelorubini commented 1 year ago

Same here, but i'm not using preset configuration along with plugins This began to be an issue about 8 days ago.

error on build

./types/generated/subgraph.ts:906:3
--
09:15:15.599 | Type error: Duplicate identifier 'ExecutorAddress'.
09:15:15.599 |  
09:15:15.599 | 904 \| export enum TransactionExecution_OrderBy {
09:15:15.599 | 905 \|   Executor = 'executor',
09:15:15.599 | > 906 \|   ExecutorAddress = 'executorAddress',
09:15:15.599 | \|   ^
09:15:15.599 | 907 \|   ExecutorAddress = 'executor__address',
09:15:15.599 | 908 \|   ExecutorBridgeType = 'executor__bridgeType',
09:15:15.599 | 909 \|   ExecutorId = 'executor__id',

generated file

export enum TransactionExecution_OrderBy {
Executor = 'executor',
ExecutorAddress = 'executorAddress',
ExecutorAddress = 'executor__address',
ExecutorBridgeType = 'executor__bridgeType',
...
}
export enum TransactionValidation_OrderBy {
  ...
  Validator = 'validator',
  ValidatorAddress = 'validatorAddress',
  ValidatorAddress = 'validator__address',
}

source entities


type TransactionValidation @entity {
id: ID!
transaction: Transaction!
transactionHash: Bytes
validator: Validator!
validatorAddress: Bytes
timestamp: BigInt
}

type TransactionExecution @entity { id: ID! transaction: Transaction! transactionHash: Bytes executor: Validator executorAddress: Bytes timestamp: BigInt }


>  config.js

module.exports = { overwrite: true, schema: schemas, documents: 'src/queries/*/.ts', generates: {

  plugins: [
    'typescript',
    'typescript-operations',
    'typescript-graphql-request',
    'plugin-typescript-swr',
  ],
},

}, config: { rawRequest: false, autogenSWRKey: true, }, }

tashigeekyants commented 1 year ago

I am getting same, can anyone check what was the issue I am using codegen along with hasura codegen.ts.

import type { CodegenConfig } from '@graphql-codegen/cli';
import { writeFileSync } from 'fs';
import dotenv from 'dotenv';
dotenv.config();

const config: CodegenConfig = {
    overwrite: true,
    schema: [
        {
            'http://localhost/graphql': {
                headers: {
                    'x-hasura-admin-secret': process.env.HASURA_ADMIN_SECRET || '',
                },
            },
        },
    ],
    documents: ['src/queries/*.ts'],
    generates: {
        'src/codegen/hasura/generated/': {
            preset: 'client-preset',
            plugins: ['typescript'],
        },
    },
};

writeFileSync('src/codegen/hasura/codegen.yml', yml.stringify(config));

export default config;   
  1. codegen.yml
schema:
  - http://localhost/graphql:
      headers:
        x-hasura-admin-secret: sadsadasdsad
documents:
  - src/queries/*.ts
generates:
  src/codegen/hasura/generated/:
    preset: client
    plugins:
      - typescript
luizakp commented 1 year ago

Using futureProofEnums worked for me

userAugustos commented 1 year ago

@ardatan Thank you so much for the response! I'll remove the client preset and see if that fixes it

Edit: It did! You're amazing, thank you again!

For anyone who runs into this in the future, in codegen.ts I changed

generates: {
    'app/javascript/generated/graphql': {
      preset: 'client',
      plugins: ['typescript', 'typescript-operations', 'typescript-react-apollo'],
    },
  }

to

generates: {
  "app/javascript/generated/graphql.tsx": {
    plugins: ['typescript', 'typescript-operations', 'typescript-react-apollo'],
  },
}

But how u guys using without the preset: 'client',, her if i run without it in gives an error: Command failed with exit code 1.

Loque- commented 1 year ago

I was getting this error also and resolved it after trying futureProofEnums (thank you @luizakp!). In-case it is useful, our config looks a bit like this;

const config: CodegenConfig = {
  overwrite: true,
  schema: [
    {
      'https://graphql.datocms.com/': {
        headers: {
          // ... 
        },
      },
    },
  ],
  generates: {
    'src/generated/graphql.ts': {
      plugins: ['typescript'],
      config: {
        enumsAsTypes: true,
        futureProofEnums: true,
      },
    },
    './graphql.schema.json': {
      plugins: ['introspection'],
    },
  },
}
llanginger commented 1 year ago

I'm running into a similar issue, I'd love some help. I'll note that the codegen.ts below works as expected if I have no plugins at all.

With my config as such (the way I would expect it to just -work-):

  overwrite: true,
  schema: [
    {
      'myurl': {
        headers: {
          Authorization: "Bearer " + TOKEN,
          ["x-correlation-id"]: uuidv4()
        }
      }
    }
  ],
  documents: ['src/graphql/testQuery/*.{ts,tsx}'],
  generates: {
    './src/__generated__/': {
      preset: 'client',
      plugins: [
        'typescript',
        'typescript-operations',
        'typescript-react-apollo'
      ],
      config: {
        withHooks: true,
        withComponent: false,
        futureProofEnums: true
      },
      presetConfig: {
        gqlTagName: 'gql',
        fragmentMasking: false,
      },
    },
  },
  ignoreNoDocuments: true,
}
export default config

I get what appears to be 100% duplication of the schema. If I remove the "typescript" plugin, I only get duplication of generated code related to the hooks I am trying to generate.

If I remove the preset: client option, I get this error:

⚠ Generate outputs
  ❯ Generate to ./src/__generated__/
    ✔ Load GraphQL schemas
    ✔ Load GraphQL documents
    ✖ Plugin "typescript-react-apollo" validation failed:
      Plugin "typescript-react-apollo" requires extension to be ".ts" or ".tsx"!

I see this suggestion above to "...add ".tsx" extension in order to use react-apollo plugin." - that sounds promising but I have no idea what that means in practical terms.

In case it's relevant, this is my project's tsconfig:

  "compilerOptions": {
    "outDir": "./dist/",
    "module": "commonjs",
    "target": "es5",
    "jsx": "react",
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "moduleResolution": "node",
    "baseUrl": "./src",
    "sourceMap": true,
    "preserveConstEnums": true,
    "types": [
      "jest"
    ],
  },
  "include": [
    "./src/**/*"
  ],
  "exclude": [
    "node_modules",
    "build",
  ],
}

Any help would be greatly appreciated!

jb-1980 commented 1 year ago

@llanginger, you need to not have the following in your plugins array: typescript, typescript-operations and typescript-react-apollo. So you config should look like


const config = {
overwrite: true,
  schema: [
    {
      'myurl': {
        headers: {
          Authorization: "Bearer " + TOKEN,
          ["x-correlation-id"]: uuidv4()
        }
      }
    }
  ],
  documents: ['src/graphql/testQuery/*.{ts,tsx}'],
  generates: {
    './src/__generated__/': {
      preset: 'client',
      plugins: [
      // don't need to add plugins when using the client preset
       ],
      config: {
        withHooks: true,
        withComponent: false,
        futureProofEnums: true
      },
      presetConfig: {
        gqlTagName: 'gql',
        fragmentMasking: false,
      },
    },
  },
  ignoreNoDocuments: true,
}
export default config
llanginger commented 1 year ago

Hmm, is the functionality to generate the hooks (useSomeQuery) available as part of the client preset? If so I completely missed that.

When I remove the plugins and make no other changes I lose these, which are the main thing I'm trying to achieve with the plugins:

/**
 * __useAllEventsQuery__
 *
 * To run a query within a React component, call `useAllEventsQuery` and pass it any options that fit your needs.
 * When your component renders, `useAllEventsQuery` returns an object from Apollo Client that contains loading, error, and data properties
 * you can use to render your UI.
 *
 * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
 *
 * @example
 * const { data, loading, error } = useAllEventsQuery({
 *   variables: {
 *      ...
 *   },
 * });
 */
export function useAllEventsQuery(baseOptions: Apollo.QueryHookOptions<AllEventsQuery, AllEventsQueryVariables>) {
        const options = {...defaultOptions, ...baseOptions}
        return Apollo.useQuery<AllEventsQuery, AllEventsQueryVariables>(AllEventsDocument, options);
      }
export function useAllEventsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<AllEventsQuery, AllEventsQueryVariables>) {
          const options = {...defaultOptions, ...baseOptions}
          return Apollo.useLazyQuery<AllEventsQuery, AllEventsQueryVariables>(AllEventsDocument, options);
        }
export type AllEventsQueryHookResult = ReturnType<typeof useAllEventsQuery>;
export type AllEventsLazyQueryHookResult = ReturnType<typeof useAllEventsLazyQuery>;
export type AllEventsQueryResult = Apollo.QueryResult<AllEventsQuery, AllEventsQueryVariables>;
jb-1980 commented 1 year ago

@llanginger, I misread your original post. Uhm, I think the preset will write to a directory, while the typescript-react-apollo wants to write to a file. So if you remove the preset, you have to change generates key to a file name instead of a directory, i.e. ./src/__generated__/ needs to become something like ./src/__generated__/generated-hooks.ts

llanginger commented 1 year ago

AHA! that did it. Thanks!

parazitl2 commented 1 year ago

@llanginger, I misread your original post. Uhm, I think the preset will write to a directory, while the typescript-react-apollo wants to write to a file. So if you remove the preset, you have to change generates key to a file name instead of a directory, i.e. ./src/__generated__/ needs to become something like ./src/__generated__/generated-hooks.ts

This approach does not allow to keep the preset features, because typescript-react-apollo writes the code dependent from the graphql.ts generated by the 'client' preset.

I want to keep the functionality of the preset (with fragment masking, gpl and index files) and I need an apollo plugin. So, is it possible?

morteza-gho commented 1 year ago

The preset: "client" is not meant to be used in combination with other plugins. You must use either the client-preset or typescript-urql-plugin which provides 2 different ways to get typed GraphQL Operations.

https://stackoverflow.com/a/74137167/2690160

Qonditive commented 11 months ago

I got the same issues with the following config:

`ignoreNoDocuments: true, hooks: { afterAllFileWrite: ['prettier --write'] }, documents: ['src/graphql/data/*/.ts'], schema: {

        headers: {
            'key': API_KEY
        }
    }
},
generates: {
    './src/graphql/codegen/': {
        preset: 'client',
        plugins: [],
        config: {
            useTypeImports: true,
            documentMode: 'string',
            namingConvention: 'keep'
        },
        presetConfig: {
            fragmentMasking: {
                unmaskFunctionName: 'unmaskFragment'
            }
        },
        documentTransforms: [addTypenameSelectionDocumentTransform]
    }
}`

The output is:

export type Explainers = { .... } export type Explainers = Explainers & { .... }

I solved it by adding namingConvention: 'keep' in the config and the second type is now:

export type explainers = Explainers & { ... }

adriangalilea commented 2 months ago

@Qonditive That worked, many thanks, my codegen.ts is quite complex but may help someone else as a reference:

import type { CodegenConfig } from "@graphql-codegen/cli"
+ import { addTypenameSelectionDocumentTransform } from "@graphql-codegen/client-preset"
import dotenv from "dotenv"

dotenv.config({ path: ".env.local" })

const config: CodegenConfig = {
  schema: process.env.DGRAPH_GRAPHQL_ENDPOINT,
  documents: [
    "db/operations/**/*.tsx",
    "db/operations/**/*.ts",
    "components/**/*.tsx",
    "components/**/*.ts",
  ],
  watch: true,
  ignoreNoDocuments: true,
+   hooks: {
+     afterAllFileWrite: ["prettier --write"],
+   },
  generates: {
    "./db/generated-graphql/": {
      preset: "client",
      presetConfig: {
        fragmentMasking: { unmaskFunctionName: "getFragmentData" },
      },
      config: {
+        useTypeImports: true,
        documentMode: "string",
+        namingConvention: "keep",
        scalars: {
          DateTime: "Date",
          Int64: "string",
        },
        defaultScalarType: "unknown",
        avoidOptionals: true,
        strictScalars: true,
      },
+      documentTransforms: [addTypenameSelectionDocumentTransform],
    },
    "./db/generated-graphql/types.ts": {
      plugins: ["typescript", "typescript-operations"],
      config: {
        enumsAsTypes: false,
        futureProofEnums: true,
        skipTypename: true,
        scalars: {
          DateTime: "Date",
          Int64: "string",
        },
        defaultScalarType: "unknown",
        avoidOptionals: true,
        strictScalars: true,
+        namingConvention: "keep",
      },
    },
    "./db/schema.graphql": {
      plugins: ["schema-ast"],
      config: {
        includeDirectives: true,
      },
    },
  },
}

export default config