UNPKG

@otris/jsdoc-tsd

Version:

JSDoc Template for generate typescript definition files from JSDoc comments

1,051 lines (1,050 loc) 54.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.JSDocTsdParser = void 0; var comment_parser_1 = require("comment-parser"); var dom = __importStar(require("dts-dom")); var dts_dom_1 = require("dts-dom"); var fs_1 = require("fs"); var Configuration_1 = require("./Configuration"); var Logger_1 = require("./Logger"); var JSDocTsdParser = /** @class */ (function () { function JSDocTsdParser(config) { this._parsedClassess = null; /** * Maps the access flags from JSDoc to declaration flags of dts-dom */ this.accessFlagMap = { private: dom.DeclarationFlags.Private, protected: dom.DeclarationFlags.Protected, public: dom.DeclarationFlags.None, }; this.parsedItems = new Map(); this.jsdocItems = []; this.config = config || new Configuration_1.Configuration(); } /** * Creates the type definition file as string * @param targetPath If passed, the output will be written to the passed file path */ JSDocTsdParser.prototype.generateTypeDefinition = function (targetPath) { var e_1, _a; var output = ""; var results = this.resolveMembershipAndExtends(); var _loop_1 = function (longname, item) { try { output += dom.emit(item); } catch (err) { /* istanbul ignore next */ Logger_1.Logger.log("Unexpected error. Please report this error on github!\nCan't emit item ".concat(longname, ": ").concat(err, "\n\n").concat(JSON.stringify(item, null, "\t")), console.error); /* istanbul ignore next */ var jsdocItems = this_1.jsdocItems.filter(function (elem) { return (elem.hasOwnProperty("name") && elem.name.endsWith(longname)) || (elem.hasOwnProperty("longname") && elem.longname === longname); }); /* istanbul ignore next */ Logger_1.Logger.log("JSDoc items: \n".concat(JSON.stringify(jsdocItems, null, "\t"))); } }; var this_1 = this; try { for (var _b = __values(results.entries()), _c = _b.next(); !_c.done; _c = _b.next()) { var _d = __read(_c.value, 2), longname = _d[0], item = _d[1]; _loop_1(longname, item); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } if (targetPath) { (0, fs_1.writeFileSync)(targetPath, output); } return output; }; /** * Returns the parsed Declaration Base of an jsdoc item * @param name The longname of the jsdoc item (name including membership, e.g. "myNamespace.myMember") * @throws {Error} When no item with this name was parsed */ JSDocTsdParser.prototype.getParsedItem = function (name) { var item = this.parsedItems.get(name); if (item) { return item.map(function (i) { return i.parsed; }); } else { throw new Error("Item with name '".concat(name, "' not found in result items")); } }; /** * Returns all parsed Declaration Bases */ JSDocTsdParser.prototype.getParsedItems = function () { var entries = __spreadArray([], __read(this.parsedItems.entries()), false); var newEntries = entries.map(function (entry) { return [entry[0], entry[1].map(function (i) { return i.parsed; })]; }); // @ts-ignore return new Map(newEntries); }; JSDocTsdParser.prototype.parse = function (jsdocItems) { var e_2, _a; this.jsdocItems = []; try { for (var jsdocItems_1 = __values(jsdocItems), jsdocItems_1_1 = jsdocItems_1.next(); !jsdocItems_1_1.done; jsdocItems_1_1 = jsdocItems_1.next()) { var item = jsdocItems_1_1.value; // Ignore inherited items // JDoc will duplicate inherited items. If we don't ignore them, // inherited items will also be duplicated in the output if (this.evaluateSinceTag(item) && !item.ignore && (!item.undocumented || !this.config.skipUndocumented) && !item.inherited && !this.config.ignoreScope(item.scope) && (!item.comment || !(item.comment.match("@type ") && item.scope === "inner"))) { var parsedItems = this.parsedItems.get(item.longname) || []; if (parsedItems.length === 0) { this.jsdocItems.push(item); } var parsedItem = this.parseJSDocItem(item); if (parsedItem) { if (item.kind !== "class") { // @ts-ignore parsedItem.jsDocComment = this.cleanJSDocComment(item.comment); } if ("flags" in parsedItem) { this.handleFlags(item, parsedItem); } this.handleTags(item, parsedItem); parsedItems.push({ longname: item.longname, memberof: item.memberof, original: item, parsed: parsedItem, }); this.parsedItems.set(item.longname, parsedItems); } } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (jsdocItems_1_1 && !jsdocItems_1_1.done && (_a = jsdocItems_1.return)) _a.call(jsdocItems_1); } finally { if (e_2) throw e_2.error; } } }; /** * Resolves the membership of all parsed items. For example a namspace member will be * added to the member-property of the parsed namespace, if the namespace was parsed. * Otherwise the member will be added to the top level declaration. * @returns Map with the top level declarations and resolved memberships. The key is the * long name of the item, the value is the @see {dom.TopLevelDeclaration} */ JSDocTsdParser.prototype.resolveMembershipAndExtends = function () { var e_3, _a, e_4, _b; if (this.resolvedItems) { return this.resolvedItems; } else { var domTopLevelDeclarations = new Map(); try { for (var _c = __values(this.parsedItems.values()), _d = _c.next(); !_d.done; _d = _c.next()) { var parsedItems = _d.value; try { for (var parsedItems_1 = (e_4 = void 0, __values(parsedItems)), parsedItems_1_1 = parsedItems_1.next(); !parsedItems_1_1.done; parsedItems_1_1 = parsedItems_1.next()) { var parsedItem = parsedItems_1_1.value; if (parsedItem.original.kind === "class" && parsedItem.original.augments) { var parentItem = this.findClassParent(parsedItem.original); if (parentItem) { var classItem = parsedItem.parsed; classItem.baseType = parentItem; } } if (parsedItem.memberof) { // @todo Do not pass the domTopLevelDeclarations but the parsedItems map. // Maybe the parent item was not processed yet, then it will not be // found var parentItem = this.findParentItem(parsedItem.memberof, domTopLevelDeclarations); if (parentItem) { // add the items we parsed before as a member of the top level declaration var dtsItem = parsedItem.parsed; var kind = parentItem.kind; switch (kind) { case "namespace": this.resolveNamespaceMembership(dtsItem, parentItem); break; case "class": this.resolveClassMembership(dtsItem, parentItem); break; case "enum": this.resolveEnumMembership(dtsItem, parentItem); break; case "interface": this.resolveInterfaceMembership(dtsItem, parentItem); break; case "module": this.resolveModuleMembership(dtsItem, parentItem); break; /* istanbul ignore next */ default: // parent type not supported Logger_1.Logger.log("Can't add member '".concat(parsedItem.longname, "' to parent item '").concat(parentItem.name, "'. Unsupported parent member type: '").concat(kind, "'.")); break; } } else { Logger_1.Logger.log("Missing top level declaration '" + parsedItem.memberof + "' for member '" + parsedItem.longname + "'.", console.warn); } } else { // member has no parent, add the item as top-level declaration if (!domTopLevelDeclarations.has(parsedItem.longname)) { domTopLevelDeclarations.set(parsedItem.longname, parsedItem.parsed); } } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (parsedItems_1_1 && !parsedItems_1_1.done && (_b = parsedItems_1.return)) _b.call(parsedItems_1); } finally { if (e_4) throw e_4.error; } } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (_d && !_d.done && (_a = _c.return)) _a.call(_c); } finally { if (e_3) throw e_3.error; } } this.resolvedItems = domTopLevelDeclarations; return domTopLevelDeclarations; } }; /** * Creates the comment for the jsdoc item * @param comment The complete comment text of the item * @param addExample Indicates if examples should be omitted or not */ JSDocTsdParser.prototype.cleanJSDocComment = function (comment, addExample) { var e_5, _a; if (addExample === void 0) { addExample = false; } var tagsToPass = new Map([ ["author", true], ["copyright", true], ["deprecated", true], ["example", addExample], ["returns", true], ["see", true], ["throws", true], ["todo", true], ["param", true], ["tutorial", true], ["variation", true], ["version", true], ["license", true], ]); var cleanedComment = ""; if (comment) { var parsedComments = (0, comment_parser_1.parse)(comment); if (parsedComments.length > 0) { // This should be maximum 1 element (except you pass more than one jsdoc comment, which is here never the case) var parsedComment = parsedComments[0]; // First, add the description // The comment parser removes the " * " by line breaks, so we have to add these again var itemDescription = ""; if (parsedComment.description.length > 0) { itemDescription = parsedComment.description; } try { // Then add all tags as we receive them for (var _b = __values(parsedComment.tags), _c = _b.next(); !_c.done; _c = _b.next()) { var annotation = _c.value; if (tagsToPass.has(annotation.tag) && tagsToPass.get(annotation.tag)) { cleanedComment += "\n@" + annotation.tag; var tagValue = (annotation.name + " " + annotation.description).trim(); if (tagValue.length > 0) { // The comment parser removes the " * " by line breaks, so we have to add these again // The format everything well, we insert as much spaces as the annotation name + 2, because // of the "@" char and a white space var spacesToInsert = annotation.tag.length + 2; if (annotation.name === "param") { spacesToInsert += annotation.name.length; } cleanedComment += " " + tagValue.replace(/\r?\n/g, "\n" + " ".repeat(spacesToInsert)); } } else if (annotation.tag === "description") { itemDescription = annotation.name + " " + annotation.description; } } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_5) throw e_5.error; } } if (itemDescription.length > 0) { cleanedComment = itemDescription.replace(/\r?\n/g, "\n") + cleanedComment; } } } return cleanedComment; }; /** * Creates parameters for functions, constructors etc. * @todo This function needs to be refactored. * @param params * @param functionName */ JSDocTsdParser.prototype.createDomParams = function (params, functionName, jsdocItem) { var _this = this; var domParams = []; var typeDef; var propParam; for (var i = 0; i < params.length; i++) { var param = params[i]; var paramIsProperty = (param.name.indexOf(".") > 0); var nextParamIsProperty = (i + 1 < params.length) && (params[i + 1].name.indexOf(".") > 0); var lastParam = (i + 1 === params.length); var domParam = void 0; // check the type of the parameter if (!paramIsProperty && nextParamIsProperty) { // the parameter is a parameter with properties // remember the parameter propParam = param; // create a new typedef typeDef = { kind: "typedef", longname: functionName + "_" + param.name, meta: param.meta, name: functionName + "_" + param.name, properties: [], scope: "", type: param.type, }; this.jsdocItems.push(typeDef); } else if (paramIsProperty) { // the parameter is a property if (!typeDef || !typeDef.properties) { /* istanbul ignore next */ throw new Error("Parent of property ".concat(param.name, " is missing or incorrect")); } // add the property to the typedef var prop = { comment: param.comment, description: param.description, name: param.name.substr(param.name.indexOf(".") + 1), optional: param.optional, type: param.type, }; typeDef.properties.push(prop); if (lastParam || !nextParamIsProperty) { // the parameter is the last property if (!propParam) { /* istanbul ignore next */ throw new Error("Parent of property ".concat(param.name, " is missing or incorrect")); } // create an interface from the typedef // the property param can also be an array, e. g. @param {fuu[]}, @param {fuu[].bar} var interfaceTypeMatches = typeDef.type.names[0].match(/(?:Array\.<([^>]+)>)|(?:([^\[]*)\[\])/i); var isArray = (!!interfaceTypeMatches); typeDef.type.names[0] = ((interfaceTypeMatches) ? interfaceTypeMatches[1] : typeDef.type.names[0]); var domInterface = this.parseTypeDefinition(typeDef); if (domInterface) { this.parsedItems.set(typeDef.longname, [{ longname: typeDef.longname, memberof: typeDef.memberof, original: typeDef, parsed: domInterface, }]); // create the parameter with the interface as type var interfaceType = void 0; if (isArray) { interfaceType = dom.create.array(domInterface); } else { interfaceType = dom.create.typeParameter(typeDef.name, domInterface); } if (typeDef.type && typeDef.type && typeDef.type.names.length > 0) { interfaceType = dom.create.union(__spreadArray([interfaceType], __read(typeDef.type.names.map(function (n) { return _this.mapVariableType(n, jsdocItem); })), false)); } domParam = dom.create.parameter(propParam.name, interfaceType); if (propParam.optional) { domParam.flags = dom.ParameterFlags.Optional; } } else { Logger_1.Logger.log("Can't create interface for property param. Invalid typedef: ".concat(JSON.stringify(typeDef))); } } } else if (param.type && param.type.names.length > 0) { // the param has a simple type domParam = dom.create.parameter(param.name, this.mapTypesToUnion(param.type.names, jsdocItem)); } else { // the param has no type => map to "any" domParam = dom.create.parameter(param.name, dom.type.any); } if (domParam) { if (param.optional) { domParam.flags = dom.ParameterFlags.Optional; } this.handleFlags(param, domParam); domParams.push(domParam); } } return domParams; }; /** * Uses the configured version comparator to check if the passed since tag is in range of the * configured latest since tag. */ JSDocTsdParser.prototype.evaluateSinceTag = function (item) { if (typeof item.since === "string" && item.since !== "") { return this.config.compareVersions(item.since, this.config.latestVersion, item.longname); } else { return true; } }; JSDocTsdParser.prototype.findClassParent = function (parsedItem) { if (!parsedItem.augments) { return; } var classes = this.getAllClasses(); var output = parsedItem.augments .map(function (augments) { try { var classItem = classes.find(function (cls) { return cls.longname === augments || (cls.original.name === augments && parsedItem.memberof === cls.memberof); }); return classItem && classItem.parsed; } catch (e) { return; } }) .find(function (x) { return x; }); return output; }; /** * Tries to find the parent item of the passed jsdoc item * @param parentItemLongname Long name of the searched item * @param domTopLevelDeclarations Source items to search in */ JSDocTsdParser.prototype.findParentItem = function (parentItemLongname, domTopLevelDeclarations) { // The parsed items are stored with their longname and by reference. // This is why we can simply return the stored elements in the parsedItems-map var parentItem = this.parsedItems.get(parentItemLongname); if (parentItem) { if (parentItem.length === 1) { return parentItem[0].parsed; } else if (parentItem.length > 1) { throw new Error("Found ".concat(parentItem.length, " items with name ").concat(parentItemLongname, " as possible parent items: ").concat(JSON.stringify(parentItem))); } } return null; }; JSDocTsdParser.prototype.getAllClasses = function () { var e_6, _a, e_7, _b; if (this._parsedClassess) { return this._parsedClassess; } var parsedClasses = []; try { for (var _c = __values(this.parsedItems.values()), _d = _c.next(); !_d.done; _d = _c.next()) { var items = _d.value; try { for (var items_1 = (e_7 = void 0, __values(items)), items_1_1 = items_1.next(); !items_1_1.done; items_1_1 = items_1.next()) { var item = items_1_1.value; if (item.original.kind === "class") { parsedClasses.push(item); } } } catch (e_7_1) { e_7 = { error: e_7_1 }; } finally { try { if (items_1_1 && !items_1_1.done && (_b = items_1.return)) _b.call(items_1); } finally { if (e_7) throw e_7.error; } } } } catch (e_6_1) { e_6 = { error: e_6_1 }; } finally { try { if (_d && !_d.done && (_a = _c.return)) _a.call(_c); } finally { if (e_6) throw e_6.error; } } return this._parsedClassess = parsedClasses; }; /** * Determines the return value of a function declaration * @param jsdocItem */ JSDocTsdParser.prototype.getFunctionReturnValue = function (jsdocItem) { var functionReturnValue; if (jsdocItem.returns && jsdocItem.returns.length > 0) { if (jsdocItem.returns[0].type) { functionReturnValue = this.mapTypesToUnion(jsdocItem.returns[0].type.names, jsdocItem); } else { // the jsdoc comment is incomplete, there is no type information for the return value Logger_1.Logger.log("Invalid return type. Check the documentation of function ".concat(jsdocItem.longname)); functionReturnValue = dom.type.any; } } else { // If no return value was specified, the function has implicity the return type "void" functionReturnValue = dom.type.void; } return functionReturnValue; }; /** * Sets the correct export flags to the declaration base. */ JSDocTsdParser.prototype.handleFlags = function (doclet, obj) { obj.flags = dom.DeclarationFlags.None; obj.flags |= this.accessFlagMap[doclet.access]; obj.flags |= doclet.optional || doclet.defaultvalue !== undefined ? doclet.kind !== "function" ? dom.ParameterFlags.Optional : dom.DeclarationFlags.None : obj.flags; obj.flags |= doclet.variable ? dom.ParameterFlags.Rest : dom.DeclarationFlags.None; obj.flags |= doclet.virtual ? dom.DeclarationFlags.Abstract : dom.DeclarationFlags.None; obj.flags |= doclet.readonly ? dom.DeclarationFlags.ReadOnly : dom.DeclarationFlags.None; obj.flags |= doclet.scope === "static" ? dom.DeclarationFlags.Static : dom.DeclarationFlags.None; var cast = obj; if (doclet.optional && cast.kind === "property" && cast.flags === dts_dom_1.ParameterFlags.Optional) { obj.flags = dom.DeclarationFlags.Optional; } }; /** * Handler for template-functions. */ JSDocTsdParser.prototype.handleTags = function (doclet, obj) { var e_8, _a; if (doclet.tags) { try { for (var _b = __values(doclet.tags), _c = _b.next(); !_c.done; _c = _b.next()) { var tag = _c.value; switch (tag.title) { case "template": if (obj.typeParameters) { obj.typeParameters.push(dom.create.typeParameter(tag.value)); } break; } } } catch (e_8_1) { e_8 = { error: e_8_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_8) throw e_8.error; } } } else if (doclet.comment) { var matches = doclet.comment.match(/@template\s?([^\r\n]+)\r?\n?/); if (matches) { obj.typeParameters.push(dom.create.typeParameter(matches[1])); } } }; JSDocTsdParser.prototype.mapTypesToUnion = function (types, jsdocItem) { var e_9, _a; var domTypes = []; try { for (var types_1 = __values(types), types_1_1 = types_1.next(); !types_1_1.done; types_1_1 = types_1.next()) { var type = types_1_1.value; domTypes.push(this.mapVariableType(type, jsdocItem)); } } catch (e_9_1) { e_9 = { error: e_9_1 }; } finally { try { if (types_1_1 && !types_1_1.done && (_a = types_1.return)) _a.call(types_1); } finally { if (e_9) throw e_9.error; } } return dom.create.union(domTypes); }; JSDocTsdParser.prototype.mapVariableType = function (variableType, jsdocItem) { // resolve array types // jsdoc will provide arrays always as "Array.<>" if it's typed or as "Array" if it's not typed var resultType = dom.type.any; if (variableType.startsWith("external:")) { return dom.type.any; } if (jsdocItem && jsdocItem.memberof && variableType.startsWith(jsdocItem.memberof)) { if (variableType === jsdocItem.memberof) { var index = variableType.replace(/~/g, ".").lastIndexOf("."); variableType = variableType.substring(index); } else { variableType = variableType.substring(jsdocItem.memberof.length); } if (variableType.startsWith(".") || variableType.startsWith("~")) { variableType = variableType.substring(1); } } while (/^Array/i.test(variableType)) { // it's an array, check if it's typed // Array.< (bllaaa|bla) > var arrayTypeMatches = variableType.match(/Array\.<(\(?[\w|~:]+\)?)>/i); // @todo: can contain namepaths if (arrayTypeMatches && arrayTypeMatches[1]) { var arrayTypeString = arrayTypeMatches[1]; var arrayType = (arrayTypeString.toLowerCase() === "array") ? dom.type.array(dom.type.any) : this.mapVariableTypeString(arrayTypeString, jsdocItem); resultType = (resultType === dom.type.any) ? dom.type.array(arrayType) : dom.type.array(resultType); // nested array // remove the string from the variable type (nested arrays) var regExp = new RegExp("Array.<".concat(arrayTypeString.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"), ">"), "i"); variableType = variableType.replace(regExp, ""); } else { resultType = dom.type.array(resultType); // remove the array keyword variableType = variableType.replace(/^Array(\.<)?/i, ""); } } if (resultType === dom.type.any) { // check if it's an object type (Object.<string, number>) var objectTypeMatches = variableType.match(/^Object\.<([^,]+),\s?([^>]+)>$/); if (objectTypeMatches && objectTypeMatches.length === 3) { resultType = "{ [key: ".concat(objectTypeMatches[1], "]: ").concat(objectTypeMatches[2], " }"); } else { resultType = this.mapVariableTypeString(variableType, jsdocItem); } } return resultType; }; JSDocTsdParser.prototype.mapVariableTypeString = function (variableType, jsdocItem) { var resultTypeStr = ""; var resultType = null; if (variableType === "bool") { resultTypeStr = "boolean"; } else if (variableType === "function") { resultTypeStr = "Function"; } else if (variableType === "*") { resultTypeStr = "any"; } else { // check if it's a module member // e.g. module:<moduleName> or module:<moduleName>~<moduleMember> var moduleMemberMatches = variableType.match(/^module:([^~]+)~?(.*)$/); if (moduleMemberMatches) { var moduleName = moduleMemberMatches[1]; var memberName = (moduleMemberMatches.length === 3) ? moduleMemberMatches[2] : null; resultTypeStr = moduleName; if (memberName) { resultTypeStr += ".".concat(memberName); } } // check if it's a union type if (!resultTypeStr && !resultType && variableType.indexOf("|") > -1) { variableType = variableType.replace(/\(|\)/g, ""); resultType = this.mapTypesToUnion(variableType.split("|"), jsdocItem); } // check if it's a type parameter // e.g. "Promise.<*>" (JSDoc always separate the type with a dot) var typeParameterMatches = variableType.match(/^([^<.]+)\.<([^>]+)>$/); if (!resultTypeStr && !resultType && typeParameterMatches && typeParameterMatches.length === 3) { // it's not a pretty nice solution, but it works for now resultType = dom.create.typeParameter("".concat(typeParameterMatches[1], "<").concat(this.mapVariableType(typeParameterMatches[2], jsdocItem).toString(), ">")); } } if (!resultType) { resultType = (resultTypeStr || variableType); } if (resultType === null) { resultType = dom.type.null; } return resultType; }; JSDocTsdParser.prototype.parseClass = function (jsdocItem, domClass) { if (!domClass) { domClass = dom.create.class(jsdocItem.name); domClass.jsDocComment = jsdocItem.classdesc; } if (!jsdocItem.hideconstructor) { // Add the constructor var constructorDeclaration = void 0; if (jsdocItem.params && jsdocItem.params.length > 0) { constructorDeclaration = dom.create.constructor(this.createDomParams(jsdocItem.params, "".concat(jsdocItem.name, "Constructor"), jsdocItem)); } else { // no params constructorDeclaration = dom.create.constructor([]); } constructorDeclaration.jsDocComment = this.cleanJSDocComment(jsdocItem.comment); domClass.members.push(constructorDeclaration); } return domClass; }; JSDocTsdParser.prototype.parseConstant = function (jsdocItem) { if (jsdocItem.isEnum) { return this.parseEnum(jsdocItem); } var propertyType = dom.type.any; if (jsdocItem.type && jsdocItem.type.names.length > 0) { propertyType = this.mapTypesToUnion(jsdocItem.type.names, jsdocItem); } return dom.create.const(jsdocItem.name, propertyType); }; JSDocTsdParser.prototype.parseEnum = function (jsdocItem) { var e_10, _a; if (!jsdocItem.isEnum) { /* istanbul ignore next */ throw new Error("item ".concat(jsdocItem.longname, " is not an enum")); } var domEnum = dom.create.enum(jsdocItem.name, (jsdocItem.kind === "constant")); if (jsdocItem.properties) { try { for (var _b = __values(jsdocItem.properties), _c = _b.next(); !_c.done; _c = _b.next()) { var property = _c.value; var isJSONValue = false; if (property.defaultvalue) { try { JSON.parse(property.defaultvalue); isJSONValue = true; } catch (err) { isJSONValue = false; } } var domEnumMember = dom.create.enumValue(property.name, isJSONValue ? undefined : property.defaultvalue); domEnumMember.jsDocComment = this.cleanJSDocComment(property.comment); domEnum.members.push(domEnumMember); } } catch (e_10_1) { e_10 = { error: e_10_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_10) throw e_10.error; } } } return domEnum; }; JSDocTsdParser.prototype.parseFunction = function (jsdocItem) { var functionReturnValue = this.getFunctionReturnValue(jsdocItem); var domFunction; if (jsdocItem.this) { jsdocItem.params = jsdocItem.params || []; jsdocItem.params.unshift({ comment: "", description: "", name: "this", type: { names: [jsdocItem.this], }, }); } if (jsdocItem.params && jsdocItem.params.length > 0) { domFunction = dom.create.function(jsdocItem.name, this.createDomParams(jsdocItem.params, jsdocItem.name, jsdocItem), functionReturnValue); } else { // no params => create a single function declaration domFunction = dom.create.function(jsdocItem.name, [], functionReturnValue); } return domFunction; }; JSDocTsdParser.prototype.parseInterface = function (jsdocItem) { return dom.create.interface(jsdocItem.name); }; /** * Converts a JSDoc item to a declaration base which can be printed * with the dts-dom module in a declaration file. * * The resulting item can be passed to dts-dom.emit which will then * output the item in the declaration file. * @param item JSDoc item from the taffy DB */ JSDocTsdParser.prototype.parseJSDocItem = function (item) { switch (item.kind) { case "function": return this.parseFunction(item); case "constant": return this.parseConstant(item); case "member": if (item.isEnum) { return this.parseEnum(item); } else { return this.parseMember(item); } case "namespace": return this.parseNamespace(item); case "typedef": return this.parseTypeDefinition(item); case "class": if (this.parsedItems.has(item.longname)) { var parsedItems = this.parsedItems.get(item.longname); if (parsedItems.length > 0) { // @ts-ignore var parsedClass = parsedItems.filter(function (parsedItem) { return parsedItem.parsed.kind === "class"; }); if (parsedClass.length > 0) { // class is already created, only add the constructor to the class this.parseClass(item, parsedClass[0].parsed); return null; } } } return this.parseClass(item); case "interface": return this.parseInterface(item); case "module": return this.parseModule(item); // suppress warnings for this type case "file": return null; default: if (item.kind !== "package" && item.kind !== "external") { /* istanbul ignore next */ Logger_1.Logger.log("Unsupported jsdoc item kind: ".concat(item.kind, " (item name: ").concat(item.longname, ")")); } return null; } }; JSDocTsdParser.prototype.parseMember = function (jsdocItem) { if (jsdocItem.isEnum) { /* istanbul ignore next */ throw new Error("item ".concat(jsdocItem.longname, " is an enum")); } var propertyType = dom.type.any; if (jsdocItem.type && jsdocItem.type.names.length > 0) { propertyType = this.mapTypesToUnion(jsdocItem.type.names, jsdocItem); } return dom.create.property(jsdocItem.name, propertyType); }; JSDocTsdParser.prototype.parseModule = function (jsdocItem) { return dom.create.module(jsdocItem.name); }; JSDocTsdParser.prototype.parseNamespace = function (jsdocItem) { return dom.create.namespace(jsdocItem.name); }; JSDocTsdParser.prototype.parseTypeDefinition = function (jsdocItem) { var result = null; if (jsdocItem.type && jsdocItem.type && jsdocItem.type.names.length > 0) { var typedefType = (jsdocItem.type.names.filter(function (t) { return t.toLowerCase() === "object" || t.toLowerCase() === "function"; })[0] || "").toLowerCase(); if (typedefType === "function") { result = this.parseTypeDefinitionAsFunction(jsdocItem); jsdocItem.type.names = jsdocItem.type.names.filter(function (t) { return t.toLowerCase() !== "function"; }); } else if (typedefType === "object") { result = this.parseTypeDefinitionAsObject(jsdocItem); jsdocItem.type.names = jsdocItem.type.names.filter(function (t) { return t.toLowerCase() !== "object"; }); } else { result = this.parseTypeDefinitionAsType(jsdocItem); jsdocItem.type.names.shift(); } } else { // No type specified (@typedef <Name> instead of @typedef {<type>} <Name>) // We assume that it's of type object result = this.parseTypeDefinitionAsObject(jsdocItem); } return result; }; JSDocTsdParser.prototype.parseTypeDefinitionAsFunction = function (jsdocItem) { // if the jsdoc item has a property "type", we can be sure that it isn't a typedef // which should be mapped to an interface. Instead we create a typeAlias-Declaration var functionType = dom.create.functionType((jsdocItem.params) ? this.createDomParams(jsdocItem.params, jsdocItem.name, jsdocItem) : [], this.getFunctionReturnValue(jsdocItem)); return dom.create.alias(jsdocItem.name, functionType); }; JSDocTsdParser.prototype.parseTypeDefinitionAsObject = function (jsdocItem) { var e_11, _a; var domInterface = dom.create.interface(jsdocItem.name); if (jsdocItem.properties) { try { for (var _b = __values(jsdocItem.properties), _c = _b.next(); !_c.done; _c = _b.next()) { var property = _c.value; var propertyType = dom.type.any; if (property.type) { propertyType = this.mapTypesToUnion(property.type.names, jsdocItem); } var domProperty = dom.create.property(property.name, propertyType); domProperty.jsDocComment = property.description; this.handleFlags(property, domProperty); domInterface.members.push(domProperty); } } catch (e_11_1) { e_11 = { error: e_11_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_11) throw e_11.error; } } } return domInterface; }; JSDocTsdParser.prototype.parseTypeDefinitionAsType = function (jsdocItem) { var _this = this; var result = null; if (jsdocItem.properties) { Logger_1.Logger.log("Invalid typedef. Typedef type is '".concat(jsdocItem.type.names[0], "' and properties are defined.\n\t\t\tProperties are only allowed for type definitions of type 'object': ").concat(JSON.stringify(jsdocItem))); } else { result = dom.create.alias(jsdocItem.name, jsdocItem.type.names.map(function (t) { return _this.mapVariableType(t, jsdocItem); }).join("|")); } return result; }; /** * Adds the class item to the class * @param classMember The parsed class member item * @param parsedClass The parsed class item */ JSDocTsdParser.prototype.resolveClassMembership = function (classMember, parsedClass) { var classMemberToAdd = null; var kind = classMember.kind; switch (kind) { // @ts-ignore case "function": // Classes can only contain method declarations var classMemberFunction = classMember; classMemberToAdd = this.transformFunctionDeclarationToMethod(classMemberFunction); break; case "index-signature": case "constructor": case "property": case "method": classMemberToAdd = classMember; break; /* istanbul ignore next */ default: Logger_1.Logger.log("Can't add member '".concat(classMember.name, "' to parent item '").concat(parsedClass.name, "'. Unsupported member type: '").concat(kind, "'")); break; } if (classMemberToAdd) { parsedClass.members.push(classMemberToAdd); } }; /** * Adds the enum item to the parent enum * @param enumMember The parsed enum member item * @param parsedEnum The parsed enum item */ JSDocTsdParser.prototype.resolveEnumMembership = function (enumMember, parsedEnum) { // enum members can already exists var enumMemberExists = parsedEnum.members.some(function (member) { return member.name === enumMember.name; }); if (!enumMemberExists) { parsedEnum.members.push(enumMember); } }; /** * Adds the interface member to it's parent interface * @param interfaceMember The parsed interface member * @param parsedInterface The parsed interface */ JSDocTsdParser.prototype.resolveInterfaceMembership = function (interfaceMember, parsedInterface) { var interfaceMemberToAdd = null; switch (interfaceMember.kind) { // @ts-ignore case "function": // Interfaces can only have method declarations as members interfaceMemberToAdd = this.transformFunctionDeclarationToMethod(interfaceMember); break; case "property": interfaceMemberToAdd = interfaceMember; break; /* istanbul ignore next*/ default: Logger_1.Logger.log("Can't add member '".concat(interfaceMember.name, "' to parent item '").concat(parsedInterface.name, "'. Unsupported member type: '").concat(interfaceMember.kind, "'")); break; } if (interfaceMemberToAdd) { parsedInterface.members.push(interfaceMemberToAdd); } }; /** * Adds the member item to it's parent module * @param moduleMember The parsed module member item * @param parsedModule The parsed module item */ JSDocTsdParser.prototype.resolveModuleMembership = function (moduleMember, parsedModule) { var moduleMemberToAdd = null; switch (moduleMember.kind) { // @ts-ignore case "property": this.resolveModuleMembership(this.transformPropertyDeclarationToVariable(moduleMember), parsedModule); break;