UNPKG

6.7 kBPlain TextView Raw
1import * as ts from 'typescript';
2import {Set} from 'immutable';
3import {namedProp} from "./transformer";
4
5function membersMatch(first, second) {
6 if (first.kind !== second.kind) {
7 return false;
8 }
9 if (first.name.text !== second.name.text) {
10 return false;
11 }
12 if (first.type.kind !== second.type.kind) {
13 return false;
14 }
15 if (first.type.kind === ts.SyntaxKind.ArrayType && second.type.kind === ts.SyntaxKind.ArrayType) {
16 if (first.type.elementType.kind !== second.type.elementType.kind) {
17 return false;
18 }
19 }
20 return true;
21}
22
23function isEmptyArrayType(member) {
24 if (member.type.kind === ts.SyntaxKind.ArrayType) {
25 if (member.type.elementType.kind === ts.SyntaxKind.AnyKeyword) {
26 return true;
27 }
28 }
29 return false;
30}
31
32export function collapseInterfaces(interfaces: any[]): any[] {
33 return interfaces.reduce((acc, current) => {
34
35 const currentName = current.name.text;
36 const currentMemberNames = Set(current.members.map(x => (x.name || x.label).text));
37
38 const matchingInterfaceIndex = acc.findIndex(x => (x.name || x.label).text === currentName);
39
40 if (matchingInterfaceIndex > -1) {
41 return acc
42 .map((int, index) => {
43 if (index === matchingInterfaceIndex) {
44
45 const prevMemberNames = Set(int.members.map(x => (x.name || x.label).text));
46
47 // if the current interface has less props than a previous one
48 // we need to back-track and make the previous one optional
49 if (currentMemberNames.size < prevMemberNames.size) {
50 // elements that existed before, but not in the current
51 const missing = int.members.filter(x => !currentMemberNames.has(x.name.text));
52 missing.forEach(mem => {
53 mem.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken);
54 });
55 }
56
57 if (currentMemberNames.has(int.name.text)) {
58 console.log('exists in both, maybe union', int.name.text);
59 } else {
60 // console.log('incoming current does not exist in prev');
61 const existinMemberNames = Set(int.members.map(x => x.name.text));
62 const newMembers = int.members.slice();
63
64 // Loop over incoming current members
65 current.members.forEach(mem => {
66
67 const existingIndex = int.members.findIndex(x => x.name.text === mem.name.text);
68 const existingMember = int.members[existingIndex];
69
70 // Here, the current member does NOT already exist in this
71 // interface, so we add it, but as optional
72 if (!existingMember) {
73 mem.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken);
74 newMembers.push(mem);
75 } else {
76 // here it exists in both, are the types the same?
77 // console.log(ts.SyntaxKind[mem.type.kind]);
78 // console.log(existingMember.kind, mem.kind);
79 if (membersMatch(existingMember, mem)) {
80 return;
81 } else {
82 const updatedMember = namedProp({name: existingMember.name.text});
83 // const exists = existingMember.type.types.some(x => x.kind === mem.kind);
84
85 // already a union, so just push a new type
86 if (existingMember.type.kind === ts.SyntaxKind.UnionType) {
87 const asSet = Set(existingMember.type.types.map(x => x.kind));
88 if (!asSet.contains(mem.type.kind)) {
89 existingMember.type.types.push(mem.type);
90 newMembers[existingIndex] = existingMember;
91 }
92 } else { // not a union yet, so create one for next time around
93
94 // was this previously marked as an empty array? eg: any[]
95 // if so & the next item is NOT, then we can ignore the any[]
96 if (isEmptyArrayType(existingMember) && !isEmptyArrayType(mem)) {
97 updatedMember.type = ts.createNode(ts.SyntaxKind.ArrayType);
98 updatedMember.type.elementType = mem.type.elementType;
99 newMembers[existingIndex] = updatedMember;
100 } else {
101 // If the INCOMING member type is an empty array, but we already have an array element with items, we bail
102 if (isEmptyArrayType(mem) && existingMember.type.kind === ts.SyntaxKind.ArrayType && (!isEmptyArrayType(existingMember))) {
103 return;
104 }
105 const memberNodes = [existingMember.type, mem.type];
106 updatedMember.type = ts.createUnionOrIntersectionTypeNode(ts.SyntaxKind.UnionType, memberNodes);
107 newMembers[existingIndex] = updatedMember;
108 }
109 }
110 }
111 // console.log(ts.compareDataObjects(existingMember, mem));
112 }
113 });
114
115 int.members = newMembers;
116 return int;
117 }
118 }
119 return int;
120 });
121 } else {
122 // console.log('Agressive merge here?')
123 }
124
125 return acc.concat(current);
126 }, []);
127}
\No newline at end of file