typescript-json-schema
Version:
typescript-json-schema generates JSON Schema files from your Typescript sources
1,163 lines • 63.8 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.exec = exports.programFromConfig = exports.generateSchema = exports.buildGenerator = exports.getProgramFromFiles = exports.JsonSchemaGenerator = exports.regexRequire = exports.getDefaultArgs = void 0;
var glob = require("glob");
var safe_stable_stringify_1 = require("safe-stable-stringify");
var path = require("path");
var crypto_1 = require("crypto");
var ts = require("typescript");
var path_equal_1 = require("path-equal");
var vm = require("vm");
var REGEX_FILE_NAME_OR_SPACE = /(\bimport\(".*?"\)|".*?")\.| /g;
var REGEX_TSCONFIG_NAME = /^.*\.json$/;
var REGEX_TJS_JSDOC = /^-([\w]+)\s+(\S|\S[\s\S]*\S)\s*$/g;
var REGEX_GROUP_JSDOC = /^[.]?([\w]+)\s+(\S|\S[\s\S]*\S)\s*$/g;
var REGEX_REQUIRE = /^(\s+)?require\((\'@?[a-zA-Z0-9.\/_-]+\'|\"@?[a-zA-Z0-9.\/_-]+\")\)(\.([a-zA-Z0-9_$]+))?(\s+|$)/;
var NUMERIC_INDEX_PATTERN = "^[0-9]+$";
function getDefaultArgs() {
return {
ref: true,
aliasRef: false,
topRef: false,
titles: false,
defaultProps: false,
noExtraProps: false,
propOrder: false,
typeOfKeyword: false,
required: false,
strictNullChecks: false,
esModuleInterop: false,
skipLibCheck: false,
experimentalDecorators: true,
ignoreErrors: false,
out: "",
validationKeywords: [],
include: [],
excludePrivate: false,
uniqueNames: false,
rejectDateType: false,
id: "",
defaultNumberType: "number",
tsNodeRegister: false,
constAsEnum: false,
};
}
exports.getDefaultArgs = getDefaultArgs;
function extend(target) {
var _ = [];
for (var _i = 1; _i < arguments.length; _i++) {
_[_i - 1] = arguments[_i];
}
if (target == null) {
throw new TypeError("Cannot convert undefined or null to object");
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) {
for (var nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
}
function unique(arr) {
var temp = {};
for (var _i = 0, arr_1 = arr; _i < arr_1.length; _i++) {
var e = arr_1[_i];
temp[e] = true;
}
var r = [];
for (var k in temp) {
if (Object.prototype.hasOwnProperty.call(temp, k)) {
r.push(k);
}
}
return r;
}
function resolveRequiredFile(symbol, key, fileName, objectName) {
var sourceFile = getSourceFile(symbol);
var requiredFilePath = /^[.\/]+/.test(fileName)
? fileName === "."
? path.resolve(sourceFile.fileName)
: path.resolve(path.dirname(sourceFile.fileName), fileName)
: fileName;
var requiredFile = require(requiredFilePath);
if (!requiredFile) {
throw Error("Required: File couldn't be loaded");
}
var requiredObject = objectName ? requiredFile[objectName] : requiredFile.default;
if (requiredObject === undefined) {
throw Error("Required: Variable is undefined");
}
if (typeof requiredObject === "function") {
throw Error("Required: Can't use function as a variable");
}
if (key === "examples" && !Array.isArray(requiredObject)) {
throw Error("Required: Variable isn't an array");
}
return requiredObject;
}
function regexRequire(value) {
return REGEX_REQUIRE.exec(value);
}
exports.regexRequire = regexRequire;
function parseValue(symbol, key, value) {
var match = regexRequire(value);
if (match) {
var fileName = match[2].substr(1, match[2].length - 2).trim();
var objectName = match[4];
return resolveRequiredFile(symbol, key, fileName, objectName);
}
try {
return JSON.parse(value);
}
catch (error) {
return value;
}
}
function extractLiteralValue(typ) {
var str = typ.value;
if (str === undefined) {
str = typ.text;
}
if (typ.flags & ts.TypeFlags.StringLiteral) {
return str;
}
else if (typ.flags & ts.TypeFlags.BooleanLiteral) {
return typ.intrinsicName === "true";
}
else if (typ.flags & ts.TypeFlags.EnumLiteral) {
var num = parseFloat(str);
return isNaN(num) ? str : num;
}
else if (typ.flags & ts.TypeFlags.NumberLiteral) {
return parseFloat(str);
}
return undefined;
}
function resolveTupleType(propertyType) {
if (!propertyType.getSymbol() &&
propertyType.getFlags() & ts.TypeFlags.Object &&
propertyType.objectFlags & ts.ObjectFlags.Reference) {
return propertyType.target;
}
if (!(propertyType.getFlags() & ts.TypeFlags.Object &&
propertyType.objectFlags & ts.ObjectFlags.Tuple)) {
return null;
}
return propertyType;
}
var simpleTypesAllowedProperties = {
type: true,
description: true,
};
function addSimpleType(def, type) {
for (var k in def) {
if (!simpleTypesAllowedProperties[k]) {
return false;
}
}
if (!def.type) {
def.type = type;
}
else if (typeof def.type !== "string") {
if (!def.type.every(function (val) {
return typeof val === "string";
})) {
return false;
}
if (def.type.indexOf("null") === -1) {
def.type.push("null");
}
}
else {
if (typeof def.type !== "string") {
return false;
}
if (def.type !== "null") {
def.type = [def.type, "null"];
}
}
return true;
}
function makeNullable(def) {
if (!addSimpleType(def, "null")) {
var union = def.oneOf || def.anyOf;
if (union) {
union.push({ type: "null" });
}
else {
var subdef = {};
for (var k in def) {
if (def.hasOwnProperty(k)) {
subdef[k] = def[k];
delete def[k];
}
}
def.anyOf = [subdef, { type: "null" }];
}
}
return def;
}
function getCanonicalDeclaration(sym) {
var _a, _b, _c;
if (sym.valueDeclaration !== undefined) {
return sym.valueDeclaration;
}
else if (((_a = sym.declarations) === null || _a === void 0 ? void 0 : _a.length) === 1) {
return sym.declarations[0];
}
var declarationCount = (_c = (_b = sym.declarations) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0;
throw new Error("Symbol \"".concat(sym.name, "\" has no valueDeclaration and ").concat(declarationCount, " declarations."));
}
function getSourceFile(sym) {
var currentDecl = getCanonicalDeclaration(sym);
while (currentDecl.kind !== ts.SyntaxKind.SourceFile) {
if (currentDecl.parent === undefined) {
throw new Error("Unable to locate source file for declaration \"".concat(sym.name, "\"."));
}
currentDecl = currentDecl.parent;
}
return currentDecl;
}
var validationKeywords = {
multipleOf: true,
maximum: true,
exclusiveMaximum: true,
minimum: true,
exclusiveMinimum: true,
maxLength: true,
minLength: true,
pattern: true,
items: true,
maxItems: true,
minItems: true,
uniqueItems: true,
contains: true,
maxProperties: true,
minProperties: true,
additionalProperties: true,
enum: true,
type: true,
examples: true,
ignore: true,
description: true,
format: true,
default: true,
$ref: true,
id: true,
$id: true,
$comment: true,
title: true
};
var annotationKeywords = {
description: true,
default: true,
examples: true,
title: true,
$ref: true,
};
var subDefinitions = {
items: true,
additionalProperties: true,
contains: true,
};
var JsonSchemaGenerator = (function () {
function JsonSchemaGenerator(symbols, allSymbols, userSymbols, inheritingTypes, tc, args) {
if (args === void 0) { args = getDefaultArgs(); }
this.args = args;
this.reffedDefinitions = {};
this.schemaOverrides = new Map();
this.typeNamesById = {};
this.typeIdsByName = {};
this.recursiveTypeRef = new Map();
this.symbols = symbols;
this.allSymbols = allSymbols;
this.userSymbols = userSymbols;
this.inheritingTypes = inheritingTypes;
this.tc = tc;
this.userValidationKeywords = args.validationKeywords.reduce(function (acc, word) {
var _a;
return (__assign(__assign({}, acc), (_a = {}, _a[word] = true, _a)));
}, {});
this.constAsEnum = args.constAsEnum;
}
Object.defineProperty(JsonSchemaGenerator.prototype, "ReffedDefinitions", {
get: function () {
return this.reffedDefinitions;
},
enumerable: false,
configurable: true
});
JsonSchemaGenerator.prototype.isFromDefaultLib = function (symbol) {
var declarations = symbol.getDeclarations();
if (declarations && declarations.length > 0 && declarations[0].parent) {
return declarations[0].parent.getSourceFile().hasNoDefaultLib;
}
return false;
};
JsonSchemaGenerator.prototype.resetSchemaSpecificProperties = function (includeAllOverrides) {
var _this = this;
if (includeAllOverrides === void 0) { includeAllOverrides = false; }
this.reffedDefinitions = {};
this.typeIdsByName = {};
this.typeNamesById = {};
if (includeAllOverrides) {
this.schemaOverrides.forEach(function (value, key) {
_this.reffedDefinitions[key] = value;
});
}
};
JsonSchemaGenerator.prototype.parseCommentsIntoDefinition = function (symbol, definition, otherAnnotations) {
var _this = this;
if (!symbol) {
return;
}
if (!this.isFromDefaultLib(symbol)) {
var comments = symbol.getDocumentationComment(this.tc);
if (comments.length) {
definition.description = comments
.map(function (comment) {
var newlineNormalizedComment = comment.text.replace(/\r\n/g, "\n");
if (comment.kind === "linkText") {
return newlineNormalizedComment.trim();
}
return newlineNormalizedComment;
})
.join("").trim();
}
}
var jsdocs = symbol.getJsDocTags();
jsdocs.forEach(function (doc) {
var _a, _b;
var name = doc.name;
var originalText = doc.text ? doc.text.map(function (t) { return t.text; }).join("") : "";
var text = originalText;
if (name.startsWith("TJS-")) {
name = name.slice(4);
if (!text) {
text = "true";
}
}
else if (name === "TJS" && text.startsWith("-")) {
var match = new RegExp(REGEX_TJS_JSDOC).exec(originalText);
if (match) {
name = match[1];
text = match[2];
}
else {
name = text.replace(/^[\s\-]+/, "");
text = "true";
}
}
if (subDefinitions[name]) {
var match = new RegExp(REGEX_GROUP_JSDOC).exec(text);
if (match) {
var k = match[1];
var v = match[2];
definition[name] = __assign(__assign({}, definition[name]), (_a = {}, _a[k] = v ? parseValue(symbol, k, v) : true, _a));
return;
}
}
if (name.includes(".")) {
var parts = name.split(".");
var key = parts[0];
if (parts.length === 2 && subDefinitions[key]) {
definition[key] = __assign(__assign({}, definition[key]), (_b = {}, _b[parts[1]] = text ? parseValue(symbol, name, text) : true, _b));
}
}
if (validationKeywords[name] || _this.userValidationKeywords[name]) {
definition[name] = text === undefined ? "" : parseValue(symbol, name, text);
}
else {
otherAnnotations[doc.name] = true;
}
});
};
JsonSchemaGenerator.prototype.getDefinitionForRootType = function (propertyType, reffedType, definition, defaultNumberType, ignoreUndefined) {
var _a;
var _this = this;
var _b;
if (defaultNumberType === void 0) { defaultNumberType = this.args.defaultNumberType; }
if (ignoreUndefined === void 0) { ignoreUndefined = false; }
var tupleType = resolveTupleType(propertyType);
if (tupleType) {
var elemTypes = propertyType.typeArguments;
var fixedTypes = elemTypes.map(function (elType) { return _this.getTypeDefinition(elType); });
definition.type = "array";
if (fixedTypes.length > 0) {
definition.items = fixedTypes;
}
var targetTupleType = propertyType.target;
definition.minItems = targetTupleType.minLength;
if (targetTupleType.hasRestElement) {
definition.additionalItems = fixedTypes[fixedTypes.length - 1];
fixedTypes.splice(fixedTypes.length - 1, 1);
}
else {
definition.maxItems = targetTupleType.fixedLength;
}
}
else {
var propertyTypeString = this.tc.typeToString(propertyType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
var flags = propertyType.flags;
var arrayType = this.tc.getIndexTypeOfType(propertyType, ts.IndexKind.Number);
if (flags & ts.TypeFlags.String) {
definition.type = "string";
}
else if (flags & ts.TypeFlags.Number) {
var isInteger = definition.type === "integer" ||
(reffedType === null || reffedType === void 0 ? void 0 : reffedType.getName()) === "integer" ||
defaultNumberType === "integer";
definition.type = isInteger ? "integer" : "number";
}
else if (flags & ts.TypeFlags.Boolean) {
definition.type = "boolean";
}
else if (flags & ts.TypeFlags.ESSymbol) {
definition.type = "object";
}
else if (flags & ts.TypeFlags.Null) {
definition.type = "null";
}
else if (flags & ts.TypeFlags.Undefined || propertyTypeString === "void") {
if (!ignoreUndefined) {
throw new Error("Not supported: root type undefined");
}
definition.type = "undefined";
}
else if (flags & ts.TypeFlags.Any || flags & ts.TypeFlags.Unknown) {
}
else if (propertyTypeString === "Date" && !this.args.rejectDateType) {
definition.type = "string";
definition.format = definition.format || "date-time";
}
else if (propertyTypeString === "object") {
definition.type = "object";
definition.properties = {};
definition.additionalProperties = true;
}
else if (propertyTypeString === "bigint") {
definition.type = "number";
definition.properties = {};
definition.additionalProperties = false;
}
else {
var value = extractLiteralValue(propertyType);
if (value !== undefined) {
var typeofValue = typeof value;
switch (typeofValue) {
case "string":
case "boolean":
definition.type = typeofValue;
break;
case "number":
definition.type = this.args.defaultNumberType;
break;
case "object":
definition.type = "null";
break;
default:
throw new Error("Not supported: ".concat(value, " as a enum value"));
}
if (this.constAsEnum) {
definition.enum = [value];
}
else {
definition.const = value;
}
}
else if (arrayType !== undefined) {
if (propertyType.flags & ts.TypeFlags.Object &&
propertyType.objectFlags &
(ts.ObjectFlags.Anonymous | ts.ObjectFlags.Interface | ts.ObjectFlags.Mapped)) {
definition.type = "object";
definition.additionalProperties = false;
definition.patternProperties = (_a = {},
_a[NUMERIC_INDEX_PATTERN] = this.getTypeDefinition(arrayType),
_a);
if (!!((_b = Array.from(propertyType.members)) === null || _b === void 0 ? void 0 : _b.find(function (member) { return member[0] !== "__index"; }))) {
this.getClassDefinition(propertyType, definition);
}
}
else if (propertyType.flags & ts.TypeFlags.TemplateLiteral) {
definition.type = "string";
var texts = propertyType.texts, types = propertyType.types;
var pattern = [];
for (var i = 0; i < texts.length; i++) {
var text = texts[i].replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
var type = types[i];
if (i === 0) {
pattern.push("^");
}
if (type) {
if (type.flags & ts.TypeFlags.String) {
pattern.push("".concat(text, ".*"));
}
if (type.flags & ts.TypeFlags.Number
|| type.flags & ts.TypeFlags.BigInt) {
pattern.push("".concat(text, "[0-9]*"));
}
if (type.flags & ts.TypeFlags.Undefined) {
pattern.push("".concat(text, "undefined"));
}
if (type.flags & ts.TypeFlags.Null) {
pattern.push("".concat(text, "null"));
}
}
if (i === texts.length - 1) {
pattern.push("".concat(text, "$"));
}
}
definition.pattern = pattern.join("");
}
else {
definition.type = "array";
if (!definition.items) {
definition.items = this.getTypeDefinition(arrayType);
}
}
}
else {
var error = new TypeError("Unsupported type: " + propertyTypeString);
error.type = propertyType;
throw error;
}
}
}
return definition;
};
JsonSchemaGenerator.prototype.getReferencedTypeSymbol = function (prop) {
var decl = prop.getDeclarations();
if (decl === null || decl === void 0 ? void 0 : decl.length) {
var type = decl[0].type;
if (type && type.kind & ts.SyntaxKind.TypeReference && type.typeName) {
var symbol = this.tc.getSymbolAtLocation(type.typeName);
if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
return this.tc.getAliasedSymbol(symbol);
}
return symbol;
}
}
return undefined;
};
JsonSchemaGenerator.prototype.getDefinitionForProperty = function (prop, node) {
if (prop.flags & ts.SymbolFlags.Method) {
return null;
}
var propertyName = prop.getName();
var propertyType = this.tc.getTypeOfSymbolAtLocation(prop, node);
var reffedType = this.getReferencedTypeSymbol(prop);
var definition = this.getTypeDefinition(propertyType, undefined, undefined, prop, reffedType);
if (this.args.titles) {
definition.title = propertyName;
}
if (definition.hasOwnProperty("ignore")) {
return null;
}
var valDecl = prop.valueDeclaration;
if (valDecl === null || valDecl === void 0 ? void 0 : valDecl.initializer) {
var initial = valDecl.initializer;
while (ts.isTypeAssertionExpression(initial)) {
initial = initial.expression;
}
if (initial.expression) {
console.warn("initializer is expression for property " + propertyName);
}
else if (initial.kind && initial.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
definition.default = initial.getText();
}
else {
try {
var sandbox = { sandboxvar: null };
vm.runInNewContext("sandboxvar=" + initial.getText(), sandbox);
var val = sandbox.sandboxvar;
if (val === null ||
typeof val === "string" ||
typeof val === "number" ||
typeof val === "boolean" ||
Object.prototype.toString.call(val) === "[object Array]") {
definition.default = val;
}
else if (val) {
console.warn("unknown initializer for property " + propertyName + ": " + val);
}
}
catch (e) {
console.warn("exception evaluating initializer for property " + propertyName);
}
}
}
return definition;
};
JsonSchemaGenerator.prototype.getEnumDefinition = function (clazzType, definition) {
var _this = this;
var node = clazzType.getSymbol().getDeclarations()[0];
var fullName = this.tc.typeToString(clazzType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
var members = node.kind === ts.SyntaxKind.EnumDeclaration
? node.members
: ts.factory.createNodeArray([node]);
var enumValues = [];
var enumTypes = [];
var addType = function (type) {
if (enumTypes.indexOf(type) === -1) {
enumTypes.push(type);
}
};
members.forEach(function (member) {
var caseLabel = member.name.text;
var constantValue = _this.tc.getConstantValue(member);
if (constantValue !== undefined) {
enumValues.push(constantValue);
addType(typeof constantValue);
}
else {
var initial = member.initializer;
if (initial) {
if (initial.expression) {
var exp = initial.expression;
var text = exp.text;
if (text) {
enumValues.push(text);
addType("string");
}
else if (exp.kind === ts.SyntaxKind.TrueKeyword || exp.kind === ts.SyntaxKind.FalseKeyword) {
enumValues.push(exp.kind === ts.SyntaxKind.TrueKeyword);
addType("boolean");
}
else {
console.warn("initializer is expression for enum: " + fullName + "." + caseLabel);
}
}
else if (initial.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
enumValues.push(initial.getText());
addType("string");
}
else if (initial.kind === ts.SyntaxKind.NullKeyword) {
enumValues.push(null);
addType("null");
}
}
}
});
if (enumTypes.length) {
definition.type = enumTypes.length === 1 ? enumTypes[0] : enumTypes;
}
if (enumValues.length > 0) {
if (enumValues.length > 1) {
definition.enum = enumValues;
}
else {
definition.const = enumValues[0];
}
}
return definition;
};
JsonSchemaGenerator.prototype.getUnionDefinition = function (unionType, unionModifier, definition) {
var enumValues = [];
var simpleTypes = [];
var schemas = [];
var pushSimpleType = function (type) {
if (simpleTypes.indexOf(type) === -1) {
simpleTypes.push(type);
}
};
var pushEnumValue = function (val) {
if (enumValues.indexOf(val) === -1) {
enumValues.push(val);
}
};
for (var _i = 0, _a = unionType.types; _i < _a.length; _i++) {
var valueType = _a[_i];
var value = extractLiteralValue(valueType);
if (value !== undefined) {
pushEnumValue(value);
}
else {
var symbol = valueType.aliasSymbol;
var def = this.getTypeDefinition(valueType, undefined, undefined, symbol, symbol, undefined, undefined, true);
if (def.type === "undefined") {
continue;
}
var keys = Object.keys(def);
if (keys.length === 1 && keys[0] === "type") {
if (typeof def.type !== "string") {
console.error("Expected only a simple type.");
}
else {
pushSimpleType(def.type);
}
}
else {
schemas.push(def);
}
}
}
if (enumValues.length > 0) {
var isOnlyBooleans = enumValues.length === 2 &&
typeof enumValues[0] === "boolean" &&
typeof enumValues[1] === "boolean" &&
enumValues[0] !== enumValues[1];
if (isOnlyBooleans) {
pushSimpleType("boolean");
}
else {
var enumSchema = enumValues.length > 1 ? { enum: enumValues.sort() } : { const: enumValues[0] };
if (enumValues.every(function (x) {
return typeof x === "string";
})) {
enumSchema.type = "string";
}
else if (enumValues.every(function (x) {
return typeof x === "number";
})) {
enumSchema.type = "number";
}
else if (enumValues.every(function (x) {
return typeof x === "boolean";
})) {
enumSchema.type = "boolean";
}
schemas.push(enumSchema);
}
}
if (simpleTypes.length > 0) {
schemas.push({ type: simpleTypes.length === 1 ? simpleTypes[0] : simpleTypes });
}
if (schemas.length === 1) {
for (var k in schemas[0]) {
if (schemas[0].hasOwnProperty(k)) {
if (k === "description" && definition.hasOwnProperty(k)) {
continue;
}
definition[k] = schemas[0][k];
}
}
}
else {
definition[unionModifier] = schemas;
}
return definition;
};
JsonSchemaGenerator.prototype.getIntersectionDefinition = function (intersectionType, definition) {
var simpleTypes = [];
var schemas = [];
var pushSimpleType = function (type) {
if (simpleTypes.indexOf(type) === -1) {
simpleTypes.push(type);
}
};
for (var _i = 0, _a = intersectionType.types; _i < _a.length; _i++) {
var intersectionMember = _a[_i];
var def = this.getTypeDefinition(intersectionMember);
var keys = Object.keys(def);
if (keys.length === 1 && keys[0] === "type") {
if (typeof def.type !== "string") {
console.error("Expected only a simple type.");
}
else {
pushSimpleType(def.type);
}
}
else {
schemas.push(def);
}
}
if (simpleTypes.length > 0) {
schemas.push({ type: simpleTypes.length === 1 ? simpleTypes[0] : simpleTypes });
}
if (schemas.length === 1) {
for (var k in schemas[0]) {
if (schemas[0].hasOwnProperty(k)) {
definition[k] = schemas[0][k];
}
}
}
else {
definition.allOf = schemas;
}
return definition;
};
JsonSchemaGenerator.prototype.getClassDefinition = function (clazzType, definition) {
var _this = this;
var _a, _b;
var node = clazzType.getSymbol().getDeclarations()[0];
if (!node) {
definition.type = "object";
return definition;
}
if (this.args.typeOfKeyword && node.kind === ts.SyntaxKind.FunctionType) {
definition.typeof = "function";
return definition;
}
var clazz = node;
var props = this.tc.getPropertiesOfType(clazzType).filter(function (prop) {
var propertyFlagType = _this.tc.getTypeOfSymbolAtLocation(prop, node).getFlags();
if (ts.TypeFlags.Never === propertyFlagType || ts.TypeFlags.Undefined === propertyFlagType) {
return false;
}
if (!_this.args.excludePrivate) {
return true;
}
var decls = prop.declarations;
return !(decls &&
decls.filter(function (decl) {
var mods = decl.modifiers;
return mods && mods.filter(function (mod) { return mod.kind === ts.SyntaxKind.PrivateKeyword; }).length > 0;
}).length > 0);
});
var fullName = this.tc.typeToString(clazzType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
var modifierFlags = ts.getCombinedModifierFlags(node);
if (modifierFlags & ts.ModifierFlags.Abstract && this.inheritingTypes[fullName]) {
var oneOf = this.inheritingTypes[fullName].map(function (typename) {
return _this.getTypeDefinition(_this.allSymbols[typename]);
});
definition.oneOf = oneOf;
}
else {
if (clazz.members) {
var indexSignatures = clazz.members == null ? [] : clazz.members.filter(function (x) { return x.kind === ts.SyntaxKind.IndexSignature; });
if (indexSignatures.length === 1) {
var indexSignature = indexSignatures[0];
if (indexSignature.parameters.length !== 1) {
throw new Error("Not supported: IndexSignatureDeclaration parameters.length != 1");
}
var indexSymbol = indexSignature.parameters[0].symbol;
var indexType = this.tc.getTypeOfSymbolAtLocation(indexSymbol, node);
var isIndexedObject = indexType.flags === ts.TypeFlags.String || indexType.flags === ts.TypeFlags.Number;
if (indexType.flags !== ts.TypeFlags.Number && !isIndexedObject) {
throw new Error("Not supported: IndexSignatureDeclaration with index symbol other than a number or a string");
}
var typ = this.tc.getTypeAtLocation(indexSignature.type);
var def = void 0;
if (typ.flags & ts.TypeFlags.IndexedAccess) {
var targetName = ts.escapeLeadingUnderscores((_b = (_a = clazzType.mapper) === null || _a === void 0 ? void 0 : _a.target) === null || _b === void 0 ? void 0 : _b.value);
var indexedAccessType = typ;
var symbols = indexedAccessType.objectType.members;
var targetSymbol = symbols === null || symbols === void 0 ? void 0 : symbols.get(targetName);
if (targetSymbol) {
var targetNode = targetSymbol.getDeclarations()[0];
var targetDef = this.getDefinitionForProperty(targetSymbol, targetNode);
if (targetDef) {
def = targetDef;
}
}
}
if (!def) {
def = this.getTypeDefinition(typ, undefined, "anyOf");
}
if (isIndexedObject) {
definition.type = "object";
if (!Object.keys(definition.patternProperties || {}).length) {
definition.additionalProperties = def;
}
}
else {
definition.type = "array";
if (!definition.items) {
definition.items = def;
}
}
}
}
var propertyDefinitions = props.reduce(function (all, prop) {
var propertyName = prop.getName();
var propDef = _this.getDefinitionForProperty(prop, node);
if (propDef != null) {
all[propertyName] = propDef;
}
return all;
}, {});
if (definition.type === undefined) {
definition.type = "object";
}
if (definition.type === "object" && Object.keys(propertyDefinitions).length > 0) {
definition.properties = propertyDefinitions;
}
if (this.args.defaultProps) {
definition.defaultProperties = [];
}
if (this.args.noExtraProps && definition.additionalProperties === undefined) {
definition.additionalProperties = false;
}
if (this.args.propOrder) {
var propertyOrder = props.reduce(function (order, prop) {
order.push(prop.getName());
return order;
}, []);
definition.propertyOrder = propertyOrder;
}
if (this.args.required) {
var requiredProps = props.reduce(function (required, prop) {
var _a, _b, _c, _d;
var def = {};
_this.parseCommentsIntoDefinition(prop, def, {});
var allUnionTypesFlags = ((_d = (_c = (_b = (_a = prop.links) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.types) === null || _c === void 0 ? void 0 : _c.map) === null || _d === void 0 ? void 0 : _d.call(_c, function (t) { return t.flags; })) || [];
if (!(prop.flags & ts.SymbolFlags.Optional) &&
!(prop.flags & ts.SymbolFlags.Method) &&
!allUnionTypesFlags.includes(ts.TypeFlags.Undefined) &&
!allUnionTypesFlags.includes(ts.TypeFlags.Void) &&
!def.hasOwnProperty("ignore")) {
required.push(prop.getName());
}
return required;
}, []);
if (requiredProps.length > 0) {
definition.required = unique(requiredProps).sort();
}
}
}
return definition;
};
JsonSchemaGenerator.prototype.getTypeName = function (typ) {
var id = typ.id;
if (this.typeNamesById[id]) {
return this.typeNamesById[id];
}
return this.makeTypeNameUnique(typ, this.tc
.typeToString(typ, undefined, ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.UseFullyQualifiedType)
.replace(REGEX_FILE_NAME_OR_SPACE, ""));
};
JsonSchemaGenerator.prototype.makeTypeNameUnique = function (typ, baseName) {
var id = typ.id;
var name = baseName;
for (var i = 1; this.typeIdsByName[name] !== undefined && this.typeIdsByName[name] !== id; ++i) {
name = baseName + "_" + i;
}
this.typeNamesById[id] = name;
this.typeIdsByName[name] = id;
return name;
};
JsonSchemaGenerator.prototype.getTypeDefinition = function (typ, asRef, unionModifier, prop, reffedType, pairedSymbol, forceNotRef, ignoreUndefined) {
var _a;
if (asRef === void 0) { asRef = this.args.ref; }
if (unionModifier === void 0) { unionModifier = "anyOf"; }
if (forceNotRef === void 0) { forceNotRef = false; }
if (ignoreUndefined === void 0) { ignoreUndefined = false; }
var definition = {};
while (typ.aliasSymbol &&
(typ.aliasSymbol.escapedName === "Readonly" || typ.aliasSymbol.escapedName === "Mutable") &&
typ.aliasTypeArguments &&
typ.aliasTypeArguments[0]) {
typ = typ.aliasTypeArguments[0];
reffedType = undefined;
}
if (this.args.typeOfKeyword &&
typ.flags & ts.TypeFlags.Object &&
typ.objectFlags & ts.ObjectFlags.Anonymous) {
definition.typeof = "function";
return definition;
}
var returnedDefinition = definition;
if (prop) {
var defs = {};
var others = {};
this.parseCommentsIntoDefinition(prop, defs, others);
if (defs.hasOwnProperty("ignore") || defs.hasOwnProperty("type")) {
return defs;
}
}
var symbol = typ.getSymbol();
var isRawType = !symbol ||
(this.tc.getFullyQualifiedName(symbol) !== "Window" &&
(this.tc.getFullyQualifiedName(symbol) === "Date" ||
symbol.name === "integer" ||
this.tc.getIndexInfoOfType(typ, ts.IndexKind.Number) !== undefined));
if (isRawType && ((_a = typ.aliasSymbol) === null || _a === void 0 ? void 0 : _a.escapedName) && typ.types) {
isRawType = false;
}
var isStringEnum = false;
if (typ.flags & ts.TypeFlags.Union) {
var unionType = typ;
isStringEnum = unionType.types.every(function (propType) {
return (propType.getFlags() & ts.TypeFlags.StringLiteral) !== 0;
});
}
var asTypeAliasRef = asRef && reffedType && (this.args.aliasRef || isStringEnum);
if (!asTypeAliasRef) {
if (isRawType ||
(typ.getFlags() & ts.TypeFlags.Object && typ.objectFlags & ts.ObjectFlags.Anonymous)) {
asRef = false;
}
}
var fullTypeName = "";
if (asTypeAliasRef) {
var typeName = this.tc
.getFullyQualifiedName(reffedType.getFlags() & ts.SymbolFlags.Alias ? this.tc.getAliasedSymbol(reffedType) : reffedType)
.replace(REGEX_FILE_NAME_OR_SPACE, "");
if (this.args.uniqueNames && reffedType) {
var sourceFile = getSourceFile(reffedType);
var relativePath = path.relative(process.cwd(), sourceFile.fileName);
fullTypeName = "".concat(typeName, ".").concat(generateHashOfNode(getCanonicalDeclaration(reffedType), relativePath));
}
else {
fullTypeName = this.makeTypeNameUnique(typ, typeName);
}
}
else {
if (this.args.uniqueNames && typ.symbol) {
var sym = typ.symbol;
var sourceFile = getSourceFile(sym);
var relativePath = path.relative(process.cwd(), sourceFile.fileName);
fullTypeName = "".concat(this.getTypeName(typ), ".").concat(generateHashOfNode(getCanonicalDeclaration(sym), relativePath));
}
else if (reffedType && this.schemaOverrides.has(reffedType.escapedName)) {
fullTypeName = reffedType.escapedName;
}
else {
fullTypeName = this.getTypeName(typ);
}
}
if (!isRawType || !!typ.aliasSymbol) {
if (this.recursiveTypeRef.has(fullTypeName) && !forceNotRef) {
asRef = true;
}
else {
this.recursiveTypeRef.set(fullTypeName, definition);
}
}
if (asRef) {
returnedDefinition = {
$ref: "".concat(this.args.id, "#/definitions/") + fullTypeName,
};
}
var otherAnnotations = {};
this.parseCommentsIntoDefinition(reffedType, definition, otherAnnotations);
this.parseCommentsIntoDefinition(symbol, definition, otherAnnotations);
this.parseCommentsIntoDefinition(typ.aliasSymbol, definition, otherAnnotations);
if (prop) {
this.parseCommentsIntoDefinition(prop, returnedDefinition, otherAnnotations);
}
if (pairedSymbol && symbol && this.isFromDefaultLib(symbol)) {
this.parseCommentsIntoDefinition(pairedSymbol, definition, otherAnnotations);
}
var overrideDefinition = this.schemaOverrides.get(fullTypeName);
if (overrideDefinition) {
this.reffedDefinitions[fullTypeName] = overrideDefinition;
}
else if (!asRef || !this.reffedDefinitions[fullTypeName]) {
if (asRef) {
var reffedDefinition = void 0;
if (asTypeAliasRef && reffedType && typ.symbol !== reffedType && symbol) {
reffedDefinition = this.getTypeDefinition(typ, true, undefined, symbol, symbol);
}
else {
reffedDefinition = definition;
}
this.reffedDefinitions[fullTypeName] = reffedDefinition;
if (this.args.titles && fullTypeName) {
definition.title = fullTypeName;
}
}
var node = (symbol === null || symbol === void 0 ? void 0 : symbol.getDeclarations()) !== undefined ? symbol.getDeclarations()[0] : null;
if (definition.type === undefined) {
if (typ.flags & ts.TypeFlags.Union && (node === null || node.kind !== ts.SyntaxKind.EnumDeclaration)) {
this.getUnionDefinition(typ, unionModifier, definition);
}
else if (typ.flags & ts.TypeFlags.Intersection) {
if (this.args.noExtraProps) {
if (this.args.noExtraProps) {
definition.additionalProperties = false;
}
var types = typ.types;
for (var _i = 0, types_1 = types; _i < types_1.length; _i++) {
var member = types_1[_i];
var other = this.getTypeDefinition(member, false, undefined, undefined, undefined, undefined, true);
definition.type = other.type;
definition.properties = __assign(__assign({}, definition.properties), other.properties);
if (Object.keys(other.default || {}).length > 0) {
definition.default = extend(definition.default || {}, other.default);
}
if (other.required) {
definition.required = unique((definition.required || []).concat(other.required)).sort();
}
}
}
else {
this.getIntersectionDefinition(typ, definition);
}
}
else if (isRawType) {
if (pairedSymbol) {
this.parseCommentsIntoDefinition(pairedSymbol, definition, {});
}
this.getDefinitionForRootType(typ, reffedType, definition, undefined, ignoreUndefined);
}
else if (node &&
(node.kind === ts.SyntaxKind.EnumDeclaration || node.kind === ts.SyntaxKind.EnumMember)) {
this.getEnumDefinition(typ, definition);
}
else if (symbol &&
symbol.flags & ts.SymbolFlags.TypeLiteral &&
symbol.members.size === 0 &&
!(node && node.kind === ts.SyntaxKind.MappedType)) {
definition.type = "object";
definition.properties = {};
}
else {
this.getClassDefinition(typ, definition);
}
}
}
if (this.recursiveTypeRef.get(fullTypeName) === definition) {
this.recursiveTypeRef.delete(fullTypeName);
if (this.reffedDefinitions[fullTypeName]) {
var annotations = Object.entries(returnedDefinition).reduce(function (acc, _a) {
var key = _a[0], value = _a[1];
if (annotationKeywords[key] && typeof value !== undefined) {
acc[key] = value;
}
return acc;
}, {});
returnedDefinition = __assign({ $ref: ""