Mirror: The small sibling of the graphql package, slimmed down for client-side libraries.
1import { getVisitFn, visitInParallel, BREAK } from 'graphql/language/visitor';
2
3export { getVisitFn, visitInParallel, BREAK };
4
5export function visit(node, visitor) {
6 const path = [];
7 const ancestors = [];
8
9 function traverse(node, key, parent) {
10 let hasEdited = false;
11
12 const enter = getVisitFn(visitor, node.kind, false);
13 const resultEnter =
14 enter && enter.call(visitor, node, key, parent, path, ancestors);
15 if (resultEnter === false) {
16 return node;
17 } else if (resultEnter === null) {
18 return null;
19 } else if (resultEnter === BREAK) {
20 throw BREAK;
21 } else if (resultEnter && typeof resultEnter.kind === 'string') {
22 hasEdited = resultEnter !== node;
23 node = resultEnter;
24 }
25
26 if (parent) ancestors.push(parent);
27
28 let result;
29 const copy = { ...node };
30 for (const nodeKey in node) {
31 path.push(nodeKey);
32 let value = node[nodeKey];
33 if (Array.isArray(value)) {
34 const newValue = [];
35 for (let index = 0; index < value.length; index++) {
36 if (value[index] != null && typeof value[index].kind === 'string') {
37 ancestors.push(node);
38 path.push(index);
39 result = traverse(value[index], index, value);
40 path.pop();
41 ancestors.pop();
42 if (result === undefined) {
43 newValue.push(value[index]);
44 } else if (result === null) {
45 hasEdited = true;
46 } else {
47 hasEdited = hasEdited || result !== value[index];
48 newValue.push(result);
49 }
50 }
51 }
52 value = newValue;
53 } else if (value != null && typeof value.kind === 'string') {
54 result = traverse(value, nodeKey, node);
55 if (result !== undefined) {
56 hasEdited = hasEdited || value !== result;
57 value = result;
58 }
59 }
60
61 path.pop();
62 if (hasEdited) copy[nodeKey] = value;
63 }
64
65 if (parent) ancestors.pop();
66 const leave = getVisitFn(visitor, node.kind, true);
67 const resultLeave =
68 leave && leave.call(visitor, node, key, parent, path, ancestors);
69 if (resultLeave === BREAK) {
70 throw BREAK;
71 } else if (resultLeave !== undefined) {
72 return resultLeave;
73 } else if (resultEnter !== undefined) {
74 return hasEdited ? copy : resultEnter;
75 } else {
76 return hasEdited ? copy : node;
77 }
78 }
79
80 try {
81 const result = traverse(node);
82 return result !== undefined && result !== false ? result : node;
83 } catch (error) {
84 if (error !== BREAK) throw error;
85 return node;
86 }
87}