json-ts
Version:
Automatically generate Typescript Definition files or Flow types from JSON input
121 lines (120 loc) • 6.86 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var ts = require("typescript");
var immutable_1 = require("immutable");
var transformer_1 = require("./transformer");
function membersMatch(first, second) {
if (first.kind !== second.kind) {
return false;
}
if (first.name.text !== second.name.text) {
return false;
}
if (first.type.kind !== second.type.kind) {
return false;
}
if (first.type.kind === ts.SyntaxKind.ArrayType && second.type.kind === ts.SyntaxKind.ArrayType) {
if (first.type.elementType.kind !== second.type.elementType.kind) {
return false;
}
}
return true;
}
function isEmptyArrayType(member) {
if (member.type.kind === ts.SyntaxKind.ArrayType) {
if (member.type.elementType.kind === ts.SyntaxKind.AnyKeyword) {
return true;
}
}
return false;
}
function collapseInterfaces(interfaces) {
return interfaces.reduce(function (acc, current) {
var currentName = current.name.text;
var currentMemberNames = immutable_1.Set(current.members.map(function (x) { return (x.name || x.label).text; }));
var matchingInterfaceIndex = acc.findIndex(function (x) { return (x.name || x.label).text === currentName; });
if (matchingInterfaceIndex > -1) {
return acc
.map(function (int, index) {
if (index === matchingInterfaceIndex) {
var prevMemberNames = immutable_1.Set(int.members.map(function (x) { return (x.name || x.label).text; }));
// if the current interface has less props than a previous one
// we need to back-track and make the previous one optional
if (currentMemberNames.size < prevMemberNames.size) {
// elements that existed before, but not in the current
var missing = int.members.filter(function (x) { return !currentMemberNames.has(x.name.text); });
missing.forEach(function (mem) {
mem.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken);
});
}
if (currentMemberNames.has(int.name.text)) {
console.log('exists in both, maybe union', int.name.text);
}
else {
// console.log('incoming current does not exist in prev');
var existinMemberNames = immutable_1.Set(int.members.map(function (x) { return x.name.text; }));
var newMembers_1 = int.members.slice();
// Loop over incoming current members
current.members.forEach(function (mem) {
var existingIndex = int.members.findIndex(function (x) { return x.name.text === mem.name.text; });
var existingMember = int.members[existingIndex];
// Here, the current member does NOT already exist in this
// interface, so we add it, but as optional
if (!existingMember) {
mem.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken);
newMembers_1.push(mem);
}
else {
// here it exists in both, are the types the same?
// console.log(ts.SyntaxKind[mem.type.kind]);
// console.log(existingMember.kind, mem.kind);
if (membersMatch(existingMember, mem)) {
return;
}
else {
var updatedMember = transformer_1.namedProp({ name: existingMember.name.text });
// const exists = existingMember.type.types.some(x => x.kind === mem.kind);
// already a union, so just push a new type
if (existingMember.type.kind === ts.SyntaxKind.UnionType) {
var asSet = immutable_1.Set(existingMember.type.types.map(function (x) { return x.kind; }));
if (!asSet.contains(mem.type.kind)) {
existingMember.type.types.push(mem.type);
newMembers_1[existingIndex] = existingMember;
}
}
else {
// was this previously marked as an empty array? eg: any[]
// if so & the next item is NOT, then we can ignore the any[]
if (isEmptyArrayType(existingMember) && !isEmptyArrayType(mem)) {
updatedMember.type = ts.createNode(ts.SyntaxKind.ArrayType);
updatedMember.type.elementType = mem.type.elementType;
newMembers_1[existingIndex] = updatedMember;
}
else {
// If the INCOMING member type is an empty array, but we already have an array element with items, we bail
if (isEmptyArrayType(mem) && existingMember.type.kind === ts.SyntaxKind.ArrayType && (!isEmptyArrayType(existingMember))) {
return;
}
var memberNodes = [existingMember.type, mem.type];
updatedMember.type = ts.createUnionOrIntersectionTypeNode(ts.SyntaxKind.UnionType, memberNodes);
newMembers_1[existingIndex] = updatedMember;
}
}
}
// console.log(ts.compareDataObjects(existingMember, mem));
}
});
int.members = newMembers_1;
return int;
}
}
return int;
});
}
else {
// console.log('Agressive merge here?')
}
return acc.concat(current);
}, []);
}
exports.collapseInterfaces = collapseInterfaces;