1 | import {
|
2 | InlineSelection,
|
3 | FragmentOrSelection,
|
4 | Field,
|
5 | OutputType,
|
6 | Typename,
|
7 | List,
|
8 | Maybe,
|
9 | Type,
|
10 | Leaf
|
11 | } from "./intermediates";
|
12 | import { Set, Map } from "immutable";
|
13 | import { every } from "lodash";
|
14 |
|
15 | export type Fragments = {
|
16 | [fragment: string]: InlineSelection;
|
17 | };
|
18 |
|
19 | type Curry<T, U, V> = (t: T) => (u: U) => V;
|
20 |
|
21 | export type ExtendedFieldType = Type<ExtendedSelection | Leaf | Typename>;
|
22 |
|
23 | const ExtendedFieldType: Curry<
|
24 | Fragments,
|
25 | OutputType | Typename,
|
26 | ExtendedFieldType
|
27 | > = fragments => type => {
|
28 | switch (type.kind) {
|
29 | case "FragmentReference":
|
30 | case "InlineSelection":
|
31 | return ExtendedFragmentOrSelection(fragments)(type);
|
32 | case "List":
|
33 | return List(ExtendedFieldType(fragments)(type.ofType));
|
34 | case "Maybe":
|
35 | return Maybe(ExtendedFieldType(fragments)(type.ofType));
|
36 | default:
|
37 | return type;
|
38 | }
|
39 | };
|
40 |
|
41 | const typesAreEqual = (
|
42 | lhs: ExtendedFieldType,
|
43 | rhs: ExtendedFieldType
|
44 | ): boolean => {
|
45 | switch (lhs.kind) {
|
46 | case "Enum":
|
47 | return lhs.kind == rhs.kind && lhs.name == rhs.name;
|
48 | case "Typename":
|
49 | return (
|
50 | lhs.kind == rhs.kind && lhs.possibleTypes.equals(rhs.possibleTypes)
|
51 | );
|
52 | case "Scalar":
|
53 | return lhs.kind == rhs.kind && lhs.name == rhs.name;
|
54 | case "InlineSelection":
|
55 | return lhs.kind == rhs.kind && extendedSelectionsAreEqual(lhs, rhs);
|
56 | case "Maybe":
|
57 | return lhs.kind == rhs.kind && typesAreEqual(lhs.ofType, rhs.ofType);
|
58 | case "List":
|
59 | return lhs.kind == rhs.kind && typesAreEqual(lhs.ofType, rhs.ofType);
|
60 | }
|
61 | };
|
62 |
|
63 | export type ExtendedField = { type: ExtendedFieldType; optional: boolean };
|
64 |
|
65 | const nameAndField: Curry<
|
66 | Fragments,
|
67 | Field,
|
68 | [fieldName, ExtendedField]
|
69 | > = fragments => field => [
|
70 | field.name,
|
71 | { type: ExtendedFieldType(fragments)(field.type), optional: false }
|
72 | ];
|
73 |
|
74 | export type __typename = string;
|
75 | export type fieldName = string;
|
76 | export type ByTypename<T> = Map<__typename, T>;
|
77 | export type ExtendedFields = Map<fieldName, ExtendedField>;
|
78 |
|
79 | export type ExtendedSelection = {
|
80 | kind: "InlineSelection";
|
81 | fields: ByTypename<ExtendedFields>;
|
82 | };
|
83 |
|
84 | export const ExtendedSelection = (
|
85 | inlineSelection: InlineSelection,
|
86 | fragments: Fragments
|
87 | ): ExtendedSelection => {
|
88 | return {
|
89 | kind: inlineSelection.kind,
|
90 | fields: Map(
|
91 | inlineSelection.possibleTypes.map(
|
92 | (
|
93 | type
|
94 | ): [
|
95 | __typename,
|
96 | Map<fieldName, { type: ExtendedFieldType; optional: boolean }>
|
97 | ] => [type, Map(inlineSelection.fields.map(nameAndField(fragments)))]
|
98 | )
|
99 | ).mergeDeep(
|
100 | ...inlineSelection.intersections
|
101 | .map(ExtendedFragmentOrSelection(fragments))
|
102 | .map(selection => selection.fields),
|
103 | ...inlineSelection.typeConditions
|
104 | .map(ExtendedFragmentOrSelection(fragments))
|
105 | .map(selection => selection.fields),
|
106 | ...inlineSelection.booleanConditions
|
107 | .map(ExtendedFragmentOrSelection(fragments))
|
108 | .map(selection => selection.fields)
|
109 | )
|
110 | };
|
111 | };
|
112 |
|
113 | export const ExtendedFragmentOrSelection: Curry<
|
114 | Fragments,
|
115 | FragmentOrSelection,
|
116 | ExtendedSelection
|
117 | > = fragments => fragmentOrSelection =>
|
118 | fragmentOrSelection.kind == "InlineSelection"
|
119 | ? ExtendedSelection(fragmentOrSelection, fragments)
|
120 | : ExtendedSelection(fragments[fragmentOrSelection.name], fragments);
|
121 |
|
122 | const mapsAreEqual = <V>(
|
123 | lhs: Map<string, V>,
|
124 | rhs: Map<string, V>,
|
125 | compareValues: (lhs: V, rhs: V) => boolean
|
126 | ): boolean =>
|
127 | Set(lhs.keySeq()).equals(Set(rhs.keySeq())) &&
|
128 | every(lhs.keySeq().toArray(), key =>
|
129 | compareValues(lhs.get(key)!, rhs.get(key)!)
|
130 | );
|
131 |
|
132 | const extendedFieldsAreEqual = (
|
133 | lhs: ByTypename<ExtendedFields>,
|
134 | rhs: ByTypename<ExtendedFields>
|
135 | ): boolean =>
|
136 | mapsAreEqual(lhs, rhs, (lhs, rhs) =>
|
137 | mapsAreEqual(
|
138 | lhs,
|
139 | rhs,
|
140 | (lhs, rhs) =>
|
141 | lhs.optional == rhs.optional && typesAreEqual(lhs.type, rhs.type)
|
142 | )
|
143 | );
|
144 |
|
145 | export const extendedSelectionsAreEqual = (
|
146 | lhs: ExtendedSelection,
|
147 | rhs: ExtendedSelection
|
148 | ): boolean => extendedFieldsAreEqual(lhs.fields, rhs.fields);
|