Skip to content

Commit

Permalink
fix: out-of-memory crash and significantly improve performance
Browse files Browse the repository at this point in the history
Co-authored-by: Cynthia Ma <[email protected]>
Co-authored-by: Bazyli Brzoska <[email protected]>

fixes #7720
  • Loading branch information
niieani committed Oct 23, 2023
1 parent 80e4a5b commit 2f1c809
Show file tree
Hide file tree
Showing 9 changed files with 1,589 additions and 105 deletions.
9 changes: 9 additions & 0 deletions .changeset/hip-garlics-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@graphql-codegen/visitor-plugin-common': minor
'@graphql-codegen/typescript-operations': minor
---

fix: out-of-memory crash (fixes #7720)
perf: implement a caching mechanism that makes sure the type originating at the same location is never generated twice, as long as the combination of selected fields and possible types matches
feat: implement `extractAllFieldsToTypes: boolean`
feat: implement `printFieldsOnNewLines: boolean`
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ function getRootType(operation: OperationTypeNode, schema: GraphQLSchema) {
export interface ParsedDocumentsConfig extends ParsedTypesConfig {
addTypename: boolean;
preResolveTypes: boolean;
extractAllFieldsToTypes: boolean;
globalNamespace: boolean;
operationResultSuffix: string;
dedupeOperationSuffix: boolean;
Expand Down Expand Up @@ -275,6 +276,11 @@ export class BaseDocumentsVisitor<
);
const operationType: string = pascalCase(node.operation);
const operationTypeSuffix = this.getOperationSuffix(name, operationType);
const selectionSetObjects = selectionSet.transformSelectionSet(
this.convertName(name, {
suffix: operationTypeSuffix,
})
);

const operationResult = new DeclarationBlock(this._declarationBlockConfig)
.export()
Expand All @@ -284,7 +290,7 @@ export class BaseDocumentsVisitor<
suffix: operationTypeSuffix + this._parsedConfig.operationResultSuffix,
})
)
.withContent(selectionSet.transformSelectionSet()).string;
.withContent(selectionSetObjects.mergedTypeString).string;

const operationVariables = new DeclarationBlock({
...this._declarationBlockConfig,
Expand All @@ -299,6 +305,23 @@ export class BaseDocumentsVisitor<
)
.withBlock(visitedOperationVariables).string;

return [operationVariables, operationResult].filter(r => r).join('\n\n');
const dependentTypesContent = this._parsedConfig.extractAllFieldsToTypes
? selectionSetObjects.dependentTypes.map(
i =>
new DeclarationBlock(this._declarationBlockConfig)
.export()
.asKind('type')
.withName(i.name)
.withContent(i.content).string
)
: [];

return [
...(dependentTypesContent.length > 0 ? [dependentTypesContent.join('\n')] : []),
operationVariables,
operationResult,
]
.filter(r => r)
.join('\n\n');
}
}
21 changes: 21 additions & 0 deletions packages/plugins/other/visitor-plugin-common/src/base-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface ParsedConfig {
typesSuffix: string;
addTypename: boolean;
nonOptionalTypename: boolean;
extractAllFieldsToTypes: boolean;
externalFragments: LoadedFragment[];
fragmentImports: ImportDeclaration<FragmentImport>[];
immutableTypes: boolean;
Expand All @@ -36,6 +37,7 @@ export interface ParsedConfig {
allowEnumStringTypes: boolean;
inlineFragmentTypes: InlineFragmentTypeOptions;
emitLegacyCommonJSImports: boolean;
printFieldsOnNewLines: boolean;
}

export interface RawConfig {
Expand Down Expand Up @@ -371,6 +373,23 @@ export interface RawConfig {
* Default it will be `true` this way it ensure that generated code works with [non-compliant bundlers](https://github.com/dotansimha/graphql-code-generator/issues/8065).
*/
emitLegacyCommonJSImports?: boolean;

/**
* @default false
* @description Extract all field types to their own types, instead of inlining them.
* This helps to reduce type duplication, and makes type errors more readable.
* It can also significantly reduce the size of the generated code, the generation time,
* and the typechecking time.
*/
extractAllFieldsToTypes?: boolean;

/**
* @default false
* @description If you prefer to have each field in generated types printed on a new line, set this to true.
* This can be useful for improving readability of the resulting types,
* without resorting to running tools like Prettier on the output.
*/
printFieldsOnNewLines?: boolean;
}

export class BaseVisitor<TRawConfig extends RawConfig = RawConfig, TPluginConfig extends ParsedConfig = ParsedConfig> {
Expand All @@ -393,6 +412,8 @@ export class BaseVisitor<TRawConfig extends RawConfig = RawConfig, TPluginConfig
inlineFragmentTypes: rawConfig.inlineFragmentTypes ?? 'inline',
emitLegacyCommonJSImports:
rawConfig.emitLegacyCommonJSImports === undefined ? true : !!rawConfig.emitLegacyCommonJSImports,
extractAllFieldsToTypes: rawConfig.extractAllFieldsToTypes ?? false,
printFieldsOnNewLines: rawConfig.printFieldsOnNewLines ?? false,
...((additionalConfig || {}) as any),
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GraphQLInterfaceType, GraphQLNamedType, GraphQLObjectType, GraphQLOutputType } from 'graphql';
import { GraphQLInterfaceType, GraphQLNamedType, GraphQLObjectType, GraphQLOutputType, Location } from 'graphql';
import { AvoidOptionalsConfig, ConvertNameFn, NormalizedScalarsMap } from '../types.js';

export type PrimitiveField = { isConditional: boolean; fieldName: string };
Expand All @@ -21,12 +21,18 @@ export type SelectionSetProcessorConfig = {
): string;
wrapTypeWithModifiers(baseType: string, type: GraphQLOutputType | GraphQLNamedType): string;
avoidOptionals?: AvoidOptionalsConfig | boolean;
printFieldsOnNewLines?: boolean;
};

export class BaseSelectionSetProcessor<Config extends SelectionSetProcessorConfig> {
typeCache = new Map<Location, Map<string, [string, string]>>();

constructor(public config: Config) {}

buildFieldsIntoObject(allObjectsMerged: string[]): string {
if (this.config.printFieldsOnNewLines) {
return `{\n ${allObjectsMerged.join(',\n ')}\n}`;
}
return `{ ${allObjectsMerged.join(', ')} }`;
}

Expand Down
Loading

0 comments on commit 2f1c809

Please sign in to comment.