Skip to content

Commit

Permalink
add flow types, more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
leebyron committed Aug 25, 2016
1 parent fc8a606 commit df66302
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 10 deletions.
56 changes: 56 additions & 0 deletions src/utilities/__tests__/separateOperations-test.js
Expand Up @@ -116,6 +116,62 @@ fragment Y on T {
fragment B on T {
something
}
`
);

});

it('survives circular dependencies', () => {

const ast = parse(`
query One {
...A
}
fragment A on T {
...B
}
fragment B on T {
...A
}
query Two {
...B
}
`);

const separatedASTs = separateOperations(ast);

expect(Object.keys(separatedASTs)).to.deep.equal([ 'One', 'Two' ]);

expect(print(separatedASTs.One)).to.equal(
`query One {
...A
}
fragment A on T {
...B
}
fragment B on T {
...A
}
`
);

expect(print(separatedASTs.Two)).to.equal(
`fragment A on T {
...B
}
fragment B on T {
...A
}
query Two {
...B
}
`
);

Expand Down
28 changes: 18 additions & 10 deletions src/utilities/separateOperations.js
Expand Up @@ -9,7 +9,10 @@
*/

import { visit } from '../language/visitor';
import type { Document } from '../language/ast';
import type {
Document,
OperationDefinition,
} from '../language/ast';

/**
* separateOperations accepts a single AST document which may contain many
Expand All @@ -22,7 +25,7 @@ export function separateOperations(
): { [operationName: string]: Document } {

const operations = [];
const depGraph = Object.create(null);
const depGraph: DepGraph = Object.create(null);
let fromName;

// Populate the list of operations and build a dependency graph.
Expand All @@ -43,33 +46,38 @@ export function separateOperations(

// For each operation, produce a new synthesized AST which includes only what
// is necessary for completing that operation.
const separatedASTs = Object.create(null);
for (let i = 0; i < operations.length; i++) {
const operation = operations[i];
const separatedDocumentASTs = Object.create(null);
operations.forEach(operation => {
const operationName = opName(operation);
const dependencies = Object.create(null);
collectTransitiveDependencies(dependencies, depGraph, operationName);

separatedASTs[operationName] = {
separatedDocumentASTs[operationName] = {
kind: 'Document',
definitions: documentAST.definitions.filter(def =>
def === operation ||
def.kind === 'FragmentDefinition' && dependencies[def.name.value]
)
};
}
});

return separatedASTs;
return separatedDocumentASTs;
}

type DepGraph = {[from: string]: {[to: string]: boolean}};

// Provides the empty string for anonymous operations.
function opName(operation): string {
function opName(operation: OperationDefinition): string {
return operation.name ? operation.name.value : '';
}

// From a dependency graph, collects a list of transitive dependencies by
// recursing through a dependency graph.
function collectTransitiveDependencies(collected, depGraph, fromName) {
function collectTransitiveDependencies(
collected: {[key: string]: boolean},
depGraph: DepGraph,
fromName: string
): void {
const immediateDeps = depGraph[fromName];
if (immediateDeps) {
Object.keys(immediateDeps).forEach(toName => {
Expand Down

0 comments on commit df66302

Please sign in to comment.