Graphql-code-generator: Fragment not imported in query when also imported in different fragment

Created on 2 Dec 2019  路  18Comments  路  Source: dotansimha/graphql-code-generator

When a query imports a fragment that imports another fragment which is also imported by the query, that common fragment is not always imported by the generated code.

For example:

Common Fragment

fragment CommonFragment on SomeType {
   fieldOne
   fieldTwo
}

Fragment One

fragment MyFirstFragment on SomeOtherType {
   fieldThree
    someType {
        ...CommonFragment
    }
}
${CommonFragment}

Query

query MyQuery {
   loadSomeData {

      ...MyFirstFragment

      someOtherField {
        someType {
           ...CommonFragment
        }
      }

   }
}
${MyFirstFragment}
${CommonFragment}

In the above case, CommonFragment is NOT imported by the generated code for MyQuery

HOWEVER if instead the fragments are included in a different order, as so

query MyQuery {
   loadSomeData {

      someOtherField {
        someType {
           ...CommonFragment
        }
      }
      ...MyFirstFragment
   }
}
${CommonFragment}
${MyFirstFragment}

The code is generated as expected.

I'm unsure if the additional nesting inside someOtherField { in the query is relevant, but it is a sample of the structure we're seeing this bug in.

bug plugins waiting-for-release

All 18 comments

@willtrking can you please share you codegen config file? do you use presets?

@willtrking can you please provide more information? Maybe a reproduction in codesandbox or a repo?

I'm having the same issue, though it doesn't seem to have the same cause.
A bit stumped, the error started occurring when I added the "folder" setting in near-operation-file
or more likely splitting my queries mutations and fragments over separate files.

- https://bartender.dagen.app/v1/graphql:
      headers:
        x-hasura-admin-secret: ${HASURA_ADMIN_SECRET}
documents: "./packages/app/query/**/*.graphql"
generates:
  ./packages/app/types/gen.ts:
    - typescript
  ./packages/app/:
    preset: near-operation-file
    presetConfig:
      extension: .tsx
      folder: gen
      baseTypesPath: types/gen.ts
    plugins:
      - typescript-operations
      - typescript-react-apollo
    config:
      withHOC: false
      withHooks: true`

the only thing missing is the import

export type SurveysQuery = (
  { __typename?: 'query_root' }
  & { survey: Array<(
    { __typename?: 'survey' }
    & SurveyPropsFragment
  )> }
);

while the import does have some fragments:

import { QuestionTypeFragment,SurveyDetailFragment,QuestionDetailFragmentFragment,QuestionOptionFragment } from './fragments';

in my fragments.graphql file:

fragment QuestionType on question_type {
  key
  name
}

fragment SurveyProps on survey {
  id
  name
  active
  created_at
  last_parsed
  survey_type {
    key
    name
  }
  source
}

fragment SurveyDetail on survey {
  questions(order_by: { index: asc }) {
    ...Question
  }
  ...SurveyProps
}

fragment QuestionOption on question {
  id
  title
}

fragment Question on question {
  id
  title
  euuid
  index
  question_type_key
  survey_id
  answer_options(order_by: { index: asc }) {
    ...AnswerOption
  }
}
fragment QuestionDetailFragment on question {
  id
  title
  euuid
  index
  question_type_key
  survey_id
  answer_options(order_by: { index: asc }) {
    ...AnswerOption
  }
  answers {
    ...AnswerFragment
  }
}

fragment AnswerOption on answer_option {
  id
  title
  euuid
  index
}

fragment AnswerFragment on answer {
  id
  entity
  answer_values {
    ...AnswerValueFragment
  }
}

fragment AnswerValueFragment on answer_value {
  answer_option_id
  number_value
  string_value
  point
  id
}

and the generated fragments file:

import * as Types from '../../../types/gen';

import gql from 'graphql-tag';

export type QuestionTypeFragment = (
  { __typename?: 'question_type' }
  & Pick<Types.Question_Type, 'key' | 'name'>
);

export type SurveyPropsFragment = (
  { __typename?: 'survey' }
  & Pick<Types.Survey, 'id' | 'name' | 'active' | 'created_at' | 'last_parsed' | 'source'>
  & { survey_type: (
    { __typename?: 'survey_type' }
    & Pick<Types.Survey_Type, 'key' | 'name'>
  ) }
);

export type SurveyDetailFragment = (
  { __typename?: 'survey' }
  & { questions: Array<(
    { __typename?: 'question' }
    & QuestionFragment
  )> }
  & SurveyPropsFragment
);

export type QuestionOptionFragment = (
  { __typename?: 'question' }
  & Pick<Types.Question, 'id' | 'title'>
);

export type QuestionFragment = (
  { __typename?: 'question' }
  & Pick<Types.Question, 'id' | 'title' | 'euuid' | 'index' | 'question_type_key' | 'survey_id'>
  & { answer_options: Array<(
    { __typename?: 'answer_option' }
    & AnswerOptionFragment
  )> }
);

export type QuestionDetailFragmentFragment = (
  { __typename?: 'question' }
  & Pick<Types.Question, 'id' | 'title' | 'euuid' | 'index' | 'question_type_key' | 'survey_id'>
  & { answer_options: Array<(
    { __typename?: 'answer_option' }
    & AnswerOptionFragment
  )>, answers: Array<(
    { __typename?: 'answer' }
    & AnswerFragmentFragment
  )> }
);

export type AnswerOptionFragment = (
  { __typename?: 'answer_option' }
  & Pick<Types.Answer_Option, 'id' | 'title' | 'euuid' | 'index'>
);

export type AnswerFragmentFragment = (
  { __typename?: 'answer' }
  & Pick<Types.Answer, 'id' | 'entity'>
  & { answer_values: Array<(
    { __typename?: 'answer_value' }
    & AnswerValueFragmentFragment
  )> }
);

export type AnswerValueFragmentFragment = (
  { __typename?: 'answer_value' }
  & Pick<Types.Answer_Value, 'answer_option_id' | 'number_value' | 'string_value' | 'point' | 'id'>
);

export const QuestionTypeFragmentDoc = gql`
    fragment QuestionType on question_type {
  key
  name
}
    `;
export const AnswerOptionFragmentDoc = gql`
    fragment AnswerOption on answer_option {
  id
  title
  euuid
  index
}
    `;
export const QuestionFragmentDoc = gql`
    fragment Question on question {
  id
  title
  euuid
  index
  question_type_key
  survey_id
  answer_options(order_by: {index: asc}) {
    ...AnswerOption
  }
}
    ${AnswerOptionFragmentDoc}`;
export const SurveyPropsFragmentDoc = gql`
    fragment SurveyProps on survey {
  id
  name
  active
  created_at
  last_parsed
  survey_type {
    key
    name
  }
  source
}
    `;
export const SurveyDetailFragmentDoc = gql`
    fragment SurveyDetail on survey {
  questions(order_by: {index: asc}) {
    ...Question
  }
  ...SurveyProps
}
    ${QuestionFragmentDoc}
${SurveyPropsFragmentDoc}`;
export const QuestionOptionFragmentDoc = gql`
    fragment QuestionOption on question {
  id
  title
}
    `;
export const AnswerValueFragmentFragmentDoc = gql`
    fragment AnswerValueFragment on answer_value {
  answer_option_id
  number_value
  string_value
  point
  id
}
    `;
export const AnswerFragmentFragmentDoc = gql`
    fragment AnswerFragment on answer {
  id
  entity
  answer_values {
    ...AnswerValueFragment
  }
}
    ${AnswerValueFragmentFragmentDoc}`;
export const QuestionDetailFragmentFragmentDoc = gql`
    fragment QuestionDetailFragment on question {
  id
  title
  euuid
  index
  question_type_key
  survey_id
  answer_options(order_by: {index: asc}) {
    ...AnswerOption
  }
  answers {
    ...AnswerFragment
  }
}
    ${AnswerOptionFragmentDoc}
${AnswerFragmentFragmentDoc}`;

any idea where the "bug" might be, would love to help look for it. Haven't dived deep enough to know which plugin is responsible for the fragment imports.

Been trying to debug the issue this morning.
If I comment out this line: https://github.com/dotansimha/graphql-code-generator/blob/e9106801607212e5cc41bf5a55f4f9fa5e08eae0/packages/presets/near-operation-file/src/utils.ts#L46 it seems to properly import the fragments.

it looks like recursively calling extractExternalFragmentsInUse stops the current running visitor and never reaches the second fragment definition.

I've been using this repo to hack around a bit:
https://github.com/lkleuver/graphql-codegen-fiddle/tree/master/dev-test/fragment-test

Not sure if it's the same issues as #3259 or not, so can you please try 1.12.2-alpha-ea7264f9.15? @lkleuver

I tried that version but it's still missing some fragment and fragmentDoc imports.

By the way, in my "fiddle" posted above, if you switch the query statements (pages first then book) the imports work correctly.

tried latest but still missing some (not all) fragment imports

@lkleuver any chance you can share a reproduction? in a code sandbox, or a repo, something we can clone and try to test locally.

Here is a repo with where the bug is reproduced:
https://github.com/lkleuver/graphql-codegen-fragment-test

I did some digging here and I thought it might be because of the level of the Page fragment is overwritten to 1 while it's also 0.

if you switch query Pages with query Books the fragment imports work.

Thanks @lkleuver , this is really helpful. Trying to fix that now :)

I think I managed to fix that in: https://github.com/dotansimha/graphql-code-generator/pull/3606

@willtrking @lkleuver can you please try the alpha version 1.12.3-alpha-b89e11fd.107? I tested it with your repo, and it seems to work now.

whoaah seems to work, awesome!
Finally I can stop vscode "add missing imports" 10 times :)

import { DashboardPropsFragmentDoc, DashboardPropsFragment, DashboardDetailsFragmentDoc, DashboardDetailsFragment, DashboardChapterFragmentDoc, DashboardChapterFragment, DashboardChapterEntryFragmentDoc, DashboardChapterEntryFragment, DashboardChapterGeoEntryFragmentDoc, DashboardChapterGeoEntryFragment, DashboardChapterMapLayerFragmentDoc, DashboardChapterMapLayerFragment, LiveAnswerFragmentDoc, LiveAnswerFragment } from "./fragments";

Fixed in v1.13.0

@dotansimha - Is there a way to prevent it from importing the FragmentDoc? This mostly fixes my issue, but I don't need the FragmentDoc imports since it doesn't seem to be referenced anywhere in the types I have generated.

Ah, I guess this was added in #3436 (1.12.2?) I was on 1.9.1 before and was waiting for the fragment issue to be fixed before upgrading =).

@pachuka I think documentMode: documentNodeImportFragments and it will skip importing fragments at all. Can you please try it? (it's available only in 1.13.0)

@pachuka I think documentMode: documentNodeImportFragments and it will skip importing fragments at all. Can you please try it? (it's available only in 1.13.0)

Hmm, I still need the Fragments though.. my issue might be the same as #3525 - @dotansimha will try the update there/reply there! Thanks for the fast responses and updates, really appreciate it!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rogerg93 picture rogerg93  路  3Comments

dotansimha picture dotansimha  路  3Comments

mszczepanczyk picture mszczepanczyk  路  3Comments

steebchen picture steebchen  路  3Comments

quolpr picture quolpr  路  3Comments