From e5361bcb7ba0c21d94ead07d7b7dfc3d2b11e98c Mon Sep 17 00:00:00 2001 From: Eddy Nguyen Date: Mon, 28 Sep 2026 23:23:09 +1000 Subject: [PATCH 1/2] [visitor-plugin-common] fix: build type cache keys from selection sets as written to stop OOM with nested fragments (#10940) (#10982) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: reproduce #10940 — type cache keys grow exponentially with nested fragment reuse Synthetic repro: fragments F0..F7 where each spreads the previous one under three fields. With `inlineFragmentTypes: 'combine'` the generated output is ~2.3 KB, but the `typeCache` keys built from the fragment-expanded field paths (`getFieldNames`) reach 180 KB for a single key and 483 KB in total, growing ~3.3x per extra nesting level. On large projects this runs out of memory. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013DCrEAx4J6ze2GV4FinZs5 eddeee888:oss:issue-verify * test: reduce #10940 repro to the minimal config Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013DCrEAx4J6ze2GV4FinZs5 * fix: build type cache keys from selection sets as written, not fragment-expanded paths (#10940) The per-selection-set type cache was keyed by every fragment-expanded field path (`getFieldNames`), so keys grew exponentially with nested fragment reuse and exhausted memory on large projects. Keys now describe the selection set as written (fragment spreads by name, plus directives and inline fragments), built by `getSelectionSetCacheKey` and memoized per node in `selectionSetCacheKeys` (both @internal). `getFieldNames` is removed. The repro test now spies on `selectionSetCacheKeys` instead of `Map.prototype.set`. Co-authored-by: Eddy Nguyen Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013DCrEAx4J6ze2GV4FinZs5 eddeee888:oss:issue-fix --------- Co-authored-by: Claude --- .changeset/nested-fragments-cache-keys.md | 12 ++ .../src/selection-set-to-object.ts | 11 +- .../other/visitor-plugin-common/src/utils.ts | 104 +++++++++--------- ...-documents.nested-fragments-memory.spec.ts | 96 ++++++++++++++++ 4 files changed, 164 insertions(+), 59 deletions(-) create mode 100644 .changeset/nested-fragments-cache-keys.md create mode 100644 packages/plugins/typescript/operations/tests/ts-documents.nested-fragments-memory.spec.ts diff --git a/.changeset/nested-fragments-cache-keys.md b/.changeset/nested-fragments-cache-keys.md new file mode 100644 index 00000000000..9e5ea854618 --- /dev/null +++ b/.changeset/nested-fragments-cache-keys.md @@ -0,0 +1,12 @@ +--- +'@graphql-codegen/visitor-plugin-common': patch +'@graphql-codegen/typescript-operations': patch +--- + +Fix out-of-memory errors with deeply nested, widely reused fragments (#10940). + +The type cache used while generating selection set types was keyed by every fragment-expanded field +path, so its keys grew exponentially with fragment nesting. Keys are now built from the selection +set as written, referencing fragment spreads by name, so they stay linear in the size of the +documents. The exported `getFieldNames` helper from `@graphql-codegen/visitor-plugin-common`, which +built those expanded paths, is removed. diff --git a/packages/plugins/other/visitor-plugin-common/src/selection-set-to-object.ts b/packages/plugins/other/visitor-plugin-common/src/selection-set-to-object.ts index 3af8bb9de17..af79a510a5c 100644 --- a/packages/plugins/other/visitor-plugin-common/src/selection-set-to-object.ts +++ b/packages/plugins/other/visitor-plugin-common/src/selection-set-to-object.ts @@ -44,9 +44,9 @@ import type { import { DeclarationBlock, DeclarationBlockConfig, - getFieldNames, getFieldNodeNameValue, getPossibleTypes, + getSelectionSetCacheKey, hasConditionalDirectives, hasIncrementalDeliveryDirectives, mergeSelectionSets, @@ -1107,12 +1107,7 @@ export class SelectionSetToObject< public transformSelectionSet(fieldName: string) { const possibleTypesList = getPossibleTypes(this._schema, this._parentSchemaType); const possibleTypes = possibleTypesList.map(v => v.name).sort(); - const fieldSelections = [ - ...getFieldNames({ - selections: this._selectionSet.selections, - loadedFragments: this._loadedFragments, - }), - ].sort(); + const selectionSetKey = getSelectionSetCacheKey(this._selectionSet); // Optimization: Do not create new dependentTypes if fragment typename exists in cache // 2-layer cache: LOC => Field Selection Type Combination => cachedTypeString @@ -1120,7 +1115,7 @@ export class SelectionSetToObject< this._processor.typeCache.get(this._selectionSet.loc) ?? new Map(); this._processor.typeCache.set(this._selectionSet.loc, objMap); - const cacheHashKey = `${fieldSelections.join(',')} @ ${possibleTypes.join('|')}`; + const cacheHashKey = `${selectionSetKey} @ ${possibleTypes.join('|')}`; const [cachedTypeString] = objMap.get(cacheHashKey) ?? []; if (cachedTypeString) { // reuse previously generated type, as it is identical diff --git a/packages/plugins/other/visitor-plugin-common/src/utils.ts b/packages/plugins/other/visitor-plugin-common/src/utils.ts index 55e85b6ca65..e08a4b15ed1 100644 --- a/packages/plugins/other/visitor-plugin-common/src/utils.ts +++ b/packages/plugins/other/visitor-plugin-common/src/utils.ts @@ -21,6 +21,7 @@ import { Kind, NamedTypeNode, NameNode, + print, SelectionNode, SelectionSetNode, StringValueNode, @@ -30,12 +31,7 @@ import type { RawConfig } from './base-visitor.js'; import { parseMapper } from './mappers.js'; import { DEFAULT_SCALARS } from './scalars.js'; import type { EnrichedFieldNode } from './selection-set-to-object.js'; -import type { - LoadedFragment, - NormalizedScalarsMap, - ParsedScalarsMap, - ScalarsMap, -} from './types.js'; +import type { NormalizedScalarsMap, ParsedScalarsMap, ScalarsMap } from './types.js'; export const getConfigValue = (value: T | null | undefined, defaultValue: T): T => { if (value === null || value === undefined) { @@ -652,64 +648,70 @@ export function unique( return Object.values(array.reduce((acc, item) => ({ [key(item)]: item, ...acc }), {})); } -function getFullPathFieldName(selection: FieldNode, parentName: string) { - const fullName = - 'alias' in selection && selection.alias - ? `${selection.alias.value}@${selection.name.value}` - : selection.name.value; - return parentName ? `${parentName}.${fullName}` : fullName; -} +/** + * Memoizes `getSelectionSetCacheKey` per selection set node. Entries are dropped once the node's + * document is no longer referenced. + * + * @internal Exported for tests only; not part of the public API. + */ +export const selectionSetCacheKeys = new WeakMap(); -export const getFieldNames = ({ - selections, - fieldNames = new Set(), - parentName = '', - loadedFragments, -}: { - selections: readonly SelectionNode[]; - fieldNames?: Set; - parentName?: string; - loadedFragments: LoadedFragment[]; -}) => { - for (const selection of selections) { +/** + * Builds a cache key describing a selection set as written: fragment spreads are referenced by + * name rather than expanded, so the key stays linear in the size of the document even when + * fragments are deeply nested and widely reused. The parts are sorted so the key does not depend + * on selection order. + * + * Examples: + * - `{ user { id name } }` becomes `user{id,name}` (the inner `{ id name }` becomes `id,name`) + * - `{ id ...UserFields }` becomes `...UserFields,id` (the fragment is referenced by name, not + * expanded) + * - `{ ... on Admin { role } }` becomes `... on Admin{role}` + * - `{ me: user { id ...UserFields @include(if: $withFields) ... on Admin { role } } }` becomes + * `me@user{... on Admin{role},...UserFields @include(if: $withFields),id}` + * + * @internal Not part of the public API. + */ +export function getSelectionSetCacheKey(selectionSet: SelectionSetNode): string { + const cached = selectionSetCacheKeys.get(selectionSet); + if (cached !== undefined) { + return cached; + } + + const printDirectives = (directives: readonly DirectiveNode[] | undefined): string => + directives?.length ? ` ${directives.map(directive => print(directive)).join(' ')}` : ''; + + const parts = new Set(); + for (const selection of selectionSet.selections) { switch (selection.kind) { case Kind.FIELD: { - const fieldName = getFullPathFieldName(selection, parentName); - fieldNames.add(fieldName); - if (selection.selectionSet) { - getFieldNames({ - selections: selection.selectionSet.selections, - fieldNames, - parentName: fieldName, - loadedFragments, - }); - } + const name = selection.alias + ? `${selection.alias.value}@${selection.name.value}` + : selection.name.value; + const subKey = selection.selectionSet + ? `{${getSelectionSetCacheKey(selection.selectionSet)}}` + : ''; + parts.add(`${name}${printDirectives(selection.directives)}${subKey}`); break; } case Kind.FRAGMENT_SPREAD: { - getFieldNames({ - selections: loadedFragments - .filter(def => def.name === selection.name.value) - .flatMap(s => s.node.selectionSet.selections), - fieldNames, - parentName, - loadedFragments, - }); + parts.add(`...${selection.name.value}${printDirectives(selection.directives)}`); break; } case Kind.INLINE_FRAGMENT: { - getFieldNames({ - selections: selection.selectionSet.selections, - fieldNames, - parentName, - loadedFragments, - }); + const onType = selection.typeCondition ? ` on ${selection.typeCondition.name.value}` : ''; + parts.add( + `...${onType}${printDirectives(selection.directives)}{${getSelectionSetCacheKey(selection.selectionSet)}}`, + ); break; } } } - return fieldNames; -}; + + const key = [...parts].sort().join(','); + selectionSetCacheKeys.set(selectionSet, key); + return key; +} export const getNodeComment = ( node: FieldDefinitionNode | EnumValueDefinitionNode | InputValueDefinitionNode, diff --git a/packages/plugins/typescript/operations/tests/ts-documents.nested-fragments-memory.spec.ts b/packages/plugins/typescript/operations/tests/ts-documents.nested-fragments-memory.spec.ts new file mode 100644 index 00000000000..2108b261a74 --- /dev/null +++ b/packages/plugins/typescript/operations/tests/ts-documents.nested-fragments-memory.spec.ts @@ -0,0 +1,96 @@ +import { buildSchema, parse, print } from 'graphql'; +import { selectionSetCacheKeys } from '@graphql-codegen/visitor-plugin-common'; +import { plugin } from '../src/index.js'; + +const schema = buildSchema(/* GraphQL */ ` + type Query { + root: Node + } + + type Node { + id: ID! + name: String + a: Node + b: Node + c: Node + } +`); + +// A chain of fragments F0..F(DEPTH-1), where each fragment spreads the previous one under several fields. +// The document as written is linear in DEPTH, but its fragment-expanded tree is 3^DEPTH in size. +const DEPTH = 8; +const buildDocumentSource = () => { + const fragments = [ + /* GraphQL */ ` + fragment F0 on Node { + id + name + } + `, + ]; + for (let i = 1; i < DEPTH; i++) { + fragments.push(/* GraphQL */ ` + fragment F${i} on Node { + id + a { + ...F${i - 1} + } + b { + ...F${i - 1} + } + c { + ...F${i - 1} + } + } + `); + } + return /* GraphQL */ ` + query Root { + root { + ...F${DEPTH - 1} + } + } + ${fragments.join('\n')} + `; +}; + +describe('TypeScript Operations Plugin - deeply nested fragments (issue #10940)', () => { + it('does not retain type cache keys that grow exponentially with nested fragment reuse', async () => { + const document = parse(buildDocumentSource()); + const printedDocumentLength = print(document).length; + + // Spy on the memo behind the type cache keys, so we can measure every key that gets built. + const setSpy = vi.spyOn(selectionSetCacheKeys, 'set'); + + let result: Awaited>; + let cacheKeys: string[]; + try { + // No config needed: each option (including every `inlineFragmentTypes` mode) was measured + // and none changes the cache key sizes; the growth comes from nested fragment reuse alone. + result = await plugin( + schema, + [{ location: 'test-file.ts', document }], + {}, + { outputFile: 'graphql.ts' }, + ); + } finally { + // Read the calls before restoring: `mockRestore()` also clears them. + cacheKeys = setSpy.mock.calls.map(([, key]) => key); + setSpy.mockRestore(); + } + + // Output is produced for both the operation and the outermost fragment. + expect(result.content).toMatch(/export type RootQuery\b/); + expect(result.content).toMatch(new RegExp(`export type F${DEPTH - 1}Fragment\\b`)); + + // In user terms: memory retained by the type cache must scale with the documents as written, + // not with the fragment-expanded tree. Otherwise it grows exponentially with nested fragment + // reuse, and large projects with deeply nested, widely reused fragments run out of memory. + // Guard against the spy silently seeing nothing, which would make the bounds below pass vacuously. + expect(cacheKeys.length).toBeGreaterThan(0); + + const bound = 10 * printedDocumentLength; + expect(Math.max(...cacheKeys.map(key => key.length))).toBeLessThan(bound); + expect(cacheKeys.reduce((total, key) => total + key.length, 0)).toBeLessThan(bound); + }); +}); From 4e8654dcbc9c0fd22d46e6b55113834b47baa124 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 23:33:20 +1000 Subject: [PATCH 2/2] chore(release): update monorepo packages versions (#10987) Co-authored-by: github-actions[bot] --- .changeset/brave-modules-skip.md | 5 ---- .changeset/calm-owls-guard.md | 5 ---- .changeset/nested-fragments-cache-keys.md | 12 --------- .changeset/quiet-foxes-skip.md | 5 ---- .changeset/quiet-pens-return.md | 5 ---- .changeset/tidy-tags-guard.md | 5 ---- .changeset/tidy-typed-nodes.md | 5 ---- .changeset/typed-old-visit.md | 7 ------ .changeset/wide-config-values.md | 5 ---- packages/graphql-codegen-core/CHANGELOG.md | 14 +++++++++++ packages/graphql-codegen-core/package.json | 2 +- packages/plugins/other/add/CHANGELOG.md | 15 +++++++++++ packages/plugins/other/add/package.json | 2 +- .../other/visitor-plugin-common/CHANGELOG.md | 25 +++++++++++++++++++ .../other/visitor-plugin-common/package.json | 2 +- .../typescript/document-nodes/CHANGELOG.md | 17 +++++++++++++ .../typescript/document-nodes/package.json | 2 +- .../typescript/operations/CHANGELOG.md | 23 +++++++++++++++++ .../typescript/operations/package.json | 2 +- .../typed-document-node/CHANGELOG.md | 17 +++++++++++++ .../typed-document-node/package.json | 2 +- packages/presets/graphql-modules/CHANGELOG.md | 17 +++++++++++++ packages/presets/graphql-modules/package.json | 2 +- .../graphql-codegen-testing/CHANGELOG.md | 15 +++++++++++ .../graphql-codegen-testing/package.json | 2 +- packages/utils/plugins-helpers/CHANGELOG.md | 18 +++++++++++++ packages/utils/plugins-helpers/package.json | 2 +- 27 files changed, 170 insertions(+), 63 deletions(-) delete mode 100644 .changeset/brave-modules-skip.md delete mode 100644 .changeset/calm-owls-guard.md delete mode 100644 .changeset/nested-fragments-cache-keys.md delete mode 100644 .changeset/quiet-foxes-skip.md delete mode 100644 .changeset/quiet-pens-return.md delete mode 100644 .changeset/tidy-tags-guard.md delete mode 100644 .changeset/tidy-typed-nodes.md delete mode 100644 .changeset/typed-old-visit.md delete mode 100644 .changeset/wide-config-values.md diff --git a/.changeset/brave-modules-skip.md b/.changeset/brave-modules-skip.md deleted file mode 100644 index 9912f1cc1f8..00000000000 --- a/.changeset/brave-modules-skip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphql-codegen/graphql-modules-preset': patch ---- - -Skip sources without a `location` when grouping sources by module, instead of throwing from `path.normalize`, and skip sources without a `document` AST when building each module's document. Generated output from the CLI is unchanged. diff --git a/.changeset/calm-owls-guard.md b/.changeset/calm-owls-guard.md deleted file mode 100644 index 5b06c7777c5..00000000000 --- a/.changeset/calm-owls-guard.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphql-codegen/core': patch ---- - -Skip documents without a `document` AST when validating documents against the schema, instead of passing `undefined` to validation. Generated output from the CLI is unchanged. diff --git a/.changeset/nested-fragments-cache-keys.md b/.changeset/nested-fragments-cache-keys.md deleted file mode 100644 index 9e5ea854618..00000000000 --- a/.changeset/nested-fragments-cache-keys.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@graphql-codegen/visitor-plugin-common': patch -'@graphql-codegen/typescript-operations': patch ---- - -Fix out-of-memory errors with deeply nested, widely reused fragments (#10940). - -The type cache used while generating selection set types was keyed by every fragment-expanded field -path, so its keys grew exponentially with fragment nesting. Keys are now built from the selection -set as written, referencing fragment spreads by name, so they stay linear in the size of the -documents. The exported `getFieldNames` helper from `@graphql-codegen/visitor-plugin-common`, which -built those expanded paths, is removed. diff --git a/.changeset/quiet-foxes-skip.md b/.changeset/quiet-foxes-skip.md deleted file mode 100644 index c8854ce8573..00000000000 --- a/.changeset/quiet-foxes-skip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphql-codegen/typescript-document-nodes': patch ---- - -Skip documents without a `document` AST instead of throwing from `concatAST` when the plugin is called directly with one. Generated output from the CLI is unchanged. diff --git a/.changeset/quiet-pens-return.md b/.changeset/quiet-pens-return.md deleted file mode 100644 index a938ccaecca..00000000000 --- a/.changeset/quiet-pens-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphql-codegen/add': patch ---- - -The plugin output's `content` is now an empty string (`''`) instead of `null` when `placement` is `prepend` or `append`. This matches the `ComplexPluginOutput` type (`content: string`). Code that calls the plugin directly and checks `content === null` should check for an empty string instead. Generated files are unchanged. diff --git a/.changeset/tidy-tags-guard.md b/.changeset/tidy-tags-guard.md deleted file mode 100644 index be3832d79b7..00000000000 --- a/.changeset/tidy-tags-guard.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphql-codegen/testing': patch ---- - -Make `validateTs`/`compileTs` type-check under `strict: true`: fall back to the compiler host's script target when the passed options have no `target`, and only compute a diagnostic's line/column when it has a `start` position. Adds `@types/common-tags` as a devDependency. diff --git a/.changeset/tidy-typed-nodes.md b/.changeset/tidy-typed-nodes.md deleted file mode 100644 index e079ae76f70..00000000000 --- a/.changeset/tidy-typed-nodes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphql-codegen/typed-document-node': patch ---- - -Skip documents without a `document` AST instead of throwing from `concatAST` when the plugin is called directly with one, and don't add an empty import when `documentNodeImport` has no module. Generated output from the CLI is unchanged. diff --git a/.changeset/typed-old-visit.md b/.changeset/typed-old-visit.md deleted file mode 100644 index 7982132b9a5..00000000000 --- a/.changeset/typed-old-visit.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@graphql-codegen/plugin-helpers': minor ---- - -Type `oldVisit`'s return value instead of returning `any`. For a `DocumentNode`, the result is now `OldVisitDocumentResult`, whose `definitions` are `unknown[]` because leave visitors can replace each definition with any value (usually a string), and definitions without a leave visitor stay as AST nodes. When a `Document` leave visitor returns its own value, pass its type as `oldVisit(...)`. For other roots, the result is `unknown` unless `TResult` is given. - -Code that used the result as `any`, for example `const definitions: string[] = result.definitions`, now needs to filter for strings, cast (`result.definitions as string[]`), or pass `oldVisit(...)`. Runtime behavior and generated output are unchanged. diff --git a/.changeset/wide-config-values.md b/.changeset/wide-config-values.md deleted file mode 100644 index 6c011d95371..00000000000 --- a/.changeset/wide-config-values.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@graphql-codegen/visitor-plugin-common': patch ---- - -Type `getConfigValue`'s `value` parameter as `T | null | undefined`, so the result is non-nullable when a non-nullable default is given. diff --git a/packages/graphql-codegen-core/CHANGELOG.md b/packages/graphql-codegen-core/CHANGELOG.md index 270e716f5bf..f0bd604c89e 100644 --- a/packages/graphql-codegen-core/CHANGELOG.md +++ b/packages/graphql-codegen-core/CHANGELOG.md @@ -1,5 +1,19 @@ # @graphql-codegen/core +## 6.2.1 + +### Patch Changes + +- [#10991](https://github.com/dotansimha/graphql-code-generator/pull/10991) + [`dc70125`](https://github.com/dotansimha/graphql-code-generator/commit/dc7012530290b1b7fa8ad1b017b66e18a6db6078) + Thanks [@eddeee888](https://github.com/eddeee888)! - Skip documents without a `document` AST when + validating documents against the schema, instead of passing `undefined` to validation. Generated + output from the CLI is unchanged. + +- Updated dependencies + [[`1d1153b`](https://github.com/dotansimha/graphql-code-generator/commit/1d1153b3b161fa82057d586bb5fe524e1e17efe3)]: + - @graphql-codegen/plugin-helpers@7.4.0 + ## 6.2.0 ### Minor Changes diff --git a/packages/graphql-codegen-core/package.json b/packages/graphql-codegen-core/package.json index af48820e85e..3e4bcd566f2 100644 --- a/packages/graphql-codegen-core/package.json +++ b/packages/graphql-codegen-core/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/core", - "version": "6.2.0", + "version": "6.2.1", "type": "module", "repository": { "type": "git", diff --git a/packages/plugins/other/add/CHANGELOG.md b/packages/plugins/other/add/CHANGELOG.md index 8efd718a69f..d855cf88744 100644 --- a/packages/plugins/other/add/CHANGELOG.md +++ b/packages/plugins/other/add/CHANGELOG.md @@ -1,5 +1,20 @@ # @graphql-codegen/add +## 7.1.1 + +### Patch Changes + +- [#10986](https://github.com/dotansimha/graphql-code-generator/pull/10986) + [`9597b5b`](https://github.com/dotansimha/graphql-code-generator/commit/9597b5bc76c24b66f3e7b000e1038ad8d6ab3450) + Thanks [@eddeee888](https://github.com/eddeee888)! - The plugin output's `content` is now an empty + string (`''`) instead of `null` when `placement` is `prepend` or `append`. This matches the + `ComplexPluginOutput` type (`content: string`). Code that calls the plugin directly and checks + `content === null` should check for an empty string instead. Generated files are unchanged. + +- Updated dependencies + [[`1d1153b`](https://github.com/dotansimha/graphql-code-generator/commit/1d1153b3b161fa82057d586bb5fe524e1e17efe3)]: + - @graphql-codegen/plugin-helpers@7.4.0 + ## 7.1.0 ### Minor Changes diff --git a/packages/plugins/other/add/package.json b/packages/plugins/other/add/package.json index ccd04f4567f..526f8d81ba2 100644 --- a/packages/plugins/other/add/package.json +++ b/packages/plugins/other/add/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/add", - "version": "7.1.0", + "version": "7.1.1", "type": "module", "description": "GraphQL Code Generator plugin for adding custom content to your output file", "repository": { diff --git a/packages/plugins/other/visitor-plugin-common/CHANGELOG.md b/packages/plugins/other/visitor-plugin-common/CHANGELOG.md index 9de987a0c8f..65963f36fb6 100644 --- a/packages/plugins/other/visitor-plugin-common/CHANGELOG.md +++ b/packages/plugins/other/visitor-plugin-common/CHANGELOG.md @@ -1,5 +1,30 @@ # @graphql-codegen/visitor-plugin-common +## 7.2.6 + +### Patch Changes + +- [#10982](https://github.com/dotansimha/graphql-code-generator/pull/10982) + [`e5361bc`](https://github.com/dotansimha/graphql-code-generator/commit/e5361bcb7ba0c21d94ead07d7b7dfc3d2b11e98c) + Thanks [@eddeee888](https://github.com/eddeee888)! - Fix out-of-memory errors with deeply nested, + widely reused fragments + ([#10940](https://github.com/dotansimha/graphql-code-generator/issues/10940)). + + The type cache used while generating selection set types was keyed by every fragment-expanded + field path, so its keys grew exponentially with fragment nesting. Keys are now built from the + selection set as written, referencing fragment spreads by name, so they stay linear in the size of + the documents. The exported `getFieldNames` helper from `@graphql-codegen/visitor-plugin-common`, + which built those expanded paths, is removed. + +- [#10993](https://github.com/dotansimha/graphql-code-generator/pull/10993) + [`be69e9d`](https://github.com/dotansimha/graphql-code-generator/commit/be69e9d1a9c234061754a1922b8b961ec80a2fcb) + Thanks [@eddeee888](https://github.com/eddeee888)! - Type `getConfigValue`'s `value` parameter as + `T | null | undefined`, so the result is non-nullable when a non-nullable default is given. + +- Updated dependencies + [[`1d1153b`](https://github.com/dotansimha/graphql-code-generator/commit/1d1153b3b161fa82057d586bb5fe524e1e17efe3)]: + - @graphql-codegen/plugin-helpers@7.4.0 + ## 7.2.5 ### Patch Changes diff --git a/packages/plugins/other/visitor-plugin-common/package.json b/packages/plugins/other/visitor-plugin-common/package.json index 5867c5585b8..a2961193eb7 100644 --- a/packages/plugins/other/visitor-plugin-common/package.json +++ b/packages/plugins/other/visitor-plugin-common/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/visitor-plugin-common", - "version": "7.2.5", + "version": "7.2.6", "type": "module", "repository": { "type": "git", diff --git a/packages/plugins/typescript/document-nodes/CHANGELOG.md b/packages/plugins/typescript/document-nodes/CHANGELOG.md index d384206711d..bcd3f244a15 100644 --- a/packages/plugins/typescript/document-nodes/CHANGELOG.md +++ b/packages/plugins/typescript/document-nodes/CHANGELOG.md @@ -1,5 +1,22 @@ # @graphql-codegen/typescript-document-nodes +## 6.1.1 + +### Patch Changes + +- [#10990](https://github.com/dotansimha/graphql-code-generator/pull/10990) + [`df288d8`](https://github.com/dotansimha/graphql-code-generator/commit/df288d8e047bbb7581a9bdc34c465a2988535e80) + Thanks [@eddeee888](https://github.com/eddeee888)! - Skip documents without a `document` AST + instead of throwing from `concatAST` when the plugin is called directly with one. Generated output + from the CLI is unchanged. + +- Updated dependencies + [[`e5361bc`](https://github.com/dotansimha/graphql-code-generator/commit/e5361bcb7ba0c21d94ead07d7b7dfc3d2b11e98c), + [`1d1153b`](https://github.com/dotansimha/graphql-code-generator/commit/1d1153b3b161fa82057d586bb5fe524e1e17efe3), + [`be69e9d`](https://github.com/dotansimha/graphql-code-generator/commit/be69e9d1a9c234061754a1922b8b961ec80a2fcb)]: + - @graphql-codegen/visitor-plugin-common@7.2.6 + - @graphql-codegen/plugin-helpers@7.4.0 + ## 6.1.0 ### Minor Changes diff --git a/packages/plugins/typescript/document-nodes/package.json b/packages/plugins/typescript/document-nodes/package.json index f993356532d..5a387a58325 100644 --- a/packages/plugins/typescript/document-nodes/package.json +++ b/packages/plugins/typescript/document-nodes/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/typescript-document-nodes", - "version": "6.1.0", + "version": "6.1.1", "type": "module", "description": "GraphQL Code Generator plugin for generating TypeScript modules with embedded GraphQL document nodes", "repository": { diff --git a/packages/plugins/typescript/operations/CHANGELOG.md b/packages/plugins/typescript/operations/CHANGELOG.md index bbd62298e11..09988e6de6b 100644 --- a/packages/plugins/typescript/operations/CHANGELOG.md +++ b/packages/plugins/typescript/operations/CHANGELOG.md @@ -1,5 +1,28 @@ # @graphql-codegen/typescript-operations +## 6.1.8 + +### Patch Changes + +- [#10982](https://github.com/dotansimha/graphql-code-generator/pull/10982) + [`e5361bc`](https://github.com/dotansimha/graphql-code-generator/commit/e5361bcb7ba0c21d94ead07d7b7dfc3d2b11e98c) + Thanks [@eddeee888](https://github.com/eddeee888)! - Fix out-of-memory errors with deeply nested, + widely reused fragments + ([#10940](https://github.com/dotansimha/graphql-code-generator/issues/10940)). + + The type cache used while generating selection set types was keyed by every fragment-expanded + field path, so its keys grew exponentially with fragment nesting. Keys are now built from the + selection set as written, referencing fragment spreads by name, so they stay linear in the size of + the documents. The exported `getFieldNames` helper from `@graphql-codegen/visitor-plugin-common`, + which built those expanded paths, is removed. + +- Updated dependencies + [[`e5361bc`](https://github.com/dotansimha/graphql-code-generator/commit/e5361bcb7ba0c21d94ead07d7b7dfc3d2b11e98c), + [`1d1153b`](https://github.com/dotansimha/graphql-code-generator/commit/1d1153b3b161fa82057d586bb5fe524e1e17efe3), + [`be69e9d`](https://github.com/dotansimha/graphql-code-generator/commit/be69e9d1a9c234061754a1922b8b961ec80a2fcb)]: + - @graphql-codegen/visitor-plugin-common@7.2.6 + - @graphql-codegen/plugin-helpers@7.4.0 + ## 6.1.7 ### Patch Changes diff --git a/packages/plugins/typescript/operations/package.json b/packages/plugins/typescript/operations/package.json index d8a96f1883c..3334698e1be 100644 --- a/packages/plugins/typescript/operations/package.json +++ b/packages/plugins/typescript/operations/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/typescript-operations", - "version": "6.1.7", + "version": "6.1.8", "type": "module", "description": "GraphQL Code Generator plugin for generating TypeScript types for GraphQL queries, mutations, subscriptions and fragments", "repository": { diff --git a/packages/plugins/typescript/typed-document-node/CHANGELOG.md b/packages/plugins/typescript/typed-document-node/CHANGELOG.md index c351098ca8a..dfec572c735 100644 --- a/packages/plugins/typescript/typed-document-node/CHANGELOG.md +++ b/packages/plugins/typescript/typed-document-node/CHANGELOG.md @@ -1,5 +1,22 @@ # @graphql-codegen/typed-document-node +## 7.1.1 + +### Patch Changes + +- [#10994](https://github.com/dotansimha/graphql-code-generator/pull/10994) + [`1d1153b`](https://github.com/dotansimha/graphql-code-generator/commit/1d1153b3b161fa82057d586bb5fe524e1e17efe3) + Thanks [@eddeee888](https://github.com/eddeee888)! - Skip documents without a `document` AST + instead of throwing from `concatAST` when the plugin is called directly with one, and don't add an + empty import when `documentNodeImport` has no module. Generated output from the CLI is unchanged. + +- Updated dependencies + [[`e5361bc`](https://github.com/dotansimha/graphql-code-generator/commit/e5361bcb7ba0c21d94ead07d7b7dfc3d2b11e98c), + [`1d1153b`](https://github.com/dotansimha/graphql-code-generator/commit/1d1153b3b161fa82057d586bb5fe524e1e17efe3), + [`be69e9d`](https://github.com/dotansimha/graphql-code-generator/commit/be69e9d1a9c234061754a1922b8b961ec80a2fcb)]: + - @graphql-codegen/visitor-plugin-common@7.2.6 + - @graphql-codegen/plugin-helpers@7.4.0 + ## 7.1.0 ### Minor Changes diff --git a/packages/plugins/typescript/typed-document-node/package.json b/packages/plugins/typescript/typed-document-node/package.json index 3557188d16f..9eff75e73b4 100644 --- a/packages/plugins/typescript/typed-document-node/package.json +++ b/packages/plugins/typescript/typed-document-node/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/typed-document-node", - "version": "7.1.0", + "version": "7.1.1", "type": "module", "description": "GraphQL Code Generator plugin for generating ready-to-use TypedDocumentNode based on GraphQL operations", "repository": { diff --git a/packages/presets/graphql-modules/CHANGELOG.md b/packages/presets/graphql-modules/CHANGELOG.md index 4a58ab0485d..4bea7565b50 100644 --- a/packages/presets/graphql-modules/CHANGELOG.md +++ b/packages/presets/graphql-modules/CHANGELOG.md @@ -1,5 +1,22 @@ # @graphql-codegen/graphql-modules-preset +## 6.1.1 + +### Patch Changes + +- [#10993](https://github.com/dotansimha/graphql-code-generator/pull/10993) + [`be69e9d`](https://github.com/dotansimha/graphql-code-generator/commit/be69e9d1a9c234061754a1922b8b961ec80a2fcb) + Thanks [@eddeee888](https://github.com/eddeee888)! - Skip sources without a `location` when + grouping sources by module, instead of throwing from `path.normalize`, and skip sources without a + `document` AST when building each module's document. Generated output from the CLI is unchanged. + +- Updated dependencies + [[`e5361bc`](https://github.com/dotansimha/graphql-code-generator/commit/e5361bcb7ba0c21d94ead07d7b7dfc3d2b11e98c), + [`1d1153b`](https://github.com/dotansimha/graphql-code-generator/commit/1d1153b3b161fa82057d586bb5fe524e1e17efe3), + [`be69e9d`](https://github.com/dotansimha/graphql-code-generator/commit/be69e9d1a9c234061754a1922b8b961ec80a2fcb)]: + - @graphql-codegen/visitor-plugin-common@7.2.6 + - @graphql-codegen/plugin-helpers@7.4.0 + ## 6.1.0 ### Minor Changes diff --git a/packages/presets/graphql-modules/package.json b/packages/presets/graphql-modules/package.json index ddf9ff01a11..42cf5958fc6 100644 --- a/packages/presets/graphql-modules/package.json +++ b/packages/presets/graphql-modules/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/graphql-modules-preset", - "version": "6.1.0", + "version": "6.1.1", "type": "module", "description": "GraphQL Code Generator preset for modularized schema", "repository": { diff --git a/packages/utils/graphql-codegen-testing/CHANGELOG.md b/packages/utils/graphql-codegen-testing/CHANGELOG.md index a1316cdde08..85bc14e2d23 100644 --- a/packages/utils/graphql-codegen-testing/CHANGELOG.md +++ b/packages/utils/graphql-codegen-testing/CHANGELOG.md @@ -1,5 +1,20 @@ # @graphql-codegen/testing +## 5.0.5 + +### Patch Changes + +- [#10992](https://github.com/dotansimha/graphql-code-generator/pull/10992) + [`024fa34`](https://github.com/dotansimha/graphql-code-generator/commit/024fa34a88073d073c7fa10db0092ee5000dfdca) + Thanks [@eddeee888](https://github.com/eddeee888)! - Make `validateTs`/`compileTs` type-check + under `strict: true`: fall back to the compiler host's script target when the passed options have + no `target`, and only compute a diagnostic's line/column when it has a `start` position. Adds + `@types/common-tags` as a devDependency. + +- Updated dependencies + [[`1d1153b`](https://github.com/dotansimha/graphql-code-generator/commit/1d1153b3b161fa82057d586bb5fe524e1e17efe3)]: + - @graphql-codegen/plugin-helpers@7.4.0 + ## 5.0.4 ### Patch Changes diff --git a/packages/utils/graphql-codegen-testing/package.json b/packages/utils/graphql-codegen-testing/package.json index c9aac4ca4d8..f9919e40ea3 100644 --- a/packages/utils/graphql-codegen-testing/package.json +++ b/packages/utils/graphql-codegen-testing/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/testing", - "version": "5.0.4", + "version": "5.0.5", "type": "module", "description": "GraphQL Codegen Testing Utils", "repository": "git@github.com:dotansimha/graphql-code-generator.git", diff --git a/packages/utils/plugins-helpers/CHANGELOG.md b/packages/utils/plugins-helpers/CHANGELOG.md index 64d98b3f3ea..24d0ebd308f 100644 --- a/packages/utils/plugins-helpers/CHANGELOG.md +++ b/packages/utils/plugins-helpers/CHANGELOG.md @@ -1,5 +1,23 @@ # @graphql-codegen/plugin-helpers +## 7.4.0 + +### Minor Changes + +- [#10994](https://github.com/dotansimha/graphql-code-generator/pull/10994) + [`1d1153b`](https://github.com/dotansimha/graphql-code-generator/commit/1d1153b3b161fa82057d586bb5fe524e1e17efe3) + Thanks [@eddeee888](https://github.com/eddeee888)! - Type `oldVisit`'s return value instead of + returning `any`. For a `DocumentNode`, the result is now `OldVisitDocumentResult`, whose + `definitions` are `unknown[]` because leave visitors can replace each definition with any value + (usually a string), and definitions without a leave visitor stay as AST nodes. When a `Document` + leave visitor returns its own value, pass its type as `oldVisit(...)`. For other roots, + the result is `unknown` unless `TResult` is given. + + Code that used the result as `any`, for example + `const definitions: string[] = result.definitions`, now needs to filter for strings, cast + (`result.definitions as string[]`), or pass `oldVisit(...)`. Runtime behavior and generated + output are unchanged. + ## 7.3.0 ### Minor Changes diff --git a/packages/utils/plugins-helpers/package.json b/packages/utils/plugins-helpers/package.json index 59be9e85ce6..58afd099c7b 100644 --- a/packages/utils/plugins-helpers/package.json +++ b/packages/utils/plugins-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/plugin-helpers", - "version": "7.3.0", + "version": "7.4.0", "type": "module", "description": "GraphQL Code Generator common utils and types", "repository": {