forked from graphql/graphql-js
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
memoize field node lists when collecting fields (#96)
* memoize field node lists when collecting fields ...so that functions operating on field lists can in turn be memoized. * add changeset * prettier * fix
- Loading branch information
Showing
5 changed files
with
207 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
'graphql-executor': patch | ||
--- | ||
|
||
Memoize field lists created by the collectFields utility function. | ||
|
||
This allows functions that operate on these field lists to be memoized. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import { expect } from 'chai'; | ||
import { describe, it } from 'mocha'; | ||
|
||
import type { FragmentDefinitionNode, OperationDefinitionNode } from 'graphql'; | ||
import { | ||
GraphQLID, | ||
GraphQLList, | ||
GraphQLObjectType, | ||
GraphQLSchema, | ||
GraphQLString, | ||
parse, | ||
} from 'graphql'; | ||
|
||
import { collectFields } from '../collectFields'; | ||
|
||
const friendType = new GraphQLObjectType({ | ||
fields: { | ||
id: { type: GraphQLID }, | ||
name: { type: GraphQLString }, | ||
}, | ||
name: 'Friend', | ||
}); | ||
|
||
const heroType = new GraphQLObjectType({ | ||
fields: { | ||
id: { type: GraphQLID }, | ||
name: { type: GraphQLString }, | ||
friends: { | ||
type: new GraphQLList(friendType), | ||
}, | ||
}, | ||
name: 'Hero', | ||
}); | ||
|
||
const query = new GraphQLObjectType({ | ||
fields: { | ||
hero: { | ||
type: heroType, | ||
}, | ||
}, | ||
name: 'Query', | ||
}); | ||
|
||
const schema = new GraphQLSchema({ query }); | ||
|
||
const document = parse(` | ||
query HeroQuery($skipFirst: Boolean, $skipSecond: Boolean) { | ||
hero { | ||
name | ||
} | ||
...HeroFragment1 @skip(if: $skipFirst) | ||
...HeroFragment2 @skip(if: $skipSecond) | ||
} | ||
fragment HeroFragment1 on Query { | ||
hero { | ||
name | ||
} | ||
} | ||
fragment HeroFragment2 on Query { | ||
hero { | ||
name | ||
} | ||
} | ||
`); | ||
|
||
const selectionSet = (document.definitions[0] as OperationDefinitionNode) | ||
.selectionSet; | ||
const fragments = { | ||
HeroFragment1: document.definitions[1] as FragmentDefinitionNode, | ||
HeroFragment2: document.definitions[2] as FragmentDefinitionNode, | ||
}; | ||
|
||
describe('collectFields', () => { | ||
it('memoizes', () => { | ||
const { fields: fields1 } = collectFields( | ||
schema, | ||
fragments, | ||
{ | ||
skipFirst: false, | ||
skipSecond: false, | ||
}, | ||
query, | ||
selectionSet, | ||
); | ||
const { fields: fields2 } = collectFields( | ||
schema, | ||
fragments, | ||
{ | ||
skipFirst: false, | ||
skipSecond: false, | ||
}, | ||
query, | ||
selectionSet, | ||
); | ||
|
||
const heroFieldNodes1 = fields1.get('hero'); | ||
const heroFieldNodes2 = fields2.get('hero'); | ||
|
||
expect(heroFieldNodes1).to.equal(heroFieldNodes2); | ||
}); | ||
|
||
it('does not yet (?) memoize everything', () => { | ||
const { fields: fields1 } = collectFields( | ||
schema, | ||
fragments, | ||
{ | ||
skipFirst: true, | ||
skipSecond: false, | ||
}, | ||
query, | ||
selectionSet, | ||
); | ||
const { fields: fields2 } = collectFields( | ||
schema, | ||
fragments, | ||
{ | ||
skipFirst: false, | ||
skipSecond: true, | ||
}, | ||
query, | ||
selectionSet, | ||
); | ||
|
||
const heroFieldNodes1 = fields1.get('hero'); | ||
const heroFieldNodes2 = fields2.get('hero'); | ||
|
||
expect(heroFieldNodes1).to.not.equal(heroFieldNodes2); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/** | ||
* Memoizes the provided one-argument function. | ||
*/ | ||
export function memoize1<A1 extends object, R>( | ||
fn: (a1: A1) => R, | ||
): (a1: A1) => R { | ||
let cache0: WeakMap<A1, R>; | ||
|
||
return function memoized(a1) { | ||
if (cache0 === undefined) { | ||
cache0 = new WeakMap(); | ||
} | ||
|
||
let fnResult = cache0.get(a1); | ||
if (fnResult === undefined) { | ||
fnResult = fn(a1); | ||
cache0.set(a1, fnResult); | ||
} | ||
|
||
return fnResult; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/** | ||
* Memoizes the provided two-argument function. | ||
*/ | ||
export function memoize2<A1 extends object, A2 extends object, R>( | ||
fn: (a1: A1, a2: A2) => R, | ||
): (a1: A1, a2: A2) => R { | ||
let cache0: WeakMap<A1, WeakMap<A2, R>>; | ||
|
||
return function memoized(a1, a2) { | ||
if (cache0 === undefined) { | ||
cache0 = new WeakMap(); | ||
} | ||
|
||
let cache1 = cache0.get(a1); | ||
if (cache1 === undefined) { | ||
cache1 = new WeakMap(); | ||
cache0.set(a1, cache1); | ||
} | ||
|
||
let fnResult = cache1.get(a2); | ||
if (fnResult === undefined) { | ||
fnResult = fn(a1, a2); | ||
cache1.set(a2, fnResult); | ||
} | ||
|
||
return fnResult; | ||
}; | ||
} |