UNPKG

typescript-swagger

Version:

Generate Swagger files from a decorator library like typescript-rest or a @decorators/express.

979 lines (973 loc) 53.8 kB
"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') {