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.
@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.
@lkleuver Not sure were the bug is, but I assume it's related to https://github.com/dotansimha/graphql-code-generator/blob/e9106801607212e5cc41bf5a55f4f9fa5e08eae0/packages/presets/near-operation-file/src/index.ts#L150
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: documentNodeImportFragmentsand 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!