typescript-swagger
Version:
Generate Swagger files from a decorator library like typescript-rest or a @decorators/express.
979 lines (973 loc) • 53.8 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
to[j] = from[i];
return to;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeNodeResolver = void 0;
var ts = require("typescript");
var utils_1 = require("../../decorator/utils");
var jsDocUtils_1 = require("../../utils/jsDocUtils");
var error_1 = require("./error");
var utils_2 = require("./utils");
var localReferenceTypeCache = {};
var inProgressTypes = {};
function getPropertyValidators(property) {
return {};
}
var TypeNodeResolver = /** @class */ (function () {
function TypeNodeResolver(typeNode, current, parentNode, context, referencer) {
if (context === void 0) { context = {}; }
this.typeNode = typeNode;
this.current = current;
this.parentNode = parentNode;
this.context = context;
this.referencer = referencer;
this.attemptToResolveKindToPrimitive = function (syntaxKind) {
if (syntaxKind === ts.SyntaxKind.NumberKeyword) {
return 'number';
}
else if (syntaxKind === ts.SyntaxKind.StringKeyword) {
return 'string';
}
else if (syntaxKind === ts.SyntaxKind.BooleanKeyword) {
return 'boolean';
}
else if (syntaxKind === ts.SyntaxKind.VoidKeyword) {
return 'void';
}
else {
return undefined;
}
};
}
TypeNodeResolver.clearCache = function () {
Object.keys(localReferenceTypeCache).forEach(function (key) {
delete localReferenceTypeCache[key];
});
Object.keys(inProgressTypes).forEach(function (key) {
delete inProgressTypes[key];
});
};
TypeNodeResolver.prototype.resolve = function () {
var _this = this;
var primitiveType = this.getPrimitiveType(this.typeNode, this.parentNode);
if (primitiveType) {
return primitiveType;
}
if (this.typeNode.kind === ts.SyntaxKind.NullKeyword) {
return {
typeName: 'enum',
members: [null],
};
}
if (this.typeNode.kind === ts.SyntaxKind.ArrayType) {
return {
typeName: 'array',
elementType: new TypeNodeResolver(this.typeNode.elementType, this.current, this.parentNode, this.context).resolve(),
};
}
if (ts.isUnionTypeNode(this.typeNode)) {
var types = this.typeNode.types.map(function (type) {
return new TypeNodeResolver(type, _this.current, _this.parentNode, _this.context).resolve();
});
return {
typeName: 'union',
members: types,
};
}
if (ts.isIntersectionTypeNode(this.typeNode)) {
var types = this.typeNode.types.map(function (type) {
return new TypeNodeResolver(type, _this.current, _this.parentNode, _this.context).resolve();
});
return {
typeName: 'intersection',
members: types,
};
}
if (this.typeNode.kind === ts.SyntaxKind.AnyKeyword || this.typeNode.kind === ts.SyntaxKind.UnknownKeyword || this.typeNode.kind === ts.SyntaxKind.UndefinedKeyword) {
return {
typeName: 'any',
};
}
if (ts.isLiteralTypeNode(this.typeNode)) {
return {
typeName: 'enum',
members: [TypeNodeResolver.getLiteralValue(this.typeNode)],
};
}
if (ts.isTypeLiteralNode(this.typeNode)) {
var properties = this.typeNode.members
.filter(function (member) { return ts.isPropertySignature(member); })
.reduce(function (res, propertySignature) {
var type = new TypeNodeResolver(propertySignature.type, _this.current, propertySignature, _this.context).resolve();
var property = {
example: TypeNodeResolver.getNodeExample(propertySignature),
default: jsDocUtils_1.getJSDocTagComment(propertySignature, 'default'),
description: _this.getNodeDescription(propertySignature),
format: TypeNodeResolver.getNodeFormat(propertySignature),
name: propertySignature.name.text,
required: !propertySignature.questionToken,
type: type,
validators: getPropertyValidators(propertySignature) || {},
};
return __spreadArray([property], res);
}, []);
var indexMember = this.typeNode.members.find(function (member) { return ts.isIndexSignatureDeclaration(member); });
var additionalType = void 0;
if (indexMember) {
var indexSignatureDeclaration = indexMember;
var indexType = new TypeNodeResolver(indexSignatureDeclaration.parameters[0].type, this.current, this.parentNode, this.context).resolve();
if (indexType.typeName !== 'string') {
throw new error_1.ResolverError("Only string indexes are supported.", this.typeNode);
}
additionalType = new TypeNodeResolver(indexSignatureDeclaration.type, this.current, this.parentNode, this.context).resolve();
}
return {
additionalProperties: indexMember && additionalType,
typeName: 'nestedObjectLiteral',
properties: properties,
};
}
if (this.typeNode.kind === ts.SyntaxKind.ObjectKeyword || ts.isFunctionTypeNode(this.typeNode)) {
return { typeName: 'object' };
}
if (ts.isMappedTypeNode(this.typeNode) && this.referencer) {
var type = this.current.typeChecker.getTypeFromTypeNode(this.referencer);
var mappedTypeNode_1 = this.typeNode;
var typeChecker_1 = this.current.typeChecker;
var getDeclaration_1 = function (prop) { return prop.declarations && prop.declarations[0]; };
var isIgnored_1 = function (prop) {
var declaration = getDeclaration_1(prop);
return (prop.getJsDocTags().find(function (tag) { return tag.name === 'ignore'; }) !== undefined ||
(declaration !== undefined && !ts.isPropertyDeclaration(declaration) && !ts.isPropertySignature(declaration) && !ts.isParameter(declaration)));
};
var properties = type
.getProperties()
// Ignore methods, getter, setter and @ignored props
.filter(function (property) { return isIgnored_1(property) === false; })
// Transform to property
.map(function (property) {
var propertyType = typeChecker_1.getTypeOfSymbolAtLocation(property, _this.typeNode);
var declaration = getDeclaration_1(property);
if (declaration && ts.isPropertySignature(declaration)) {
return __assign(__assign({}, _this.propertyFromSignature(declaration, mappedTypeNode_1.questionToken)), { name: property.getName() });
}
else if (declaration && (ts.isPropertyDeclaration(declaration) || ts.isParameter(declaration))) {
return __assign(__assign({}, _this.propertyFromDeclaration(declaration, mappedTypeNode_1.questionToken)), { name: property.getName() });
}
// Resolve default value, required and typeNode
var required = false;
var typeNode = _this.current.typeChecker.typeToTypeNode(propertyType, undefined, ts.NodeBuilderFlags.NoTruncation);
if (mappedTypeNode_1.questionToken && mappedTypeNode_1.questionToken.kind === ts.SyntaxKind.MinusToken) {
required = true;
}
else if (mappedTypeNode_1.questionToken && mappedTypeNode_1.questionToken.kind === ts.SyntaxKind.QuestionToken) {
required = false;
}
// Push property
return {
name: property.getName(),
required: required,
type: new TypeNodeResolver(typeNode, _this.current, _this.typeNode, _this.context, _this.referencer).resolve(),
validators: {},
};
});
return {
typeName: 'nestedObjectLiteral',
properties: properties,
};
}
if (ts.isConditionalTypeNode(this.typeNode) && this.referencer && ts.isTypeReferenceNode(this.referencer)) {
var type_1 = this.current.typeChecker.getTypeFromTypeNode(this.referencer);
if (type_1.aliasSymbol) {
var declaration_1 = type_1.aliasSymbol.declarations[0];
if (declaration_1.name) {
declaration_1 = this.getModelTypeDeclaration(declaration_1.name);
}
var name_1 = TypeNodeResolver.getRefTypeName(this.referencer.getText());
return this.handleCachingAndCircularReferences(name_1, function () {
if (ts.isTypeAliasDeclaration(declaration_1)) {
// Note: I don't understand why typescript lose type for `this.referencer` (from above with isTypeReferenceNode())
return _this.getTypeAliasReference(declaration_1, _this.current.typeChecker.typeToString(type_1), _this.referencer);
}
else if (ts.isEnumDeclaration(declaration_1)) {
return _this.getEnumerateType(declaration_1.name);
}
else {
throw new error_1.ResolverError("Couldn't resolve Conditional to TypeNode. If you think this should be resolvable, please file an Issue. We found an aliasSymbol and it's declaration was of kind " + declaration_1.kind, _this.typeNode);
}
});
}
else if (type_1.isClassOrInterface()) {
var declaration_2 = type_1.symbol.declarations[0];
if (declaration_2.name) {
declaration_2 = this.getModelTypeDeclaration(declaration_2.name);
}
var name_2 = TypeNodeResolver.getRefTypeName(this.referencer.getText());
return this.handleCachingAndCircularReferences(name_2, function () { return _this.getModelReference(declaration_2, _this.current.typeChecker.typeToString(type_1)); });
}
else {
try {
return new TypeNodeResolver(this.current.typeChecker.typeToTypeNode(type_1, undefined, ts.NodeBuilderFlags.NoTruncation), this.current, this.typeNode, this.context, this.referencer).resolve();
}
catch (_a) {
throw new error_1.ResolverError("Couldn't resolve Conditional to TypeNode. If you think this should be resolvable, please file an Issue. The flags on the result of the ConditionalType was " + type_1.flags, this.typeNode);
}
}
}
if (ts.isTypeOperatorNode(this.typeNode) && this.typeNode.operator === ts.SyntaxKind.KeyOfKeyword) {
var type = this.current.typeChecker.getTypeFromTypeNode(this.typeNode);
try {
return new TypeNodeResolver(this.current.typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.NoTruncation), this.current, this.typeNode, this.context, this.referencer).resolve();
}
catch (err) {
var indexedTypeName = this.current.typeChecker.typeToString(this.current.typeChecker.getTypeFromTypeNode(this.typeNode.type));
throw new error_1.ResolverError("Could not determine the keys on " + indexedTypeName, this.typeNode);
}
}
if (ts.isIndexedAccessTypeNode(this.typeNode) && (this.typeNode.indexType.kind === ts.SyntaxKind.NumberKeyword || this.typeNode.indexType.kind === ts.SyntaxKind.StringKeyword)) {
var numberIndexType = this.typeNode.indexType.kind === ts.SyntaxKind.NumberKeyword;
var objectType = this.current.typeChecker.getTypeFromTypeNode(this.typeNode.objectType);
var type = numberIndexType ? objectType.getNumberIndexType() : objectType.getStringIndexType();
if (type === undefined) {
throw new error_1.ResolverError("Could not determine " + (numberIndexType ? 'number' : 'string') + " index on " + this.current.typeChecker.typeToString(objectType), this.typeNode);
}
return new TypeNodeResolver(this.current.typeChecker.typeToTypeNode(type, undefined, undefined), this.current, this.typeNode, this.context, this.referencer).resolve();
}
if (ts.isIndexedAccessTypeNode(this.typeNode) &&
ts.isLiteralTypeNode(this.typeNode.indexType) &&
(ts.isStringLiteral(this.typeNode.indexType.literal) || ts.isNumericLiteral(this.typeNode.indexType.literal))) {
var hasType = function (node) { return node !== undefined && node.hasOwnProperty('type'); };
var symbol = this.current.typeChecker.getPropertyOfType(this.current.typeChecker.getTypeFromTypeNode(this.typeNode.objectType), this.typeNode.indexType.literal.text);
if (symbol === undefined) {
throw new error_1.ResolverError("Could not determine the keys on " + this.current.typeChecker.typeToString(this.current.typeChecker.getTypeFromTypeNode(this.typeNode.objectType)), this.typeNode);
}
if (hasType(symbol.valueDeclaration) && symbol.valueDeclaration.type) {
return new TypeNodeResolver(symbol.valueDeclaration.type, this.current, this.typeNode, this.context, this.referencer).resolve();
}
var declaration = this.current.typeChecker.getTypeOfSymbolAtLocation(symbol, this.typeNode.objectType);
try {
return new TypeNodeResolver(this.current.typeChecker.typeToTypeNode(declaration, undefined, undefined), this.current, this.typeNode, this.context, this.referencer).resolve();
}
catch (_b) {
throw new error_1.ResolverError("Could not determine the keys on " + this.current.typeChecker.typeToString(this.current.typeChecker.getTypeFromTypeNode(this.current.typeChecker.typeToTypeNode(declaration, undefined, undefined))), this.typeNode);
}
}
// @ts-ignore
if (this.typeNode.kind === ts.SyntaxKind.TemplateLiteralType) {
var type = this.current.typeChecker.getTypeFromTypeNode(this.referencer || this.typeNode);
if (type.isUnion() && type.types.every(function (unionElementType) { return unionElementType.isStringLiteral(); })) {
return {
typeName: 'enum',
members: type.types.map(function (stringLiteralType) { return stringLiteralType.value; }),
};
}
else {
throw new error_1.ResolverError("Could not the type of " + this.current.typeChecker.typeToString(this.current.typeChecker.getTypeFromTypeNode(this.typeNode), this.typeNode), this.typeNode);
}
}
if (ts.isParenthesizedTypeNode(this.typeNode)) {
return new TypeNodeResolver(this.typeNode.type, this.current, this.typeNode, this.context, this.referencer).resolve();
}
if (this.typeNode.kind !== ts.SyntaxKind.TypeReference) {
throw new error_1.ResolverError("Unknown type: " + ts.SyntaxKind[this.typeNode.kind], this.typeNode);
}
var typeReference = this.typeNode;
if (typeReference.typeName.kind === ts.SyntaxKind.Identifier) {
// Special Utility Type
if (typeReference.typeName.text === 'Record') {
return {
additionalProperties: new TypeNodeResolver(typeReference.typeArguments[1], this.current, this.parentNode, this.context).resolve(),
typeName: 'nestedObjectLiteral',
properties: [],
};
}
var specialReference = TypeNodeResolver.resolveSpecialReference(typeReference.typeName);
if (typeof specialReference !== 'undefined') {
return specialReference;
}
if (typeReference.typeName.text === 'Date') {
return this.getDateType(this.parentNode);
}
if (typeReference.typeName.text === 'Buffer' || typeReference.typeName.text === 'Readable') {
return { typeName: 'buffer' };
}
if (typeReference.typeName.text === 'Array' && typeReference.typeArguments && typeReference.typeArguments.length >= 1) {
return {
typeName: 'array',
elementType: new TypeNodeResolver(typeReference.typeArguments[0], this.current, this.parentNode, this.context).resolve(),
};
}
if (typeReference.typeName.text === 'Promise' && typeReference.typeArguments && typeReference.typeArguments.length === 1) {
return new TypeNodeResolver(typeReference.typeArguments[0], this.current, this.parentNode, this.context).resolve();
}
if (typeReference.typeName.text === 'String') {
return { typeName: 'string' };
}
if (this.context[typeReference.typeName.text]) {
return new TypeNodeResolver(this.context[typeReference.typeName.text], this.current, this.parentNode, this.context).resolve();
}
}
var referenceType = this.getReferenceType(typeReference);
this.current.addReferenceType(referenceType);
return referenceType;
};
// ------------------------------------------------------------------------
// Utility Type(s)
// ------------------------------------------------------------------------
TypeNodeResolver.isSupportedUtilityType = function (typeName) {
if (typeof typeName === 'undefined') {
return false;
}
return ['NonNullable', 'Pick', 'Omit', 'Partial', 'Readonly', 'Record', 'Required'].indexOf(typeof typeName !== 'string' ? typeName.text : typeName) !== -1;
};
TypeNodeResolver.getUtilityTypeOptions = function (typeArguments) {
var utilityOptions = {
keys: []
};
if (typeArguments.length >= 2) {
if (ts.isUnionTypeNode(typeArguments[1])) {
var args = typeArguments[1].types;
for (var i = 0; i < args.length; i++) {
if (ts.isLiteralTypeNode(args[i])) {
utilityOptions['keys'].push(TypeNodeResolver.getLiteralValue(args[i]));
}
}
}
if (ts.isLiteralTypeNode(typeArguments[1])) {
utilityOptions['keys'].push(TypeNodeResolver.getLiteralValue(typeArguments[1]));
}
}
return utilityOptions;
};
TypeNodeResolver.prototype.filterUtilityProperties = function (properties, utilityType, utilityOptions) {
if (typeof utilityType === 'undefined' || typeof utilityOptions === 'undefined') {
return properties;
}
return properties
.filter(function (property) {
var name = typeof property.name !== 'string' ? property.name.text : property.name;
switch (utilityType) {
case 'Pick':
return utilityOptions.keys.indexOf(name) !== -1;
case 'Omit':
return utilityOptions.keys.indexOf(name) === -1;
}
return true;
})
.map(function (property) {
if (utils_2.hasOwnProperty(property, 'required')) {
switch (utilityType) {
case 'Partial':
property.required = false;
break;
case 'Required':
case 'NonNullable':
property.required = true;
break;
}
}
return property;
});
};
TypeNodeResolver.resolveSpecialReference = function (node) {
switch (node.text) {
case 'Buffer':
case 'DownloadBinaryData':
case 'DownloadResource':
return { typeName: 'buffer' };
default:
return undefined;
}
};
TypeNodeResolver.getLiteralValue = function (typeNode) {
var value;
switch (typeNode.literal.kind) {
case ts.SyntaxKind.TrueKeyword:
value = true;
break;
case ts.SyntaxKind.FalseKeyword:
value = false;
break;
case ts.SyntaxKind.StringLiteral:
value = typeNode.literal.text;
break;
case ts.SyntaxKind.NumericLiteral:
value = parseFloat(typeNode.literal.text);
break;
case ts.SyntaxKind.NullKeyword:
value = null;
break;
default:
if (typeNode.literal.hasOwnProperty('text')) {
value = typeNode.literal.text;
}
else {
throw new error_1.ResolverError("Couldn't resolve literal node: " + typeNode.literal.getText());
}
}
return value;
};
TypeNodeResolver.prototype.getPrimitiveType = function (typeNode, parentNode) {
if (!typeNode) {
return {
typeName: 'void',
};
}
var resolvedType = this.attemptToResolveKindToPrimitive(typeNode.kind);
if (typeof resolvedType === 'undefined') {
return undefined;
}
if (resolvedType === 'number') {
if (!parentNode) {
return { typeName: 'double' };
}
var tags = jsDocUtils_1.getJSDocTagNames(parentNode)
.filter(function (name) {
return [
'isInt',
'isLong',
'isFloat',
'isDouble'
].some(function (m) { return m.toLowerCase() === name.toLowerCase(); });
})
.map(function (name) { return name.toLowerCase(); });
var decorator = utils_1.getDecoratorName(parentNode, function (identifier) { return [
'isInt',
'isLong',
'isFloat',
'isDouble'
].some(function (m) { return m.toLowerCase() === identifier.text.toLowerCase(); }); });
if (typeof decorator !== 'undefined') {
decorator = decorator.toLowerCase();
}
if (!decorator && tags.length === 0) {
return { typeName: 'double' };
}
switch (decorator || tags[0]) {
case 'isint':
return { typeName: 'integer' };
case 'islong':
return { typeName: 'long' };
case 'isfloat':
return { typeName: 'float' };
case 'isdouble':
return { typeName: 'double' };
default:
return { typeName: 'double' };
}
}
else if (resolvedType === 'string') {
return {
typeName: 'string',
};
}
else if (resolvedType === 'boolean') {
return {
typeName: 'boolean',
};
}
else if (resolvedType === 'void') {
return {
typeName: 'void',
};
}
else {
// todo: should not occur
return resolvedType;
}
};
TypeNodeResolver.prototype.getDateType = function (parentNode) {
if (!parentNode) {
return { typeName: 'datetime' };
}
var tags = jsDocUtils_1.getJSDocTagNames(parentNode).filter(function (name) {
return ['isDate', 'isDateTime'].some(function (m) { return m === name; });
});
if (tags.length === 0) {
return { typeName: 'datetime' };
}
switch (tags[0]) {
case 'isDate':
return { typeName: 'date' };
case 'isDateTime':
return { typeName: 'datetime' };
default:
return { typeName: 'datetime' };
}
};
TypeNodeResolver.getDesignatedModels = function (nodes, typeName) {
return nodes;
};
TypeNodeResolver.prototype.getEnumerateType = function (typeName) {
var enumName = typeName.text;
var enumNodes = this.current.nodes.filter(function (node) { return node.kind === ts.SyntaxKind.EnumDeclaration; }).filter(function (node) { return node.name.text === enumName; });
if (!enumNodes.length) {
return undefined;
}
enumNodes = TypeNodeResolver.getDesignatedModels(enumNodes, enumName);
if (enumNodes.length > 1) {
throw new error_1.ResolverError("Multiple matching enum found for enum " + enumName + "; please make enum names unique.");
}
var enumDeclaration = enumNodes[0];
var isNotUndefined = function (item) {
return item !== undefined;
};
var enums = enumDeclaration.members.map(this.current.typeChecker.getConstantValue.bind(this.current.typeChecker)).filter(isNotUndefined);
var enumNames = enumDeclaration.members.map(function (e) { return e.name.getText(); }).filter(isNotUndefined);
return {
typeName: 'refEnum',
description: this.getNodeDescription(enumDeclaration),
members: enums,
memberNames: enumNames,
refName: enumName,
};
};
TypeNodeResolver.prototype.getReferenceType = function (node) {
var _this = this;
var type;
if (ts.isTypeReferenceNode(node)) {
type = node.typeName;
}
else if (ts.isExpressionWithTypeArguments(node)) {
type = node.expression;
}
else {
throw new error_1.ResolverError("Can't resolve Reference type.");
}
// Can't invoke getText on Synthetic Nodes
var resolvableName = node.pos !== -1 ? node.getText() : type.text;
if (node.pos === -1 && 'typeArguments' in node && Array.isArray(node.typeArguments)) {
// Add typeArguments for Synthetic nodes (e.g. Record<> in TestClassModel.indexedResponse)
var argumentsString = node.typeArguments
.map(function (arg) {
if (ts.isLiteralTypeNode(arg)) {
return "'" + String(TypeNodeResolver.getLiteralValue(arg)) + "'";
}
var resolvedType = _this.attemptToResolveKindToPrimitive(arg.kind);
if (typeof resolvedType === 'undefined') {
return 'any';
}
return resolvedType;
});
resolvableName += "<" + argumentsString.join(', ') + ">";
}
var name = this.contextualizedName(resolvableName);
// Handle Utility Types
var identifierName = type.text;
var utilityTypeSupported = TypeNodeResolver.isSupportedUtilityType(identifierName);
var utilityType = utilityTypeSupported ? identifierName : undefined;
var utilityOptions = {
keys: []
};
if (utilityTypeSupported) {
var typeArguments = type.parent.typeArguments;
if (ts.isTypeReferenceNode(typeArguments[0])) {
type = typeArguments[0].typeName;
}
else if (ts.isExpressionWithTypeArguments(typeArguments[0])) {
type = typeArguments[0].expression;
}
else {
throw new error_1.ResolverError("Can't resolve Reference type.");
}
utilityOptions = TypeNodeResolver.getUtilityTypeOptions(typeArguments);
}
else {
this.typeArgumentsToContext(node, type, this.context);
}
try {
var existingType = localReferenceTypeCache[name];
if (existingType) {
return existingType;
}
var refEnumType = this.getEnumerateType(type);
if (refEnumType) {
localReferenceTypeCache[name] = refEnumType;
return refEnumType;
}
if (inProgressTypes[name]) {
return this.createCircularDependencyResolver(name);
}
inProgressTypes[name] = true;
var declaration = this.getModelTypeDeclaration(type);
var referenceType = void 0;
if (ts.isTypeAliasDeclaration(declaration)) {
referenceType = this.getTypeAliasReference(declaration, name, node);
}
else if (ts.isEnumMember(declaration)) {
referenceType = {
typeName: 'refEnum',
refName: TypeNodeResolver.getRefTypeName(name, utilityType),
members: [this.current.typeChecker.getConstantValue(declaration)],
// @ts-ignore
memberNames: [declaration.name.getText()],
};
}
else {
referenceType = this.getModelReference(declaration, name, utilityType, utilityOptions);
}
localReferenceTypeCache[name] = referenceType;
return referenceType;
}
catch (err) {
// eslint-disable-next-line no-console
console.error("There was a problem resolving type of '" + name + "'.");
throw err;
}
};
TypeNodeResolver.prototype.getTypeAliasReference = function (declaration, name, referencer) {
var refName = TypeNodeResolver.getRefTypeName(name);
var type;
if (declaration.type.kind === ts.SyntaxKind.TypeReference) {
var referenceType = this.getReferenceType(declaration.type);
if (referenceType.refName === refName) {
return referenceType;
}
}
if (typeof type === 'undefined') {
type = new TypeNodeResolver(declaration.type, this.current, declaration, this.context, this.referencer || referencer).resolve();
}
var example = TypeNodeResolver.getNodeExample(declaration);
return __assign({ typeName: 'refAlias', default: jsDocUtils_1.getJSDocTagComment(declaration, 'default'), description: this.getNodeDescription(declaration), refName: refName, format: TypeNodeResolver.getNodeFormat(declaration), type: type, validators: getPropertyValidators(declaration) || {} }, (example && { example: example }));
};
TypeNodeResolver.prototype.getModelReference = function (modelType, name, utilityType, utilityOptions) {
var example = TypeNodeResolver.getNodeExample(modelType);
var description = this.getNodeDescription(modelType);
// Handle toJSON methods
if (!modelType.name) {
throw new error_1.ResolverError("Can't get Symbol from anonymous class", modelType);
}
var type = this.current.typeChecker.getTypeAtLocation(modelType.name);
var toJSON = this.current.typeChecker.getPropertyOfType(type, 'toJSON');
if (toJSON && toJSON.valueDeclaration && (ts.isMethodDeclaration(toJSON.valueDeclaration) || ts.isMethodSignature(toJSON.valueDeclaration))) {
var nodeType = toJSON.valueDeclaration.type;
if (!nodeType) {
var signature = this.current.typeChecker.getSignatureFromDeclaration(toJSON.valueDeclaration);
var implicitType = this.current.typeChecker.getReturnTypeOfSignature(signature);
nodeType = this.current.typeChecker.typeToTypeNode(implicitType, undefined, ts.NodeBuilderFlags.NoTruncation);
}
return __assign({ refName: TypeNodeResolver.getRefTypeName(name, utilityType) + 'Alias', typeName: 'refAlias', description: description, type: new TypeNodeResolver(nodeType, this.current).resolve(), validators: {} }, (example && { example: example }));
}
var properties = this.getModelProperties(modelType, undefined, utilityType, utilityOptions);
var additionalProperties = this.getModelAdditionalProperties(modelType);
var inheritedProperties = this.getModelInheritedProperties(modelType) || [];
var referenceType = __assign({ additionalProperties: additionalProperties, typeName: 'refObject', description: description, properties: this.filterUtilityProperties(inheritedProperties, utilityType, utilityOptions), refName: TypeNodeResolver.getRefTypeName(name, utilityType) }, (example && { example: example }));
referenceType.properties = referenceType.properties.concat(properties);
return referenceType;
};
TypeNodeResolver.getRefTypeName = function (name, utilityType) {
return encodeURIComponent(name
.replace(/<|>/g, '_')
.replace(/\s+/g, '')
.replace(/,/g, '.')
.replace(/\'([^']*)\'/g, '$1')
.replace(/\"([^"]*)\"/g, '$1')
.replace(/&/g, typeof utilityType !== 'undefined' ? '--' : '-and-')
.replace(/\|/g, typeof utilityType !== 'undefined' ? '--' : '-or-')
.replace(/\[\]/g, '-array')
.replace(/{|}/g, '_') // SuccessResponse_{indexesCreated-number}_ -> SuccessResponse__indexesCreated-number__
.replace(/([a-z]+):([a-z]+)/gi, '$1-$2') // SuccessResponse_indexesCreated:number_ -> SuccessResponse_indexesCreated-number_
.replace(/;/g, '--')
.replace(/([a-z]+)\[([a-z]+)\]/gi, '$1-at-$2') // Partial_SerializedDatasourceWithVersion[format]_ -> Partial_SerializedDatasourceWithVersion~format~_,
.replace(/_/g, '')
.replace(/-/g, ''));
};
TypeNodeResolver.prototype.contextualizedName = function (name) {
return Object.entries(this.context).reduce(function (acc, _a) {
var key = _a[0], entry = _a[1];
return acc
.replace(new RegExp("<\\s*([^>]*\\s)*\\s*(" + key + ")(\\s[^>]*)*\\s*>", 'g'), "<$1" + entry.getText() + "$3>")
.replace(new RegExp("<\\s*([^,]*\\s)*\\s*(" + key + ")(\\s[^,]*)*\\s*,", 'g'), "<$1" + entry.getText() + "$3,")
.replace(new RegExp(",\\s*([^>]*\\s)*\\s*(" + key + ")(\\s[^>]*)*\\s*>", 'g'), ",$1" + entry.getText() + "$3>")
.replace(new RegExp("<\\s*([^<]*\\s)*\\s*(" + key + ")(\\s[^<]*)*\\s*<", 'g'), "<$1" + entry.getText() + "$3<");
}, name);
};
TypeNodeResolver.prototype.handleCachingAndCircularReferences = function (name, declarationResolver) {
try {
var existingType = localReferenceTypeCache[name];
if (existingType) {
return existingType;
}
if (inProgressTypes[name]) {
return this.createCircularDependencyResolver(name);
}
inProgressTypes[name] = true;
var reference = declarationResolver();
localReferenceTypeCache[name] = reference;
this.current.addReferenceType(reference);
return reference;
}
catch (err) {
// eslint-disable-next-line no-console
console.error("There was a problem resolving type of '" + name + "'.");
throw err;
}
};
TypeNodeResolver.prototype.createCircularDependencyResolver = function (refName) {
var referenceType = {
typeName: 'refObject',
refName: refName,
};
this.current.onFinish(function (referenceTypes) {
var realReferenceType = referenceTypes[refName];
if (!realReferenceType) {
return;
}
referenceType.description = realReferenceType.description;
if (realReferenceType.typeName === 'refObject' && referenceType.typeName === 'refObject') {
referenceType.properties = realReferenceType.properties;
}
referenceType.typeName = realReferenceType.typeName;
referenceType.refName = realReferenceType.refName;
});
return referenceType;
};
TypeNodeResolver.nodeIsUsable = function (node) {
switch (node.kind) {
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.TypeAliasDeclaration:
case ts.SyntaxKind.EnumDeclaration:
case ts.SyntaxKind.EnumMember:
return true;
default:
return false;
}
};
// @ts-ignore
TypeNodeResolver.resolveLeftmostIdentifier = function (type) {
while (type.kind !== ts.SyntaxKind.Identifier) {
type = type.left;
}
return type;
};
// @ts-ignore
TypeNodeResolver.resolveRightMostIdentifier = function (type) {
while (type.kind !== ts.SyntaxKind.Identifier) {
type = type.right;
}
return type;
};
TypeNodeResolver.prototype.resolveModelTypeScope = function (leftmost, statements) {
/*
while (leftmost.parent && leftmost.parent.kind === ts.SyntaxKind.QualifiedName) {
const leftmostName = leftmost.kind === ts.SyntaxKind.Identifier ? leftmost.text : leftmost.right.text;
const moduleDeclarations = statements.filter((node: ts.Node) => {
if ((node.kind !== ts.SyntaxKind.ModuleDeclaration || !this.current.isExportedNode(node)) && !ts.isEnumDeclaration(node)) {
return false;
}
const moduleDeclaration = node as ts.ModuleDeclaration | ts.EnumDeclaration;
return (moduleDeclaration.name as ts.Identifier).text.toLowerCase() === leftmostName.toLowerCase();
}) as Array<ts.ModuleDeclaration | ts.EnumDeclaration>;
if (!moduleDeclarations.length) {
throw new ResolverError(`No matching module declarations found for ${leftmostName}.`);
}
statements = Array.prototype.concat(
...moduleDeclarations.map(declaration => {
if (ts.isEnumDeclaration(declaration)) {
return declaration.members;
} else {
if (!declaration.body || !ts.isModuleBlock(declaration.body)) {
throw new ResolverError(`Module declaration found for ${leftmostName} has no body.`);
}
return declaration.body.statements;
}
}),
);
leftmost = leftmost.parent as ts.EntityName;
}
*/
return statements;
};
TypeNodeResolver.prototype.getModelTypeDeclaration = function (type) {
var _this = this;
var leftmostIdentifier = TypeNodeResolver.resolveLeftmostIdentifier(type);
var statements = this.resolveModelTypeScope(leftmostIdentifier, this.current.nodes);
var typeName = type.kind === ts.SyntaxKind.Identifier ? type.text : type.right.text;
var modelTypes = statements.filter(function (node) {
var _a;
if (!TypeNodeResolver.nodeIsUsable(node) || !_this.current.isExportedNode(node)) {
return false;
}
var modelTypeDeclaration = node;
return ((_a = modelTypeDeclaration.name) === null || _a === void 0 ? void 0 : _a.text) === typeName;
});
if (!modelTypes.length) {
throw new error_1.ResolverError("No matching model found for referenced type " + typeName + ". If " + typeName + " comes from a dependency, please create an interface in your own code that has the same structure. The compiler can not utilize interfaces from external dependencies.");
}
if (modelTypes.length > 1) {
// remove types that are from typescript e.g. 'Account'
modelTypes = modelTypes.filter(function (modelType) {
return modelType.getSourceFile().fileName.replace(/\\/g, '/').toLowerCase().indexOf('node_modules') <= -1;
});
modelTypes = TypeNodeResolver.getDesignatedModels(modelTypes, typeName);
}
if (modelTypes.length > 1) {
var conflicts = modelTypes.map(function (modelType) { return modelType.getSourceFile().fileName; }).join('"; "');
throw new error_1.ResolverError("Multiple matching models found for referenced type " + typeName + "; please make model names unique. Conflicts found: \"" + conflicts + "\".");
}
return modelTypes[0];
};
TypeNodeResolver.prototype.getModelProperties = function (node, overrideToken, utilityType, utilityOptions) {
var _this = this;
var isIgnored = function (e) {
return jsDocUtils_1.isExistJSDocTag(e, function (tag) { return tag.tagName.text === 'ignore'; });
};
// Interface model
if (ts.isInterfaceDeclaration(node)) {
return node.members.filter(function (member) { return !isIgnored(member) && ts.isPropertySignature(member); }).map(function (member) { return _this.propertyFromSignature(member, overrideToken); });
}
// Class model
var properties = node.members
.filter(function (member) { return !isIgnored(member); })
.filter(function (member) { return member.kind === ts.SyntaxKind.PropertyDeclaration; })
.filter(function (member) { return !_this.hasStaticModifier(member); })
.filter(function (member) { return _this.hasPublicModifier(member); });
var classConstructor = node.members.find(function (member) { return ts.isConstructorDeclaration(member); });
if (classConstructor && classConstructor.parameters) {
var constructorProperties = classConstructor.parameters.filter(function (parameter) { return _this.isAccessibleParameter(parameter); });
properties.push.apply(properties, constructorProperties);
}
properties = this.filterUtilityProperties(properties, utilityType, utilityOptions);
return properties.map(function (property) { return _this.propertyFromDeclaration(property, overrideToken, utilityType); });
};
TypeNodeResolver.prototype.propertyFromSignature = function (propertySignature, overrideToken) {
var identifier = propertySignature.name;
if (!propertySignature.type) {
throw new error_1.ResolverError("No valid type found for property declaration.");
}
var required = !propertySignature.questionToken;
if (overrideToken && overrideToken.kind === ts.SyntaxKind.MinusToken) {
required = true;
}
else if (overrideToken && overrideToken.kind === ts.SyntaxKind.QuestionToken) {
required = false;
}
var property = {
default: jsDocUtils_1.getJSDocTagComment(propertySignature, 'default'),
description: this.getNodeDescription(propertySignature),
example: TypeNodeResolver.getNodeExample(propertySignature),
format: TypeNodeResolver.getNodeFormat(propertySignature),
name: identifier.text,
required: required,
type: new TypeNodeResolver(propertySignature.type, this.current, propertySignature.type.parent, this.context, propertySignature.type).resolve(),
validators: getPropertyValidators(propertySignature) || {},
};
return property;
};
TypeNodeResolver.prototype.propertyFromDeclaration = function (propertyDeclaration, overrideToken, utilityType) {
var identifier = propertyDeclaration.name;
var typeNode = propertyDeclaration.type;
if (!typeNode) {
var tsType = this.current.typeChecker.getTypeAtLocation(propertyDeclaration);
typeNode = this.current.typeChecker.typeToTypeNode(tsType, undefined, ts.NodeBuilderFlags.NoTruncation);
}
if (!typeNode) {
throw new error_1.ResolverError("No valid type found for property declaration.");
}
var type = new TypeNodeResolver(typeNode, this.current, propertyDeclaration, this.context, typeNode).resolve();
var required = !propertyDeclaration.questionToken && !propertyDeclaration.initializer;
if (overrideToken && overrideToken.kind === ts.SyntaxKind.MinusToken) {
required = true;
}
else if (overrideToken && overrideToken.kind === ts.SyntaxKind.QuestionToken) {
required = false;
}
if (typeof utilityType !== 'undefined') {
if (utilityType === 'Partial') {
required = false;
}
if (utilityType === 'Required') {
required = true;
}
}
var property = {
default: utils_2.getInitializerValue(propertyDeclaration.initializer, this.current.typeChecker),
description: this.getNodeDescription(propertyDeclaration),
example: TypeNodeResolver.getNodeExample(propertyDeclaration),
format: TypeNodeResolver.getNodeFormat(propertyDeclaration),
name: identifier.text,
required: required,
type: type,
validators: getPropertyValidators(propertyDeclaration) || {},
};
return property;
};
TypeNodeResolver.prototype.getModelAdditionalProperties = function (node) {
if (node.kind === ts.SyntaxKind.InterfaceDeclaration) {
var indexMember = node.members.find(function (member) { return member.kind === ts.SyntaxKind.IndexSignature; });
if (!indexMember) {
return undefined;
}
var indexSignatureDeclaration = indexMember;
var indexType = new TypeNodeResolver(indexSignatureDeclaration.parameters[0].type, this.current, this.parentNode, this.context).resolve();
if (indexType.typeName !== 'string') {
throw new error_1.ResolverError("Only string indexers are supported.", this.typeNode);
}
return new TypeNodeResolver(indexSignatureDeclaration.type, this.current, this.parentNode, this.context).resolve();
}
return undefined;
};
TypeNodeResolver.prototype.typeArgumentsToContext = function (type, targetEntity, context) {
// this.context = {};
var _a;
var declaration = this.getModelTypeDeclaration(targetEntity);
var typeParameters = 'typeParameters' in declaration ? declaration.typeParameters : undefined;
if (typeParameters) {
for (var index = 0; index < typeParameters.length; index++) {
var typeParameter = typeParameters[index];
var typeArg = type.typeArguments && type.typeArguments[index];
var resolvedType = void 0;
// Argument may be a forward reference from context
if (typeArg && ts.isTypeReferenceNode(typeArg) && ts.isIdentifier(typeArg.typeName) && context[typeArg.typeName.text]) {
resolvedType = context[typeArg.typeName.text];
}
else if (typeArg) {
resolvedType = typeArg;
}
else if (typeParameter.default) {
resolvedType = typeParameter.default;
}
else {
throw new error_1.ResolverError("Could not find a value for type parameter " + typeParameter.name.text, type);
}
this.context = __assign(__assign({}, this.context), (_a = {}, _a[typeParameter.name.text] = resolvedType, _a));
}
}
return context;
};
TypeNodeResolver.prototype.getModelInheritedProperties = function (modelTypeDeclaration) {
var _this = this;
var properties = [];
var heritageClauses = modelTypeDeclaration.heritageClauses;
if (!heritageClauses) {
return properties;
}
heritageClauses.forEach(function (clause) {
if (!clause.types) {
return;
}
clause.types.forEach(function (t) {
var baseEntityName = t.expression;
// create subContext
var resetCtx = _this.typeArgumentsToContext(t, baseEntityName, _this.context);
var referenceType = _this.getReferenceType(t);
if (referenceType) {
if (referenceType.typeName === 'refEnum') {
// since it doesn't have properties to iterate over, then we don't do anything with it
}
else if (referenceType.typeName === 'refAlias') {