Skip to content

Commit ff7c945

Browse files
committed
✨ mutation tree
1 parent e142111 commit ff7c945

File tree

6 files changed

+664
-169
lines changed

6 files changed

+664
-169
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "graphql-js-tree",
3-
"version": "0.2.2",
3+
"version": "0.2.3",
44
"private": false,
55
"license": "MIT",
66
"description": "GraphQL Parser providing simplier structure",

src/TreeOperations/interface.ts

Lines changed: 18 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { ParserField, TypeDefinition } from '@/Models';
22
import { createParserField } from '@/shared';
3-
import { filterNotNull } from '@/TreeOperations/shared';
3+
import { regenerateId } from '@/TreeOperations/shared';
44

5-
const updateNodeByInterfaceAddField = (interfaceNode: ParserField) => (node: ParserField) => {
5+
export const _updateNodeByInterfaceAddField = (interfaceNode: ParserField) => (node: ParserField) => {
66
interfaceNode.args.forEach((ia) => {
77
const sameFieldInNode = node.args.findIndex((na) => na.name === ia.name);
88
if (sameFieldInNode === -1) {
@@ -17,63 +17,20 @@ const updateNodeByInterfaceAddField = (interfaceNode: ParserField) => (node: Par
1717
});
1818
};
1919

20-
export const deleteFieldFromInterface = (
21-
nodes: ParserField[],
22-
updatedInterfaceNode: ParserField,
23-
fieldName: string,
24-
) => {
25-
nodes
26-
.filter((n) => n.interfaces.includes(updatedInterfaceNode.name))
27-
.forEach((nodeWithThisInterface) => {
28-
nodeWithThisInterface.args = nodeWithThisInterface.args
29-
.map((a) => {
30-
if (a.name !== fieldName || !a.fromInterface) return a;
31-
if (a.fromInterface.length === 1) return null;
32-
return {
33-
...a,
34-
fromInterface: a.fromInterface.filter((ai) => ai !== updatedInterfaceNode.name),
35-
};
36-
})
37-
.filter(filterNotNull);
38-
});
39-
};
40-
41-
export const updateInterfaceNodeAddField = (nodes: ParserField[], interfaceNode: ParserField) => {
42-
const updateWithInterface = updateNodeByInterfaceAddField(interfaceNode);
43-
nodes.filter((n) => n.interfaces.includes(interfaceNode.name)).forEach(updateWithInterface);
44-
};
45-
const replaceField = (oldField: ParserField, newField: ParserField) => (node: ParserField) => {
20+
export const _replaceField = (oldField: ParserField, newField: ParserField) => (node: ParserField) => {
4621
const fieldToChange = node.args.findIndex((na) => na.name === oldField.name);
4722
const argToChange = node.args[fieldToChange];
4823
if (node.args[fieldToChange] && argToChange.fromInterface) {
4924
node.args[fieldToChange] = createParserField({
5025
...newField,
5126
fromInterface: [...argToChange.fromInterface],
5227
});
28+
regenerateId(node.args[fieldToChange]);
29+
regenerateId(node);
5330
}
5431
};
5532

56-
export const changeInterfaceField = (
57-
nodes: ParserField[],
58-
interfaceNode: ParserField,
59-
oldField: ParserField,
60-
newField: ParserField,
61-
) => {
62-
const updateWithOldField = replaceField(oldField, newField);
63-
nodes.filter((n) => n.interfaces.includes(interfaceNode.name)).forEach(updateWithOldField);
64-
};
65-
66-
export const renameInterfaceNode = (nodes: ParserField[], newName: string, oldName: string) => {
67-
nodes
68-
.filter((n) => n.interfaces.includes(oldName))
69-
.forEach((n) => {
70-
n.interfaces = n.interfaces.filter((i) => i !== oldName).concat([newName]);
71-
n.args.forEach((a) => {
72-
a.fromInterface = a.fromInterface?.filter((fi) => fi !== oldName).concat([newName]);
73-
});
74-
});
75-
};
76-
const getAllConnectedInterfaces = (nodes: ParserField[], interfaces: string[]) => {
33+
export const _getAllConnectedInterfaces = (nodes: ParserField[], interfaces: string[]) => {
7734
const computedInterfaces: string[] = [];
7835
const computeConnectedInterfaces = (nodes: ParserField[], interfaces: string[], interfacesToPush: string[]) => {
7936
const allInterfaces = nodes.filter((ni) => ni.data.type === TypeDefinition.InterfaceTypeDefinition);
@@ -88,38 +45,20 @@ const getAllConnectedInterfaces = (nodes: ParserField[], interfaces: string[]) =
8845
computeConnectedInterfaces(nodes, interfaces, computedInterfaces);
8946
return computedInterfaces;
9047
};
91-
export const implementInterfaceOnNode = (nodes: ParserField[], node: ParserField, interfaceNode: ParserField) => {
92-
const interfacesToPush = getAllConnectedInterfaces(nodes, [interfaceNode.name]);
93-
node.interfaces.push(...interfacesToPush);
94-
const argsToPush = interfaceNode.args?.filter((a) => !node.args?.find((na) => na.name === a.name)) || [];
95-
node.args = node.args.map((a) => {
96-
if (interfaceNode.args.find((interfaceArg) => interfaceArg.name === a.name)) {
97-
return {
98-
...a,
99-
fromInterface: (a.fromInterface || []).concat([interfaceNode.name]),
100-
};
101-
}
102-
return a;
103-
});
104-
node.args = node.args?.concat(
105-
argsToPush.map((atp) => ({
106-
...atp,
107-
fromInterface: [interfaceNode.name],
108-
})),
109-
);
48+
49+
export const updateInterfaceNodeAddField = (nodes: ParserField[], interfaceNode: ParserField) => {
50+
const updateWithInterface = _updateNodeByInterfaceAddField(interfaceNode);
51+
nodes.filter((n) => n.interfaces.includes(interfaceNode.name)).forEach(updateWithInterface);
11052
};
11153

112-
export const deImplementInterfaceOnNode = (nodes: ParserField[], node: ParserField, interfaceName: string) => {
113-
const interfacesToDeImplement = getAllConnectedInterfaces(nodes, [interfaceName]);
114-
node.interfaces = node.interfaces.filter((ni) => !interfacesToDeImplement.includes(ni));
115-
node.args = node.args
116-
.map((a) => {
117-
if (!a.fromInterface?.length) return a;
118-
a.fromInterface = a.fromInterface.filter((fi) => !interfacesToDeImplement.includes(fi));
119-
if (a.fromInterface.length === 0) return null;
120-
return a;
121-
})
122-
.filter(filterNotNull);
54+
export const changeInterfaceField = (
55+
nodes: ParserField[],
56+
interfaceNode: ParserField,
57+
oldField: ParserField,
58+
newField: ParserField,
59+
) => {
60+
const updateWithOldField = _replaceField(oldField, newField);
61+
nodes.filter((n) => n.interfaces.includes(interfaceNode.name)).forEach(updateWithOldField);
12362
};
12463

12564
// export const deleteInterfaceExtensionNode = (nodes: ParserField[], node: ParserField) => {};

src/TreeOperations/tree.ts

Lines changed: 77 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,80 @@
1-
import { ParserField, TypeDefinition, Options, TypeSystemDefinition, TypeExtension, ParserTree } from '@/Models';
2-
import { getTypeName, createParserField } from '@/shared';
1+
import { ParserField, TypeDefinition, TypeSystemDefinition, TypeExtension, ParserTree } from '@/Models';
2+
import { getTypeName } from '@/shared';
33
import {
4-
deleteFieldFromInterface,
54
changeInterfaceField,
65
updateInterfaceNodeAddField,
7-
renameInterfaceNode,
8-
implementInterfaceOnNode,
9-
deImplementInterfaceOnNode,
6+
_getAllConnectedInterfaces,
107
} from '@/TreeOperations/interface';
118
import { ChangeAllRelatedNodes, filterNotNull, isExtensionNode, regenerateId } from '@/TreeOperations/shared';
129

1310
export const mutate = (tree: ParserTree, allNodes: ParserField[]) => {
14-
const mutateParentIfField = (node: ParserField, nodeId: string) => {
11+
const mutateParentIfField = (node: ParserField) => {
1512
if (node.data.type === TypeSystemDefinition.FieldDefinition) {
16-
const parentNode = allNodes.find((an) => an.args.some((a) => a.id === nodeId));
13+
const parentNode = allNodes.find((an) => an.args.some((a) => a.id === node.id));
1714
if (!parentNode) throw new Error('Invalid field definition');
18-
const fieldIndex = parentNode.args.findIndex((a) => a.id == nodeId);
15+
const fieldIndex = parentNode.args.findIndex((a) => a.id == node.id);
1916
updateFieldOnNode(parentNode, fieldIndex, node);
2017
return;
2118
}
2219
};
2320
const deleteFieldFromNode = (n: ParserField, i: number) => {
24-
const nodeId = n.id;
2521
const argName = n.args[i].name;
2622
if (n.data.type === TypeDefinition.InterfaceTypeDefinition) {
27-
deleteFieldFromInterface(tree.nodes, n, argName);
23+
tree.nodes
24+
.filter((filterNode) => filterNode.interfaces.includes(n.name))
25+
.forEach((nodeWithThisInterface) => {
26+
nodeWithThisInterface.args = nodeWithThisInterface.args
27+
.map((a) => {
28+
if (a.name !== argName || !a.fromInterface) return a;
29+
if (a.fromInterface.length === 1) return null;
30+
return {
31+
...a,
32+
fromInterface: a.fromInterface.filter((ai) => ai !== n.name),
33+
};
34+
})
35+
.filter(filterNotNull);
36+
});
2837
}
2938
n.args.splice(i, 1);
3039
regenerateId(n);
31-
mutateParentIfField(n, nodeId);
40+
mutateParentIfField(n);
3241
};
3342

3443
const updateFieldOnNode = (node: ParserField, i: number, updatedField: ParserField) => {
35-
const oldField = JSON.parse(JSON.stringify(node.args[i]));
36-
const nodeId = node.id;
44+
regenerateId(updatedField);
3745
if (node.data.type === TypeDefinition.InterfaceTypeDefinition) {
46+
const oldField: ParserField = JSON.parse(JSON.stringify(node.args[i]));
3847
changeInterfaceField(tree.nodes, node, oldField, updatedField);
3948
}
4049
node.args[i] = updatedField;
4150
regenerateId(node);
42-
mutateParentIfField(node, nodeId);
51+
mutateParentIfField(node);
4352
};
4453

45-
const addFieldToNode = (node: ParserField, { id, ...f }: ParserField, name?: string) => {
46-
let newName = name || f.name[0].toLowerCase() + f.name.slice(1);
47-
const existingNodes = node.args?.filter((a) => a.name.match(`${newName}\d?`)) || [];
48-
if (existingNodes.length > 0) {
49-
newName = `${newName}${existingNodes.length}`;
50-
}
51-
const nodeId = id;
52-
node.args?.push(
53-
createParserField({
54-
...f,
55-
directives: [],
56-
interfaces: [],
57-
args: [],
58-
type: {
59-
fieldType: {
60-
name: f.name,
61-
type: Options.name,
62-
},
63-
},
64-
name: newName,
65-
}),
66-
);
54+
const addFieldToNode = (node: ParserField, f: ParserField) => {
55+
node.args?.push({ ...f });
6756
if (node.data.type === TypeDefinition.InterfaceTypeDefinition) {
6857
updateInterfaceNodeAddField(tree.nodes, node);
6958
}
70-
mutateParentIfField(node, nodeId);
59+
regenerateId(node);
60+
mutateParentIfField(node);
7161
};
7262
const renameNode = (node: ParserField, newName: string) => {
7363
const isError = allNodes.map((n) => n.name).includes(newName);
7464
if (isError) {
7565
return;
7666
}
7767
if (node.data.type === TypeDefinition.InterfaceTypeDefinition) {
78-
renameInterfaceNode(tree.nodes, newName, node.name);
68+
const oldName = node.name;
69+
tree.nodes
70+
.filter((n) => n.interfaces.includes(oldName))
71+
.forEach((n) => {
72+
n.interfaces = n.interfaces.filter((i) => i !== oldName).concat([newName]);
73+
n.args.forEach((a) => {
74+
a.fromInterface = a.fromInterface?.filter((fi) => fi !== oldName).concat([newName]);
75+
});
76+
regenerateId(n);
77+
});
7978
}
8079
ChangeAllRelatedNodes({
8180
newName,
@@ -88,11 +87,10 @@ export const mutate = (tree: ParserTree, allNodes: ParserField[]) => {
8887
const removeNode = (node: ParserField) => {
8988
const deletedNode = tree.nodes.findIndex((n) => n === node);
9089
if (deletedNode === -1) throw new Error('Error deleting a node');
91-
const allNodes = [...tree.nodes];
9290
// co jak usuwamy extension interface
9391
if (node.data.type === TypeExtension.InterfaceTypeExtension) {
9492
}
95-
allNodes.splice(deletedNode, 1);
93+
tree.nodes.splice(deletedNode, 1);
9694
tree.nodes.forEach((n) => {
9795
n.args = n.args
9896
.filter((a) => {
@@ -103,13 +101,49 @@ export const mutate = (tree: ParserTree, allNodes: ParserField[]) => {
103101
return a;
104102
})
105103
.filter(filterNotNull);
104+
regenerateId(n);
106105
});
106+
if (node.data.type === TypeDefinition.InterfaceTypeDefinition) {
107+
tree.nodes
108+
.filter((n) => n.interfaces.includes(node.name))
109+
.forEach((n) => {
110+
deImplementInterface(n, node.name);
111+
});
112+
}
107113
};
108114
const implementInterface = (node: ParserField, interfaceNode: ParserField) => {
109-
implementInterfaceOnNode(tree.nodes, node, interfaceNode);
115+
const interfacesToPush = _getAllConnectedInterfaces(allNodes, [interfaceNode.name]);
116+
node.interfaces.push(...interfacesToPush);
117+
const argsToPush = interfaceNode.args?.filter((a) => !node.args?.find((na) => na.name === a.name)) || [];
118+
node.args = node.args.map((a) => {
119+
if (interfaceNode.args.find((interfaceArg) => interfaceArg.name === a.name)) {
120+
return {
121+
...a,
122+
fromInterface: (a.fromInterface || []).concat([interfaceNode.name]),
123+
};
124+
}
125+
return a;
126+
});
127+
node.args = node.args?.concat(
128+
argsToPush.map((atp) => ({
129+
...atp,
130+
fromInterface: [interfaceNode.name],
131+
})),
132+
);
133+
regenerateId(node);
110134
};
111135
const deImplementInterface = (node: ParserField, interfaceName: string) => {
112-
deImplementInterfaceOnNode(tree.nodes, node, interfaceName);
136+
const interfacesToDeImplement = _getAllConnectedInterfaces(allNodes, [interfaceName]);
137+
node.interfaces = node.interfaces.filter((ni) => !interfacesToDeImplement.includes(ni));
138+
node.args = node.args
139+
.map((a) => {
140+
if (!a.fromInterface?.length) return a;
141+
a.fromInterface = a.fromInterface.filter((fi) => !interfacesToDeImplement.includes(fi));
142+
if (a.fromInterface.length === 0) return null;
143+
return a;
144+
})
145+
.filter(filterNotNull);
146+
regenerateId(node);
113147
};
114148
return {
115149
deleteFieldFromNode,

0 commit comments

Comments
 (0)