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 callback(node, key, parent, isLeaving) {
10 const visitFn = getVisitFn(visitor, node.kind, isLeaving);
11 if (visitFn) {
12 const result = visitFn.call(visitor, node, key, parent, path, ancestors);
13 if (result === BREAK) throw BREAK;
14 return result;
15 }
16 }
17
18 function traverse(node, key, parent) {
19 let hasEdited = false;
20
21 const resultEnter = callback(node, key, parent, false);
22 if (resultEnter === false) {
23 return node;
24 } else if (resultEnter === null) {
25 return null;
26 } else if (resultEnter && typeof resultEnter.kind === 'string') {
27 hasEdited = resultEnter !== node;
28 node = resultEnter;
29 }
30
31 if (parent) ancestors.push(parent);
32
33 let result;
34 const copy = { ...node };
35 for (const nodeKey in node) {
36 path.push(nodeKey);
37 let value = node[nodeKey];
38 if (Array.isArray(value)) {
39 const newValue = [];
40 for (let index = 0; index < value.length; index++) {
41 if (value[index] != null && typeof value[index].kind === 'string') {
42 ancestors.push(node);
43 path.push(index);
44 result = traverse(value[index], index, value);
45 path.pop();
46 ancestors.pop();
47 if (result === undefined) {
48 newValue.push(value[index]);
49 } else if (result === null) {
50 hasEdited = true;
51 } else {
52 hasEdited = hasEdited || result !== value[index];
53 newValue.push(result);
54 }
55 }
56 }
57 value = newValue;
58 } else if (value != null && typeof value.kind === 'string') {
59 result = traverse(value, nodeKey, node);
60 if (result !== undefined) {
61 hasEdited = hasEdited || value !== result;
62 value = result;
63 }
64 }
65
66 path.pop();
67 if (hasEdited) copy[nodeKey] = value;
68 }
69
70 if (parent) ancestors.pop();
71 const resultLeave = callback(node, key, parent, true);
72 if (resultLeave !== undefined) {
73 return resultLeave;
74 } else if (resultEnter !== undefined) {
75 return hasEdited ? copy : resultEnter;
76 } else {
77 return hasEdited ? copy : node;
78 }
79 }
80
81 try {
82 const result = traverse(node);
83 return result !== undefined && result !== false ? result : node;
84 } catch (error) {
85 if (error !== BREAK) throw error;
86 return node;
87 }
88}