Skip to content

[near-operation-file-preset] Fix missing fragment document imports for nested fragments (graphQLTag) - #1530

Open
wassim-k wants to merge 1 commit into
dotansimha:mainfrom
wassim-k:fix/near-operation-file-fragment-import
Open

[near-operation-file-preset] Fix missing fragment document imports for nested fragments (graphQLTag)#1530
wassim-k wants to merge 1 commit into
dotansimha:mainfrom
wassim-k:fix/near-operation-file-fragment-import

Conversation

@wassim-k

@wassim-k wassim-k commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Description

Since visitor-plugin-common v7, in documentMode: graphQLTag (default) an operation document inlines every fragment it transitively spreads, while a fragment document inlines nothing. The preset still imports *Docs by direct-spread (level === 0), so:

  • operation files miss the *Doc of fragments reached through another fragment → Cannot find name 'XFragmentDoc';
  • fragment files import *Docs they no longer interpolate → unused imports.

Reproduction

query List { list { ...Book } }, fragment Book on Book { pages { ...Page } }, fragment Page on Page { id } — each in its own file.

// queries.generated.ts
import { BookFragmentDoc } from './book-fragment.generated';
// ❌ missing: PageFragmentDoc
export const ListDocument = gql`query List{list{...Book}} ${BookFragmentDoc} ${PageFragmentDoc}`; // PageFragmentDoc undefined

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

Added new unit tests and ran the fixed version against our codebase with 100s of GraphQL queries

Notes

I have taken some liberty in trimming some code paths that are no longer used, which coincidentally happens to be my code from across the years

@changeset-bot

changeset-bot Bot commented Jun 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 152b967

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@graphql-codegen/near-operation-file-preset Patch
@graphql-codegen/typescript-react-apollo Major
@graphql-codegen/typescript-stencil-apollo Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@wassim-k

Copy link
Copy Markdown
Contributor Author

Hi @eddeee888, the tests for this fix will only pass once the client plugins (typescript-stencil-apollo, typescript-react-apollo) are on visitor-plugin-common v7, and v7 then needs the empty-import guard. The repo's plugins are currently split across v6/v7, and the preset can't be correct for both.

What are your thoughts on the best way to tackle this?

@eddeee888 eddeee888 self-assigned this Jul 2, 2026
@wassim-k

Copy link
Copy Markdown
Contributor Author

Hi @eddeee888, if you can merge and release this fix I can update the rest of the plugins in this repo to latest visitor-plugin-common in a separate PR to unblock this one.

@eddeee888

Copy link
Copy Markdown
Collaborator

Hi @wassim-k,
Could you please share your codegen config and documents?
I'm trying to reproduce this issue so I could review the PR

@wassim-k
wassim-k force-pushed the fix/near-operation-file-fragment-import branch from 5a8e411 to 982b839 Compare July 31, 2026 05:11
…r nested fragments (graphQLTag)

Since visitor-plugin-common v7, graphQLTag operations inline every transitively-spread fragment, so
import a fragment's *Doc wherever it's interpolated, not just at direct-spread level.
@wassim-k
wassim-k force-pushed the fix/near-operation-file-fragment-import branch from 982b839 to 152b967 Compare July 31, 2026 05:14
@wassim-k

Copy link
Copy Markdown
Contributor Author

Hi @eddeee888

Heads-up first: this can't be reproduced on main as-is. The bug only appears when the client
plugin resolves @graphql-codegen/visitor-plugin-common v7, since that's where a graphQLTag
operation started interpolating every fragment it transitively spreads. On main,
typescript-react-apollo is still pinned to ^6.3.0, and typescript-react-query /
typescript-rtk-query are on v7 but hardcode documentMode: DocumentMode.string, so they emit
TypedDocumentString and never interpolate a *Doc at all. That's why the PR now also migrates
react-apollo and stencil-apollo — more on that below.

Reproduction

// codegen.ts
const config: CodegenConfig = {
  schema: './schema.graphql',
  documents: './src/**/*.graphql',
  generates: {
    './src/types.ts': { plugins: ['typescript'] },
    './src/': {
      preset: 'near-operation-file',
      presetConfig: { extension: '.generated.ts', baseTypesPath: 'types.ts' },
      plugins: ['typescript-operations', 'typescript-react-apollo']
    }
  }
}
# schema.graphql
type Query {
  list: [Book!]!
}
type Book {
  id: ID!
  title: String!
  pages: [Page!]!
}
type Page {
  id: ID!
  number: Int!
}

# src/graphql/page-fragment.graphql
fragment Page on Page {
  id
  number
}

# src/graphql/book-fragment.graphql
fragment Book on Book {
  id
  title
  pages {
    ...Page
  }
}

# src/graphql/queries.graphql
query List {
  list {
    ...Book
  }
}

Page is reachable only through Book, so before the fix src/graphql/queries.generated.ts is:

import { gql } from '@apollo/client';
import { BookFragmentDoc } from './book-fragment.generated';   // PageFragmentDoc never imported

export const ListDocument = gql`
    query List {
  list {
    ...Book
  }
}
    ${BookFragmentDoc}
${PageFragmentDoc}`;
queries.generated.ts(20,3): error TS2304: Cannot find name 'PageFragmentDoc'.

The same shape already exists in this repo's dev-test (FeedFeedEntry
VoteButtons/RepoInfo); it just wasn't visible while typescript-stencil-apollo was on v6. With
the migration in place you can see the fix directly in
dev-test/githunt/__generated__/feed.query.stencil-component.tsx, which now imports and
interpolates VoteButtonsFragmentDoc and RepoInfoFragmentDoc.

What's in the PR now

I'd originally scoped this to the preset, but the fix isn't demonstrable — or safely releasable — on
its own. It ended up as:

  1. The fix itself — a fragment's *Doc is now imported wherever it's interpolated
    (transitively reachable from an operation), rather than only at direct-spread level. Fragment
    files correspondingly stop importing *Docs they no longer use.

  2. visitor-plugin-common floor raised to ^7.2.2 (preset, react-apollo, stencil-apollo). This
    one matters: on v7.0–v7.1 the fix produces invalid TypeScript. Once a fragment file's imports
    are all elided, ClientSideBaseVisitor.getImports() filters the identifier list to empty and
    generateImportStatement falls back to '*', emitting
    import * from './page-fragment.generated';TS1005: 'as' expected. 7.2.2 fixed exactly this
    by returning '' for an import source with no identifiers and no namespace. Without the floor
    raise, this PR would be a regression on older v7.

  3. react-apollo + stencil-apollo migrated v6 → v7 (^6.3.0^7.2.2), including the
    _fragments ArrayMap API change in react-apollo's visitor. Changeset is major for both,
    following the precedent of react-query's v7 jump — generated document layout changes for
    consumers even though the output is equivalent.

  4. Two react-apollo test expectations updated, both encoding the same v7 shift (fragment *Docs
    no longer self-interpolate; operations carry all transitive fragments), plus regenerated dev-test
    output.

Full suite is green (1591 passing).

Happy to split this — the preset fix could land alone if you'd rather take the two majors
separately, though it'd be dormant until a plugin here is on v7. Let me know which you prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants