Skip to content

Conversation

@ikusakov2
Copy link

@ikusakov2 ikusakov2 commented Dec 2, 2025

Description

Generates input types into target files.
Generates output enums into target files.

Related # #10496

Type of change

Please delete options that are not relevant.

  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

How Has This Been Tested?

  • Unit testing

@changeset-bot
Copy link

changeset-bot bot commented Dec 2, 2025

🦋 Changeset detected

Latest commit: 9cea149

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/typescript-operations Major
@graphql-codegen/client-preset Major
@graphql-codegen/cli Patch

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

@ikusakov2 ikusakov2 changed the base branch from master to master-next December 2, 2025 01:47
);
this._declarationBlockConfig = {
ignoreExport: this.config.noExport,
enumNameValueSeparator: ' =',
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I experimented with different enumType, this value appears to be required for other enumTypes to work.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ikusakov2 ikusakov2 changed the title Generate Input types, Output enums into target file Generate input types and output enums into target file Dec 2, 2025
const operationsResult = oldVisit(allAst, { leave: visitor });

let operationsContent = operationsResult.definitions.join('\n');
const operationsDefinitions = operationsResult.definitions;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rearranged/simplified the code here - please check.

operationsContent = operationsResult.definitions.concat(exportConsts).join('\n');
}

if (config.globalNamespace) {
Copy link
Author

@ikusakov2 ikusakov2 Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this block to the end - to include all the generated code.

`);
});

it('try different ways to generate enums', async () => {
Copy link
Collaborator

@eddeee888 eddeee888 Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops sorry! I should have mentioned I'm working on the tests here: #10525

Since similar tests have been moved to standalone.enum.spec.ts, shall we remove the duplicates in this PR?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! Extra tests removed.
We can probably rearrange tests in a better way - to avoid this file getting cluttered.

@eddeee888
Copy link
Collaborator

Sorry! I caused some conflicts in this PR after merging #10525 🙏

Comment on lines 231 to 256
private getInputObjectDeclarationBlock(node: InputObjectTypeDefinitionNode): DeclarationBlock {
return new DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind('type')
.withName(this.convertName(node))
.withComment(node.description?.value)
.withBlock((node.fields || []).join('\n'));
}

private getInputObjectOneOfDeclarationBlock(node: InputObjectTypeDefinitionNode): DeclarationBlock {
const declarationKind = (node.fields?.length || 0) === 1 ? 'type' : 'type';
return new DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind(declarationKind)
.withName(this.convertName(node))
.withComment(node.description?.value)
.withContent(`\n` + (node.fields || []).join('\n |'));
}

private isValidVisit(ancestors: any): boolean {
const currentVisitContext = this.getVisitorKindContextFromAncestors(ancestors);
const isVisitingInputType = currentVisitContext.includes(Kind.INPUT_OBJECT_TYPE_DEFINITION);
const isVisitingEnumType = currentVisitContext.includes(Kind.ENUM_TYPE_DEFINITION);

return isVisitingInputType || isVisitingEnumType;
}
Copy link
Collaborator

@eddeee888 eddeee888 Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With utility functions that can be shared between typescript-operations and base-types-visitor like these, I wonder if we should abstract them into visitor-plugin-common now or in a following PR? 🤔

Having them in visitor-plugin-common helps ensure they'd behave the same all the time. I've done the same for Enum utilities here : https://github.com/dotansimha/graphql-code-generator/pull/10525/files#r2585220345

Copy link
Author

@ikusakov2 ikusakov2 Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll move them to visitor-plugin-common!

Maybe after I fix all the bugs that I discover while running over the yelp codebase?
I am working on a quite tricky bug now with enum not being rendered from a fragment in a separate document.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! No rush.
I also put a FIXME or TODO on these utility functions in my PRs to refactor later

Comment on lines 347 to 350
const typeInfo = new TypeInfo(schema);
visit(
documentNode,
visitWithTypeInfo(typeInfo, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, this is my first time learning about this. Could you help me with the TLDR; of TypeInfo and how it helps us here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am no expert on this as well. I relied on claude AI for this piece of code.
Having said that, here is what I think this code is doing:

@eddeee888 eddeee888 force-pushed the generate-input-types-into-target-file branch 2 times, most recently from 0b36e76 to eca9224 Compare December 21, 2025 08:32
@eddeee888 eddeee888 force-pushed the generate-input-types-into-target-file branch from eca9224 to 83ee745 Compare December 21, 2025 08:39
@eddeee888 eddeee888 force-pushed the generate-input-types-into-target-file branch from 83ee745 to e89b353 Compare December 21, 2025 09:51
@eddeee888 eddeee888 force-pushed the generate-input-types-into-target-file branch 3 times, most recently from ac4d132 to e51f65e Compare December 21, 2025 10:09
@eddeee888 eddeee888 force-pushed the generate-input-types-into-target-file branch from e51f65e to 58a9ac9 Compare December 21, 2025 10:11
.withBlock((node.fields || []).join('\n')).string;
}

InputValueDefinition(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In typescript plugin, the logic to generate a Input Value string is spread across multiple methods:

  • InputObjectTypeDefinition
  • NamedType
  • ListType
  • NonNull
  • And a couple of private methods

That was very confusing to both maintainers and contributors, and results in as any as string in many places.

This version brings all logic into one place method.
I'll see if this could be re-used between both plugins

Copy link
Author

@ikusakov2 ikusakov2 Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent!
I like that you replace all these functions with just one!

However, I am bit concerned about IIFE that you do here. Because this code runs in a hot loop, there is a chance for this code to cause performance issue. The chance is low (it should be optimized by JIT), but still...

Is there a way to write this code without IIFE? In the worst case - we can split it into 3 functions (it is still better than the original version).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call! I've replaced the IIFEs with pure functions in 9cea149

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! Yes, this look safer.
Thanks!

@eddeee888 eddeee888 merged commit ba65af5 into dotansimha:master-next Dec 24, 2025
16 checks passed
eddeee888 added a commit that referenced this pull request Jan 6, 2026
* input types, input/output enums are generated to the target files

* cleanup

* better code

* more tests

* cleanup

* better code

* better tests

* bugfixing for inner types and outer enums

* bugfixing after merge

* cleanup

* cleanup

* fix snapshots

* fix type errors in presets/client

* updated tests/examples

* Add standalone.input.spec.ts and update standalone tests to TDD

* Update operations/visitor.ts to satisfy tests

* Revert Client Preset changes

* Add oneOf directive for GraphQL 15

* Update changeset

* Refactor IIFE

---------

Co-authored-by: Igor Kusakov <[email protected]>
Co-authored-by: Eddy Nguyen <[email protected]>
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.

3 participants