Skip to content

Commit 0717b7d

Browse files
feat(terminateCircularRelationships): Add 'immediate' as a value for the terminateCircularRelationships option (#161)
This value will make the mock not create a new Set of relationships to ignore and create a global Set. This will circumvent possible OOM due to large graphs. fixes #126
1 parent 97ebd1b commit 0717b7d

File tree

6 files changed

+103
-10
lines changed

6 files changed

+103
-10
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@ Adds `__typename` property to mock data
2626

2727
Changes enums to TypeScript string union types
2828

29-
### terminateCircularRelationships (`boolean`, defaultValue: `false`)
29+
### terminateCircularRelationships (`boolean | 'immediate'`, defaultValue: `false`)
3030

3131
When enabled, prevents circular relationships from triggering infinite recursion. After the first resolution of a
3232
specific type in a particular call stack, subsequent resolutions will return an empty object cast to the correct type.
3333

34+
When enabled with `immediate`, it will only resolve the relationship once, independently of the call stack. Use this option if you're experiencing `out of memory` errors while generating mocks.
35+
3436
### prefix (`string`, defaultValue: `a` for consonants & `an` for vowels)
3537

3638
The prefix to add to the mock function name. Cannot be empty since it will clash with the associated

src/index.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type Options<T = TypeNode> = {
2222
types: TypeItem[];
2323
typeNamesConvention: NamingConvention;
2424
enumValuesConvention: NamingConvention;
25-
terminateCircularRelationships: boolean;
25+
terminateCircularRelationships: boolean | 'immediate';
2626
prefix: string | undefined;
2727
typesPrefix: string;
2828
enumsPrefix: string;
@@ -40,6 +40,9 @@ type Options<T = TypeNode> = {
4040
nonNull: boolean;
4141
};
4242

43+
const getTerminateCircularRelationshipsConfig = ({ terminateCircularRelationships }: TypescriptMocksPluginConfig) =>
44+
terminateCircularRelationships ? terminateCircularRelationships : false;
45+
4346
const convertName = (value: string, fn: (v: string) => string, transformUnderscore: boolean): string => {
4447
if (transformUnderscore) {
4548
return fn(value);
@@ -430,7 +433,7 @@ const getMockString = (
430433
typeName: string,
431434
fields: string,
432435
typeNamesConvention: NamingConvention,
433-
terminateCircularRelationships: boolean,
436+
terminateCircularRelationships: boolean | 'immediate',
434437
addTypename = false,
435438
prefix,
436439
typesPrefix = '',
@@ -443,13 +446,15 @@ const getMockString = (
443446
const typenameReturnType = addTypename ? `{ __typename: '${typeName}' } & ` : '';
444447

445448
if (terminateCircularRelationships) {
449+
const relationshipsToOmitInit =
450+
terminateCircularRelationships === 'immediate' ? '_relationshipsToOmit' : 'new Set(_relationshipsToOmit)';
446451
return `
447452
export const ${toMockName(
448453
typeName,
449454
casedName,
450455
prefix,
451456
)} = (overrides?: Partial<${casedNameWithPrefix}>, _relationshipsToOmit: Set<string> = new Set()): ${typenameReturnType}${casedNameWithPrefix} => {
452-
const relationshipsToOmit: Set<string> = new Set(_relationshipsToOmit);
457+
const relationshipsToOmit: Set<string> = ${relationshipsToOmitInit};
453458
relationshipsToOmit.add('${casedName}');
454459
return {${typename}
455460
${fields}
@@ -541,7 +546,7 @@ export interface TypescriptMocksPluginConfig {
541546
addTypename?: boolean;
542547
prefix?: string;
543548
scalars?: ScalarMap;
544-
terminateCircularRelationships?: boolean;
549+
terminateCircularRelationships?: boolean | 'immediate';
545550
typesPrefix?: string;
546551
enumsPrefix?: string;
547552
transformUnderscore?: boolean;
@@ -669,7 +674,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
669674
types,
670675
typeNamesConvention,
671676
enumValuesConvention,
672-
terminateCircularRelationships: !!config.terminateCircularRelationships,
677+
terminateCircularRelationships: getTerminateCircularRelationshipsConfig(config),
673678
prefix: config.prefix,
674679
typesPrefix: config.typesPrefix,
675680
enumsPrefix: config.enumsPrefix,
@@ -706,7 +711,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
706711
types,
707712
typeNamesConvention,
708713
enumValuesConvention,
709-
terminateCircularRelationships: !!config.terminateCircularRelationships,
714+
terminateCircularRelationships: getTerminateCircularRelationshipsConfig(config),
710715
prefix: config.prefix,
711716
typesPrefix: config.typesPrefix,
712717
enumsPrefix: config.enumsPrefix,
@@ -733,7 +738,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
733738
fieldName,
734739
mockFields,
735740
typeNamesConvention,
736-
!!config.terminateCircularRelationships,
741+
getTerminateCircularRelationshipsConfig(config),
737742
false,
738743
config.prefix,
739744
config.typesPrefix,
@@ -756,7 +761,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
756761
typeName,
757762
mockFields,
758763
typeNamesConvention,
759-
!!config.terminateCircularRelationships,
764+
getTerminateCircularRelationshipsConfig(config),
760765
!!config.addTypename,
761766
config.prefix,
762767
config.typesPrefix,
@@ -777,7 +782,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
777782
typeName,
778783
mockFields,
779784
typeNamesConvention,
780-
!!config.terminateCircularRelationships,
785+
getTerminateCircularRelationshipsConfig(config),
781786
!!config.addTypename,
782787
config.prefix,
783788
config.typesPrefix,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`should support setting terminateCircularRelationships as imediate and not create a new set of relationships 1`] = `
4+
"
5+
export const anA = (overrides?: Partial<A>, _relationshipsToOmit: Set<string> = new Set()): A => {
6+
const relationshipsToOmit: Set<string> = _relationshipsToOmit;
7+
relationshipsToOmit.add('A');
8+
return {
9+
B: overrides && overrides.hasOwnProperty('B') ? overrides.B! : relationshipsToOmit.has('B') ? {} as B : aB({}, relationshipsToOmit),
10+
C: overrides && overrides.hasOwnProperty('C') ? overrides.C! : relationshipsToOmit.has('C') ? {} as C : aC({}, relationshipsToOmit),
11+
};
12+
};
13+
14+
export const aB = (overrides?: Partial<B>, _relationshipsToOmit: Set<string> = new Set()): B => {
15+
const relationshipsToOmit: Set<string> = _relationshipsToOmit;
16+
relationshipsToOmit.add('B');
17+
return {
18+
A: overrides && overrides.hasOwnProperty('A') ? overrides.A! : relationshipsToOmit.has('A') ? {} as A : anA({}, relationshipsToOmit),
19+
};
20+
};
21+
22+
export const aC = (overrides?: Partial<C>, _relationshipsToOmit: Set<string> = new Set()): C => {
23+
const relationshipsToOmit: Set<string> = _relationshipsToOmit;
24+
relationshipsToOmit.add('C');
25+
return {
26+
aCollection: overrides && overrides.hasOwnProperty('aCollection') ? overrides.aCollection! : [relationshipsToOmit.has('A') ? {} as A : anA({}, relationshipsToOmit)],
27+
};
28+
};
29+
30+
export const aD = (overrides?: Partial<D>, _relationshipsToOmit: Set<string> = new Set()): D => {
31+
const relationshipsToOmit: Set<string> = _relationshipsToOmit;
32+
relationshipsToOmit.add('D');
33+
return {
34+
A: overrides && overrides.hasOwnProperty('A') ? overrides.A! : relationshipsToOmit.has('A') ? {} as A : anA({}, relationshipsToOmit),
35+
B: overrides && overrides.hasOwnProperty('B') ? overrides.B! : relationshipsToOmit.has('B') ? {} as B : aB({}, relationshipsToOmit),
36+
};
37+
};
38+
"
39+
`;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { buildSchema } from 'graphql';
2+
3+
export default buildSchema(/* GraphQL */ `
4+
type A {
5+
B: B!
6+
C: C!
7+
}
8+
type B {
9+
A: A!
10+
}
11+
type C {
12+
aCollection: [A!]!
13+
}
14+
type D {
15+
A: A!
16+
B: B!
17+
}
18+
`);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { plugin } from '../../src';
2+
import testSchema from './schema';
3+
4+
it('should support setting terminateCircularRelationships as imediate and not create a new set of relationships', async () => {
5+
const result = await plugin(testSchema, [], {
6+
terminateCircularRelationships: 'immediate',
7+
});
8+
9+
expect(result).toBeDefined();
10+
expect(result).not.toContain('new Set(_relationshipsToOmit)');
11+
expect(result).toMatchSnapshot();
12+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export type A = {
2+
B: B;
3+
C: C;
4+
};
5+
6+
export type B = {
7+
A: A;
8+
};
9+
10+
export type C = {
11+
aCollection: A[];
12+
};
13+
14+
export type D = {
15+
A: A;
16+
B: B;
17+
};

0 commit comments

Comments
 (0)