Mirror: The small sibling of the graphql package, slimmed down for client-side libraries.
1import {
2 getEnterLeaveForKind,
3 getVisitFn,
4 visitInParallel,
5 BREAK,
6} from 'graphql/language/visitor';
7
8export { getEnterLeaveForKind, getVisitFn, visitInParallel, BREAK };
9
10export function visit(node, visitor) {
11 const path = [];
12 const ancestors = [];
13
14 function traverse(node, key, parent) {
15 let hasEdited = false;
16
17 const enter = getVisitFn(visitor, node.kind, false);
18 const resultEnter =
19 enter && enter.call(visitor, node, key, parent, path, ancestors);
20 if (resultEnter === false) {
21 return node;
22 } else if (resultEnter === null) {
23 return null;
24 } else if (resultEnter === BREAK) {
25 throw BREAK;
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 leave = getVisitFn(visitor, node.kind, true);
72 const resultLeave =
73 leave && leave.call(visitor, node, key, parent, path, ancestors);
74 if (resultLeave === BREAK) {
75 throw BREAK;
76 } else if (resultLeave !== undefined) {
77 return resultLeave;
78 } else if (resultEnter !== undefined) {
79 return hasEdited ? copy : resultEnter;
80 } else {
81 return hasEdited ? copy : node;
82 }
83 }
84
85 try {
86 const result = traverse(node);
87 return result !== undefined && result !== false ? result : node;
88 } catch (error) {
89 if (error !== BREAK) throw error;
90 return node;
91 }
92}