Skip to content

Commit

Permalink
feat: Add support for generic typing with more than one type
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasdao committed Mar 7, 2018
1 parent 4cfdc56 commit 4d2106e
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 70 deletions.
105 changes: 74 additions & 31 deletions lib/graphqls2s.js
Original file line number Diff line number Diff line change
Expand Up @@ -18838,21 +18838,57 @@ var genericDefaultNameAlias = function genericDefaultNameAlias(genName) {

/**
* Example: [T] -> [User], or T -> User or Toy<T> -> Toy<User>
* @param {string} genericType e.g. Toy<T>
* @param {string} genericLetter e.g. T
* @param {string} concreteType e.g. User
* @return {string} e.g. Toy<User>
* @param {string} genericType e.g. 'Toy<T>', 'Toy<T,U>'
* @param {array} genericLetters e.g. ['T'], ['T','U']
* @param {string} concreteType e.g. 'User', 'User,Product'
* @return {string} e.g. 'Toy<User>', 'Toy<User,Product>'
*/
var replaceGenericWithType = function replaceGenericWithType(genericType, genericLetter, concreteType) {
return chain({ gType: genericType.trim(), gLetter: genericLetter.trim(), cType: concreteType.trim() }).next(function (_ref2) {
var replaceGenericWithType = function replaceGenericWithType(genericType, genericLetters, concreteType) {
return chain({ gType: genericType.trim(), gLetters: genericLetters.map(function (x) {
return x.trim();
}), cTypes: concreteType.split(',').map(function (x) {
return x.trim();
}) }).next(function (_ref2) {
var gType = _ref2.gType,
gLetter = _ref2.gLetter,
cType = _ref2.cType;
return gType == gLetter ? cType : gType.indexOf('[') == 0 && gType.indexOf(']') > 0 ? '[' + gType.match(/\[(.*?)\]/)[1].replace(genericLetter, concreteType) + ']' : gType.indexOf('<') > 0 && gType.indexOf('>') > 0 ? chain(gType.match(/(.*?)<(.*?)>/)).next(function (match) {
return match[1] + '<' + match[2].replace(genericLetter, concreteType) + '>';
}).val() : function () {
throw new Error('Schema error: Cannot replace generic type \'' + gLetter + '\' of generic object \'' + gType + '\' with type \'' + cType + '\'');
}();
gLetters = _ref2.gLetters,
cTypes = _ref2.cTypes;

var cTypesLength = cTypes.length;
var genericTypeIsArray = gType.indexOf('[') == 0 && gType.indexOf(']') > 0;
var endingChar = gType.match(/!$/) ? '!' : '';
if (gLetters.length != cTypesLength) throw new Error('Invalid argument exception. Mismatch between the number of types in \'genericLetters\' (' + genericLetters.join(',') + ') and \'concreteType\' (' + concreteType + ').');
// e.g. genericType = 'T', genericLetters = ['T'], concreteType = 'User' -> resp = 'User'
if (gLetters.length == 1 && gType == gLetters[0]) return '' + cTypes[0] + endingChar;
// e.g. genericType = 'Paged<T>' or '[Paged<T>]'
else if (gType.indexOf('<') > 0 && gType.indexOf('>') > 0) {
var type = genericTypeIsArray ? gType.match(/\[(.*?)\]/)[1] : gType;
var typeName = type.match(/.*</)[0].replace(/<$/, '').trim(); // e.g. 'Toy'
var types = type.match(/<(.*?)>/)[1].split(',').map(function (x) {
return x.trim();
});
if (types.length != gLetters.length) throw new Error('Invalid argument exception. Mismatch between the number of types in \'genericLetters\' (' + genericLetters.join(',') + ') and \'genericType\' (' + genericType + ').');

var matchingConcreteTypes = types.map(function (t) {
for (var i = 0; i < cTypesLength; i++) {
if (gLetters[i] == t) return cTypes[i];
}
throw new Error('Invalid argument exception. Mismatch types between the \'genericType\' (' + genericType + ') and the allowed types \'genericLetters\' (' + genericLetters.join(',') + ').');
});
var result = typeName + '<' + matchingConcreteTypes.join(',') + '>';

return genericTypeIsArray ? '[' + result + ']' + endingChar : '' + result + endingChar;
} else {
// e.g. genericType = 'T' or '[T]'
var _type = genericTypeIsArray ? gType.match(/\[(.*?)\]/)[1] : gType;
var _matchingConcreteTypes = _type.split(',').map(function (t) {
for (var i = 0; i < cTypesLength; i++) {
if (gLetters[i] == t.trim()) return cTypes[i];
}
throw new Error('Invalid argument exception. Mismatch types between the \'genericType\' (' + genericType + ') and the allowed types \'genericLetters\' (' + genericLetters.join(',') + ').');
});
var _result = _matchingConcreteTypes.join(',');
return genericTypeIsArray ? '[' + _result + ']' + endingChar : '' + _result + endingChar;
}
}).val();
};

Expand Down Expand Up @@ -18904,6 +18940,7 @@ var getTypeDetails = function getTypeDetails(t, metadata, genericParentTypes) {
return x.trim();
}) : null;
var originName = t;
var endingChar = t.match(/!$/) ? '!' : '';
var dependsOnParent = isGen && genericParentTypes && genericParentTypes.length > 0 && genericTypes.some(function (x) {
return genericParentTypes.some(function (y) {
return x == y;
Expand All @@ -18915,7 +18952,7 @@ var getTypeDetails = function getTypeDetails(t, metadata, genericParentTypes) {
dependsOnParent: dependsOnParent,
metadata: metadata,
genericParentTypes: genericParentTypes,
name: isGen && !dependsOnParent ? getAliasName(originName, metadata) : t
name: isGen && !dependsOnParent ? '' + getAliasName(originName, metadata) + endingChar : t
};
}).next(function (result) {
if (result.isGen && !memoizedGenericSchemaObjects[result.name]) memoizedGenericSchemaObjects[result.name] = result;
Expand Down Expand Up @@ -19200,7 +19237,7 @@ var addComments = function addComments(obj, comments) {

var parseSchemaObjToString = function parseSchemaObjToString(comments, type, name, _implements, blockProps) {
var extend = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
return ['' + (comments && comments != '' ? '\n' + comments : ''), '' + (extend ? 'extend ' : '') + type.toLowerCase() + ' ' + name + (_implements && _implements.length > 0 ? ' implements ' + _implements.join(', ') : '') + ' ' + (blockProps.some(function (x) {
return ['' + (comments && comments != '' ? '\n' + comments : ''), '' + (extend ? 'extend ' : '') + type.toLowerCase() + ' ' + name.replace('!', '') + (_implements && _implements.length > 0 ? ' implements ' + _implements.join(', ') : '') + ' ' + (blockProps.some(function (x) {
return x;
}) ? '{' : '') + ' ', blockProps.map(function (prop) {
return ' ' + (prop.comments != '' ? prop.comments + '\n ' : '') + prop.value;
Expand All @@ -19214,20 +19251,23 @@ var parseSchemaObjToString = function parseSchemaObjToString(comments, type, nam
/**
* Tests if the type is a generic type based on the value of genericLetter
*
* @param {String} type e.g. Paged<T>, [T]
* @param {String} genericLetter e.g. T
* @param {String} type e.g. 'Paged<T>', '[T]', 'T'
* @param {String} genericLetter e.g. 'T', 'T,U'
* @return {Boolean} e.g. if type equals 'Paged<T>' or '[T]' and genericLetter equals 'T' then true.
*/
var isTypeGeneric = function isTypeGeneric(type, genericLetter) {
var sanitizedType = type ? type.replace(/^\[|\]$/g, '') : type;
if (!sanitizedType || !genericLetter) return false;else if (sanitizedType == genericLetter) return true;else if (sanitizedType.indexOf('<') > 0 && sanitizedType.indexOf('>') > 0) return (sanitizedType.match(/<(.*?)>/) || [null, ''])[1].split(',').some(function (x) {
return x.trim() == genericLetter;
});else return false;
// return !type || !genericLetter ? false :
// type == genericLetter ? true :
// type.indexOf('[') == 0 && type.indexOf(']') > 0 ? _(type.match(/\[(.*?)\]/)[1].split(',')).some(x => x.trim() == genericLetter) :
// type.indexOf('<') > 0 && type.indexOf('>') > 0 ? _(type.match(/<(.*?)>/)[1].split(',')).some(x => x.trim() == genericLetter) :
// false
var sanitizedType = type ? type.replace(/^\[|\s|\]$/g, '') : type;
var sanitizedgenericLetter = genericLetter ? genericLetter.replace(/^\[|\s|\]$/g, '') : genericLetter;
if (!sanitizedType || !sanitizedgenericLetter) return false;else if (sanitizedType == sanitizedgenericLetter) return true;else if (sanitizedType.indexOf('<') > 0 && sanitizedType.indexOf('>') > 0) {
var genericLetters = sanitizedgenericLetter.split(',');
return (sanitizedType.match(/<(.*?)>/) || [null, ''])[1].split(',').some(function (x) {
return genericLetters.some(function (y) {
return y == x.trim();
});
});
} else return sanitizedgenericLetter.split(',').some(function (x) {
return x.trim() == sanitizedType;
});
};

var createNewSchemaObjectFromGeneric = function createNewSchemaObjectFromGeneric(_ref4, schemaBreakDown, memoizedNewSchemaObjectFromGeneric) {
Expand Down Expand Up @@ -19257,14 +19297,17 @@ var createNewSchemaObjectFromGeneric = function createNewSchemaObjectFromGeneric
result: {
originName: prop.details.originName,
isGen: prop.details.isGen,
name: replaceGenericWithType(prop.details.result.name, baseGenObj.genericType, concreteType)
name: replaceGenericWithType(prop.details.result.name, baseGenObj.genericType.split(','), concreteType)
}
};
if (prop.details.result.dependsOnParent) {
var propTypeIsArray = prop.details.result.name.match(/^\[.*\]$/);
var propTypeIsRequired = prop.details.result.name.match(/!$/);
// e.g. [Paged<T>]
var originalConcretePropType = replaceGenericWithType(prop.details.result.name, prop.details.result.genericParentTypes[0], concreteType);
// e.g. Paged<T>
var propTypeName = propTypeIsRequired ? prop.details.result.name.replace(/!$/, '') : prop.details.result.name;
var propTypeIsArray = propTypeName.match(/^\[.*\]$/);
// e.g. [Paged<Product>]
var originalConcretePropType = replaceGenericWithType(propTypeName, prop.details.result.genericParentTypes, concreteType);
// e.g. Paged<Product>
var concretePropType = propTypeIsArray ? originalConcretePropType.replace(/^\[|\]$/g, '') : originalConcretePropType;
var concreteGenProp = getTypeDetails(concretePropType, prop.details.result.metadata);
// e.g. PagedProduct
Expand All @@ -19275,7 +19318,7 @@ var createNewSchemaObjectFromGeneric = function createNewSchemaObjectFromGeneric
details.result = {
originName: prop.details.result.name,
isGen: true,
name: originalConcretePropTypeName
name: originalConcretePropTypeName + (propTypeIsRequired ? '!' : '')
};
}

Expand Down
2 changes: 1 addition & 1 deletion lib/graphqls2s.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/graphqls2s.min.js

Large diffs are not rendered by default.

100 changes: 68 additions & 32 deletions src/graphqls2s.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,19 +139,53 @@ const genericDefaultNameAlias = genName => {

/**
* Example: [T] -> [User], or T -> User or Toy<T> -> Toy<User>
* @param {string} genericType e.g. Toy<T>
* @param {string} genericLetter e.g. T
* @param {string} concreteType e.g. User
* @return {string} e.g. Toy<User>
* @param {string} genericType e.g. 'Toy<T>', 'Toy<T,U>'
* @param {array} genericLetters e.g. ['T'], ['T','U']
* @param {string} concreteType e.g. 'User', 'User,Product'
* @return {string} e.g. 'Toy<User>', 'Toy<User,Product>'
*/
const replaceGenericWithType = (genericType, genericLetter, concreteType) =>
chain({ gType: genericType.trim(), gLetter: genericLetter.trim(), cType: concreteType.trim() })
.next(({ gType, gLetter, cType }) =>
gType == gLetter ? cType :
gType.indexOf('[') == 0 && gType.indexOf(']') > 0 ? `[${gType.match(/\[(.*?)\]/)[1].replace(genericLetter, concreteType)}]` :
gType.indexOf('<') > 0 && gType.indexOf('>') > 0 ? chain(gType.match(/(.*?)<(.*?)>/)).next(match => `${match[1]}<${match[2].replace(genericLetter, concreteType)}>`).val() :
(() => { throw new Error(`Schema error: Cannot replace generic type '${gLetter}' of generic object '${gType}' with type '${cType}'`) })()
)
const replaceGenericWithType = (genericType, genericLetters, concreteType) =>
chain({ gType: genericType.trim(), gLetters: genericLetters.map(x => x.trim()), cTypes: concreteType.split(',').map(x => x.trim()) })
.next(({ gType, gLetters, cTypes }) => {
const cTypesLength = cTypes.length
const genericTypeIsArray = gType.indexOf('[') == 0 && gType.indexOf(']') > 0
const endingChar = gType.match(/!$/) ? '!' : ''
if (gLetters.length != cTypesLength)
throw new Error(`Invalid argument exception. Mismatch between the number of types in 'genericLetters' (${genericLetters.join(',')}) and 'concreteType' (${concreteType}).`)
// e.g. genericType = 'T', genericLetters = ['T'], concreteType = 'User' -> resp = 'User'
if (gLetters.length == 1 && gType == gLetters[0])
return `${cTypes[0]}${endingChar}`
// e.g. genericType = 'Paged<T>' or '[Paged<T>]'
else if (gType.indexOf('<') > 0 && gType.indexOf('>') > 0) {
const type = genericTypeIsArray ? gType.match(/\[(.*?)\]/)[1] : gType
const typeName = type.match(/.*</)[0].replace(/<$/,'').trim() // e.g. 'Toy'
const types = type.match(/<(.*?)>/)[1].split(',').map(x => x.trim())
if (types.length != gLetters.length)
throw new Error(`Invalid argument exception. Mismatch between the number of types in 'genericLetters' (${genericLetters.join(',')}) and 'genericType' (${genericType}).`)

const matchingConcreteTypes = types.map(t => {
for(let i=0;i<cTypesLength;i++) {
if (gLetters[i] == t)
return cTypes[i]
}
throw new Error(`Invalid argument exception. Mismatch types between the 'genericType' (${genericType}) and the allowed types 'genericLetters' (${genericLetters.join(',')}).`)
})
const result = `${typeName}<${matchingConcreteTypes.join(',')}>`

return genericTypeIsArray ? `[${result}]${endingChar}` : `${result}${endingChar}`
} else { // e.g. genericType = 'T' or '[T]'
const type = genericTypeIsArray ? gType.match(/\[(.*?)\]/)[1] : gType
const matchingConcreteTypes = type.split(',').map(t => {
for(let i=0;i<cTypesLength;i++) {
if (gLetters[i] == t.trim())
return cTypes[i]
}
throw new Error(`Invalid argument exception. Mismatch types between the 'genericType' (${genericType}) and the allowed types 'genericLetters' (${genericLetters.join(',')}).`)
})
const result = matchingConcreteTypes.join(',')
return genericTypeIsArray ? `[${result}]${endingChar}` : `${result}${endingChar}`
}
})
.val()

let memoizedGenericNameAliases = {}
Expand Down Expand Up @@ -193,14 +227,15 @@ const getTypeDetails = (t, metadata, genericParentTypes) => chain((t.match(GENER
const isGen = genTypes ? true : false
const genericTypes = isGen ? genTypes.split(',').map(x => x.trim()) : null
const originName = t
const endingChar = t.match(/!$/) ? '!' : ''
const dependsOnParent = isGen && genericParentTypes && genericParentTypes.length > 0 && genericTypes.some(x => genericParentTypes.some(y => x == y))
return {
originName,
isGen,
dependsOnParent,
metadata,
genericParentTypes,
name: isGen && !dependsOnParent ? getAliasName(originName, metadata) : t
name: isGen && !dependsOnParent ? `${getAliasName(originName, metadata)}${endingChar}` : t
}
})
.next(result => {
Expand Down Expand Up @@ -457,33 +492,31 @@ const addComments = (obj, comments) => {
const parseSchemaObjToString = (comments, type, name, _implements, blockProps, extend=false) =>
[
`${comments && comments != '' ? `\n${comments}` : ''}`,
`${extend ? 'extend ' : ''}${type.toLowerCase()} ${name}${_implements && _implements.length > 0 ? ` implements ${_implements.join(', ')}` : ''} ${blockProps.some(x => x) ? '{': ''} `,
`${extend ? 'extend ' : ''}${type.toLowerCase()} ${name.replace('!', '')}${_implements && _implements.length > 0 ? ` implements ${_implements.join(', ')}` : ''} ${blockProps.some(x => x) ? '{': ''} `,
blockProps.map(prop => ` ${prop.comments != '' ? `${prop.comments}\n ` : ''}${prop.value}`).join('\n'),
blockProps.some(x => x) ? '}': ''
].filter(x => x).join('\n')

/**
* Tests if the type is a generic type based on the value of genericLetter
*
* @param {String} type e.g. Paged<T>, [T]
* @param {String} genericLetter e.g. T
* @param {String} type e.g. 'Paged<T>', '[T]', 'T'
* @param {String} genericLetter e.g. 'T', 'T,U'
* @return {Boolean} e.g. if type equals 'Paged<T>' or '[T]' and genericLetter equals 'T' then true.
*/
const isTypeGeneric = (type, genericLetter) => {
const sanitizedType = type ? type.replace(/^\[|\]$/g, '') : type
if (!sanitizedType || !genericLetter)
const sanitizedType = type ? type.replace(/^\[|\s|\]$/g, '') : type
const sanitizedgenericLetter = genericLetter ? genericLetter.replace(/^\[|\s|\]$/g, '') : genericLetter
if (!sanitizedType || !sanitizedgenericLetter)
return false
else if (sanitizedType == genericLetter)
else if (sanitizedType == sanitizedgenericLetter)
return true
else if (sanitizedType.indexOf('<') > 0 && sanitizedType.indexOf('>') > 0)
return (sanitizedType.match(/<(.*?)>/) || [null, ''])[1].split(',').some(x => x.trim() == genericLetter)
else if (sanitizedType.indexOf('<') > 0 && sanitizedType.indexOf('>') > 0) {
const genericLetters = sanitizedgenericLetter.split(',')
return (sanitizedType.match(/<(.*?)>/) || [null, ''])[1].split(',').some(x => genericLetters.some(y => y == x.trim()))
}
else
return false
// return !type || !genericLetter ? false :
// type == genericLetter ? true :
// type.indexOf('[') == 0 && type.indexOf(']') > 0 ? _(type.match(/\[(.*?)\]/)[1].split(',')).some(x => x.trim() == genericLetter) :
// type.indexOf('<') > 0 && type.indexOf('>') > 0 ? _(type.match(/<(.*?)>/)[1].split(',')).some(x => x.trim() == genericLetter) :
// false
return sanitizedgenericLetter.split(',').some(x => x.trim() == sanitizedType)
}

const createNewSchemaObjectFromGeneric = ({ originName, isGen, name }, schemaBreakDown, memoizedNewSchemaObjectFromGeneric) => {
Expand All @@ -508,14 +541,17 @@ const createNewSchemaObjectFromGeneric = ({ originName, isGen, name }, schemaBre
result: {
originName: prop.details.originName,
isGen: prop.details.isGen,
name: replaceGenericWithType(prop.details.result.name, baseGenObj.genericType, concreteType)
name: replaceGenericWithType(prop.details.result.name, baseGenObj.genericType.split(','), concreteType)
}
}
if (prop.details.result.dependsOnParent) {
const propTypeIsArray = prop.details.result.name.match(/^\[.*\]$/)
const propTypeIsRequired = prop.details.result.name.match(/!$/)
// e.g. [Paged<T>]
const originalConcretePropType = replaceGenericWithType(prop.details.result.name, prop.details.result.genericParentTypes[0], concreteType)
// e.g. Paged<T>
const propTypeName = propTypeIsRequired ? prop.details.result.name.replace(/!$/,'') : prop.details.result.name
const propTypeIsArray = propTypeName.match(/^\[.*\]$/)
// e.g. [Paged<Product>]
const originalConcretePropType = replaceGenericWithType(propTypeName, prop.details.result.genericParentTypes, concreteType)
// e.g. Paged<Product>
const concretePropType = propTypeIsArray ? originalConcretePropType.replace(/^\[|\]$/g,'') : originalConcretePropType
const concreteGenProp = getTypeDetails(concretePropType, prop.details.result.metadata)
// e.g. PagedProduct
Expand All @@ -526,7 +562,7 @@ const createNewSchemaObjectFromGeneric = ({ originName, isGen, name }, schemaBre
details.result = {
originName: prop.details.result.name,
isGen: true,
name: originalConcretePropTypeName
name: originalConcretePropTypeName + (propTypeIsRequired ? '!' : '')
}
}

Expand Down
Loading

0 comments on commit 4d2106e

Please sign in to comment.