@otris/jsdoc-tsd
Version:
JSDoc Template for generate typescript definition files from JSDoc comments
1,051 lines (1,050 loc) • 54.1 kB
JavaScript
"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;