UNPKG

42.7 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13Object.defineProperty(exports, "__esModule", { value: true });
14var glob = require("glob");
15var stringify = require("json-stable-stringify");
16var path = require("path");
17var crypto_1 = require("crypto");
18var ts = require("typescript");
19var vm = require("vm");
20var REGEX_FILE_NAME_OR_SPACE = /(\bimport\(".*?"\)|".*?")\.| /g;
21var REGEX_TSCONFIG_NAME = /^.*\.json$/;
22var REGEX_TJS_JSDOC = /^-([\w]+)\s+(\S|\S[\s\S]*\S)\s*$/g;
23var NUMERIC_INDEX_PATTERN = "^[0-9]+$";
24function getDefaultArgs() {
25 return {
26 ref: true,
27 aliasRef: false,
28 topRef: false,
29 titles: false,
30 defaultProps: false,
31 noExtraProps: false,
32 propOrder: false,
33 typeOfKeyword: false,
34 required: false,
35 strictNullChecks: false,
36 ignoreErrors: false,
37 out: "",
38 validationKeywords: [],
39 include: [],
40 excludePrivate: false,
41 uniqueNames: false,
42 rejectDateType: false,
43 id: "",
44 };
45}
46exports.getDefaultArgs = getDefaultArgs;
47function extend(target) {
48 var _ = [];
49 for (var _i = 1; _i < arguments.length; _i++) {
50 _[_i - 1] = arguments[_i];
51 }
52 if (target == null) {
53 throw new TypeError("Cannot convert undefined or null to object");
54 }
55 var to = Object(target);
56 for (var index = 1; index < arguments.length; index++) {
57 var nextSource = arguments[index];
58 if (nextSource != null) {
59 for (var nextKey in nextSource) {
60 if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
61 to[nextKey] = nextSource[nextKey];
62 }
63 }
64 }
65 }
66 return to;
67}
68function unique(arr) {
69 var temp = {};
70 for (var _i = 0, arr_1 = arr; _i < arr_1.length; _i++) {
71 var e = arr_1[_i];
72 temp[e] = true;
73 }
74 var r = [];
75 for (var k in temp) {
76 if (Object.prototype.hasOwnProperty.call(temp, k)) {
77 r.push(k);
78 }
79 }
80 return r;
81}
82function parseValue(value) {
83 try {
84 return JSON.parse(value);
85 }
86 catch (error) {
87 return value;
88 }
89}
90function extractLiteralValue(typ) {
91 var str = typ.value;
92 if (str === undefined) {
93 str = typ.text;
94 }
95 if (typ.flags & ts.TypeFlags.StringLiteral) {
96 return str;
97 }
98 else if (typ.flags & ts.TypeFlags.BooleanLiteral) {
99 return typ.intrinsicName === "true";
100 }
101 else if (typ.flags & ts.TypeFlags.EnumLiteral) {
102 var num = parseFloat(str);
103 return isNaN(num) ? str : num;
104 }
105 else if (typ.flags & ts.TypeFlags.NumberLiteral) {
106 return parseFloat(str);
107 }
108 return undefined;
109}
110function resolveTupleType(propertyType) {
111 if (!propertyType.getSymbol() && (propertyType.getFlags() & ts.TypeFlags.Object && propertyType.objectFlags & ts.ObjectFlags.Reference)) {
112 return propertyType.target;
113 }
114 if (!(propertyType.getFlags() & ts.TypeFlags.Object && propertyType.objectFlags & ts.ObjectFlags.Tuple)) {
115 return null;
116 }
117 return propertyType;
118}
119var simpleTypesAllowedProperties = {
120 type: true,
121 description: true
122};
123function addSimpleType(def, type) {
124 for (var k in def) {
125 if (!simpleTypesAllowedProperties[k]) {
126 return false;
127 }
128 }
129 if (!def.type) {
130 def.type = type;
131 }
132 else if (typeof def.type !== "string") {
133 if (!def.type.every(function (val) { return typeof val === "string"; })) {
134 return false;
135 }
136 if (def.type.indexOf("null") === -1) {
137 def.type.push("null");
138 }
139 }
140 else {
141 if (typeof def.type !== "string") {
142 return false;
143 }
144 if (def.type !== "null") {
145 def.type = [def.type, "null"];
146 }
147 }
148 return true;
149}
150function makeNullable(def) {
151 if (!addSimpleType(def, "null")) {
152 var union = def.oneOf || def.anyOf;
153 if (union) {
154 union.push({ type: "null" });
155 }
156 else {
157 var subdef = {};
158 for (var k in def) {
159 if (def.hasOwnProperty(k)) {
160 subdef[k] = def[k];
161 delete def[k];
162 }
163 }
164 def.anyOf = [subdef, { type: "null" }];
165 }
166 }
167 return def;
168}
169var validationKeywords = {
170 multipleOf: true,
171 maximum: true,
172 exclusiveMaximum: true,
173 minimum: true,
174 exclusiveMinimum: true,
175 maxLength: true,
176 minLength: true,
177 pattern: true,
178 maxItems: true,
179 minItems: true,
180 uniqueItems: true,
181 maxProperties: true,
182 minProperties: true,
183 additionalProperties: true,
184 enum: true,
185 type: true,
186 examples: true,
187 ignore: true,
188 description: true,
189 format: true,
190 default: true,
191 $ref: true,
192 id: true
193};
194var JsonSchemaGenerator = (function () {
195 function JsonSchemaGenerator(symbols, allSymbols, userSymbols, inheritingTypes, tc, args) {
196 if (args === void 0) { args = getDefaultArgs(); }
197 this.args = args;
198 this.reffedDefinitions = {};
199 this.typeNamesById = {};
200 this.typeIdsByName = {};
201 this.symbols = symbols;
202 this.allSymbols = allSymbols;
203 this.userSymbols = userSymbols;
204 this.inheritingTypes = inheritingTypes;
205 this.tc = tc;
206 this.userValidationKeywords = args.validationKeywords.reduce(function (acc, word) {
207 var _a;
208 return (__assign({}, acc, (_a = {}, _a[word] = true, _a)));
209 }, {});
210 }
211 Object.defineProperty(JsonSchemaGenerator.prototype, "ReffedDefinitions", {
212 get: function () {
213 return this.reffedDefinitions;
214 },
215 enumerable: true,
216 configurable: true
217 });
218 JsonSchemaGenerator.prototype.parseCommentsIntoDefinition = function (symbol, definition, otherAnnotations) {
219 var _this = this;
220 if (!symbol) {
221 return;
222 }
223 var comments = symbol.getDocumentationComment(this.tc);
224 if (comments.length) {
225 definition.description = comments.map(function (comment) { return comment.kind === "lineBreak" ? comment.text : comment.text.trim().replace(/\r\n/g, "\n"); }).join("");
226 }
227 var jsdocs = symbol.getJsDocTags();
228 jsdocs.forEach(function (doc) {
229 var _a = (doc.name === "TJS" ? new RegExp(REGEX_TJS_JSDOC).exec(doc.text).slice(1, 3) : [doc.name, doc.text]), name = _a[0], text = _a[1];
230 if (validationKeywords[name] || _this.userValidationKeywords[name]) {
231 definition[name] = text === undefined ? "" : parseValue(text);
232 }
233 else {
234 otherAnnotations[doc.name] = true;
235 }
236 });
237 };
238 JsonSchemaGenerator.prototype.getDefinitionForRootType = function (propertyType, reffedType, definition) {
239 var _this = this;
240 var _a;
241 var tupleType = resolveTupleType(propertyType);
242 if (tupleType) {
243 var elemTypes = tupleType.elementTypes || propertyType.typeArguments;
244 var fixedTypes = elemTypes.map(function (elType) { return _this.getTypeDefinition(elType); });
245 definition.type = "array";
246 definition.items = fixedTypes;
247 var targetTupleType = propertyType.target;
248 definition.minItems = targetTupleType.minLength;
249 if (targetTupleType.hasRestElement) {
250 definition.additionalItems = fixedTypes[fixedTypes.length - 1];
251 fixedTypes.splice(fixedTypes.length - 1, 1);
252 }
253 else {
254 definition.additionalItems = {
255 anyOf: fixedTypes
256 };
257 }
258 }
259 else {
260 var propertyTypeString = this.tc.typeToString(propertyType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
261 var flags = propertyType.flags;
262 var arrayType = this.tc.getIndexTypeOfType(propertyType, ts.IndexKind.Number);
263 if (flags & ts.TypeFlags.String) {
264 definition.type = "string";
265 }
266 else if (flags & ts.TypeFlags.Number) {
267 var isInteger = (definition.type === "integer" || (reffedType && reffedType.getName() === "integer"));
268 definition.type = isInteger ? "integer" : "number";
269 }
270 else if (flags & ts.TypeFlags.Boolean) {
271 definition.type = "boolean";
272 }
273 else if (flags & ts.TypeFlags.Null) {
274 definition.type = "null";
275 }
276 else if (flags & ts.TypeFlags.Undefined) {
277 definition.type = "undefined";
278 }
279 else if ((flags & ts.TypeFlags.Any) || (flags & ts.TypeFlags.Unknown)) {
280 }
281 else if (propertyTypeString === "Date" && !this.args.rejectDateType) {
282 definition.type = "string";
283 definition.format = "date-time";
284 }
285 else if (propertyTypeString === "object") {
286 definition.type = "object";
287 definition.properties = {};
288 definition.additionalProperties = true;
289 }
290 else {
291 var value = extractLiteralValue(propertyType);
292 if (value !== undefined) {
293 definition.type = typeof value;
294 definition.enum = [value];
295 }
296 else if (arrayType !== undefined) {
297 if ((propertyType.flags & ts.TypeFlags.Object) &&
298 (propertyType.objectFlags & (ts.ObjectFlags.Anonymous | ts.ObjectFlags.Interface | ts.ObjectFlags.Mapped))) {
299 definition.type = "object";
300 definition.additionalProperties = false;
301 definition.patternProperties = (_a = {},
302 _a[NUMERIC_INDEX_PATTERN] = this.getTypeDefinition(arrayType),
303 _a);
304 }
305 else {
306 definition.type = "array";
307 definition.items = this.getTypeDefinition(arrayType);
308 }
309 }
310 else {
311 var error = new TypeError("Unsupported type: " + propertyTypeString);
312 error.type = propertyType;
313 throw error;
314 }
315 }
316 }
317 return definition;
318 };
319 JsonSchemaGenerator.prototype.getReferencedTypeSymbol = function (prop) {
320 var decl = prop.getDeclarations();
321 if (decl && decl.length) {
322 var type = decl[0].type;
323 if (type && (type.kind & ts.SyntaxKind.TypeReference) && type.typeName) {
324 var symbol = this.tc.getSymbolAtLocation(type.typeName);
325 if (symbol && (symbol.flags & ts.SymbolFlags.Alias)) {
326 return this.tc.getAliasedSymbol(symbol);
327 }
328 return symbol;
329 }
330 }
331 return undefined;
332 };
333 JsonSchemaGenerator.prototype.getDefinitionForProperty = function (prop, node) {
334 if (prop.flags & ts.SymbolFlags.Method) {
335 return null;
336 }
337 var propertyName = prop.getName();
338 var propertyType = this.tc.getTypeOfSymbolAtLocation(prop, node);
339 var reffedType = this.getReferencedTypeSymbol(prop);
340 var definition = this.getTypeDefinition(propertyType, undefined, undefined, prop, reffedType);
341 if (this.args.titles) {
342 definition.title = propertyName;
343 }
344 if (definition.hasOwnProperty("ignore")) {
345 return null;
346 }
347 var valDecl = prop.valueDeclaration;
348 if (valDecl && valDecl.initializer) {
349 var initial = valDecl.initializer;
350 while (ts.isTypeAssertion(initial)) {
351 initial = initial.expression;
352 }
353 if (initial.expression) {
354 console.warn("initializer is expression for property " + propertyName);
355 }
356 else if (initial.kind && initial.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
357 definition.default = initial.getText();
358 }
359 else {
360 try {
361 var sandbox = { sandboxvar: null };
362 vm.runInNewContext("sandboxvar=" + initial.getText(), sandbox);
363 var val = sandbox.sandboxvar;
364 if (val === null || typeof val === "string" || typeof val === "number" || typeof val === "boolean" || Object.prototype.toString.call(val) === "[object Array]") {
365 definition.default = val;
366 }
367 else if (val) {
368 console.warn("unknown initializer for property " + propertyName + ": " + val);
369 }
370 }
371 catch (e) {
372 console.warn("exception evaluating initializer for property " + propertyName);
373 }
374 }
375 }
376 return definition;
377 };
378 JsonSchemaGenerator.prototype.getEnumDefinition = function (clazzType, definition) {
379 var _this = this;
380 var node = clazzType.getSymbol().getDeclarations()[0];
381 var fullName = this.tc.typeToString(clazzType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
382 var members = node.kind === ts.SyntaxKind.EnumDeclaration ?
383 node.members :
384 ts.createNodeArray([node]);
385 var enumValues = [];
386 var enumTypes = [];
387 var addType = function (type) {
388 if (enumTypes.indexOf(type) === -1) {
389 enumTypes.push(type);
390 }
391 };
392 members.forEach(function (member) {
393 var caseLabel = member.name.text;
394 var constantValue = _this.tc.getConstantValue(member);
395 if (constantValue !== undefined) {
396 enumValues.push(constantValue);
397 addType(typeof constantValue);
398 }
399 else {
400 var initial = member.initializer;
401 if (initial) {
402 if (initial.expression) {
403 var exp = initial.expression;
404 var text = exp.text;
405 if (text) {
406 enumValues.push(text);
407 addType("string");
408 }
409 else if (exp.kind === ts.SyntaxKind.TrueKeyword || exp.kind === ts.SyntaxKind.FalseKeyword) {
410 enumValues.push((exp.kind === ts.SyntaxKind.TrueKeyword));
411 addType("boolean");
412 }
413 else {
414 console.warn("initializer is expression for enum: " + fullName + "." + caseLabel);
415 }
416 }
417 else if (initial.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
418 enumValues.push(initial.getText());
419 addType("string");
420 }
421 else if (initial.kind === ts.SyntaxKind.NullKeyword) {
422 enumValues.push(null);
423 addType("null");
424 }
425 }
426 }
427 });
428 if (enumTypes.length) {
429 definition.type = (enumTypes.length === 1) ? enumTypes[0] : enumTypes;
430 }
431 if (enumValues.length > 0) {
432 definition.enum = enumValues.sort();
433 }
434 return definition;
435 };
436 JsonSchemaGenerator.prototype.getUnionDefinition = function (unionType, prop, unionModifier, definition) {
437 var enumValues = [];
438 var simpleTypes = [];
439 var schemas = [];
440 var pushSimpleType = function (type) {
441 if (simpleTypes.indexOf(type) === -1) {
442 simpleTypes.push(type);
443 }
444 };
445 var pushEnumValue = function (val) {
446 if (enumValues.indexOf(val) === -1) {
447 enumValues.push(val);
448 }
449 };
450 for (var _i = 0, _a = unionType.types; _i < _a.length; _i++) {
451 var valueType = _a[_i];
452 var value = extractLiteralValue(valueType);
453 if (value !== undefined) {
454 pushEnumValue(value);
455 }
456 else {
457 var def = this.getTypeDefinition(valueType);
458 if (def.type === "undefined") {
459 if (prop) {
460 prop.mayBeUndefined = true;
461 }
462 }
463 else {
464 var keys = Object.keys(def);
465 if (keys.length === 1 && keys[0] === "type") {
466 if (typeof def.type !== "string") {
467 console.error("Expected only a simple type.");
468 }
469 else {
470 pushSimpleType(def.type);
471 }
472 }
473 else {
474 schemas.push(def);
475 }
476 }
477 }
478 }
479 if (enumValues.length > 0) {
480 var isOnlyBooleans = enumValues.length === 2 &&
481 typeof enumValues[0] === "boolean" &&
482 typeof enumValues[1] === "boolean" &&
483 enumValues[0] !== enumValues[1];
484 if (isOnlyBooleans) {
485 pushSimpleType("boolean");
486 }
487 else {
488 var enumSchema = { enum: enumValues.sort() };
489 if (enumValues.every(function (x) { return typeof x === "string"; })) {
490 enumSchema.type = "string";
491 }
492 else if (enumValues.every(function (x) { return typeof x === "number"; })) {
493 enumSchema.type = "number";
494 }
495 else if (enumValues.every(function (x) { return typeof x === "boolean"; })) {
496 enumSchema.type = "boolean";
497 }
498 schemas.push(enumSchema);
499 }
500 }
501 if (simpleTypes.length > 0) {
502 schemas.push({ type: simpleTypes.length === 1 ? simpleTypes[0] : simpleTypes });
503 }
504 if (schemas.length === 1) {
505 for (var k in schemas[0]) {
506 if (schemas[0].hasOwnProperty(k)) {
507 definition[k] = schemas[0][k];
508 }
509 }
510 }
511 else {
512 definition[unionModifier] = schemas;
513 }
514 return definition;
515 };
516 JsonSchemaGenerator.prototype.getIntersectionDefinition = function (intersectionType, definition) {
517 var simpleTypes = [];
518 var schemas = [];
519 var pushSimpleType = function (type) {
520 if (simpleTypes.indexOf(type) === -1) {
521 simpleTypes.push(type);
522 }
523 };
524 for (var _i = 0, _a = intersectionType.types; _i < _a.length; _i++) {
525 var intersectionMember = _a[_i];
526 var def = this.getTypeDefinition(intersectionMember);
527 if (def.type === "undefined") {
528 console.error("Undefined in intersection makes no sense.");
529 }
530 else {
531 var keys = Object.keys(def);
532 if (keys.length === 1 && keys[0] === "type") {
533 if (typeof def.type !== "string") {
534 console.error("Expected only a simple type.");
535 }
536 else {
537 pushSimpleType(def.type);
538 }
539 }
540 else {
541 schemas.push(def);
542 }
543 }
544 }
545 if (simpleTypes.length > 0) {
546 schemas.push({ type: simpleTypes.length === 1 ? simpleTypes[0] : simpleTypes });
547 }
548 if (schemas.length === 1) {
549 for (var k in schemas[0]) {
550 if (schemas[0].hasOwnProperty(k)) {
551 definition[k] = schemas[0][k];
552 }
553 }
554 }
555 else {
556 definition.allOf = schemas;
557 }
558 return definition;
559 };
560 JsonSchemaGenerator.prototype.getClassDefinition = function (clazzType, definition) {
561 var _this = this;
562 var node = clazzType.getSymbol().getDeclarations()[0];
563 if (this.args.typeOfKeyword && node.kind === ts.SyntaxKind.FunctionType) {
564 definition.typeof = "function";
565 return definition;
566 }
567 var clazz = node;
568 var props = this.tc.getPropertiesOfType(clazzType).filter(function (prop) {
569 if (!_this.args.excludePrivate) {
570 return true;
571 }
572 var decls = prop.declarations;
573 return !(decls && decls.filter(function (decl) {
574 var mods = decl.modifiers;
575 return mods && mods.filter(function (mod) { return mod.kind === ts.SyntaxKind.PrivateKeyword; }).length > 0;
576 }).length > 0);
577 });
578 var fullName = this.tc.typeToString(clazzType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
579 var modifierFlags = ts.getCombinedModifierFlags(node);
580 if (modifierFlags & ts.ModifierFlags.Abstract && this.inheritingTypes[fullName]) {
581 var oneOf = this.inheritingTypes[fullName].map(function (typename) {
582 return _this.getTypeDefinition(_this.allSymbols[typename]);
583 });
584 definition.oneOf = oneOf;
585 }
586 else {
587 if (clazz.members) {
588 var indexSignatures = clazz.members == null ? [] : clazz.members.filter(function (x) { return x.kind === ts.SyntaxKind.IndexSignature; });
589 if (indexSignatures.length === 1) {
590 var indexSignature = indexSignatures[0];
591 if (indexSignature.parameters.length !== 1) {
592 throw new Error("Not supported: IndexSignatureDeclaration parameters.length != 1");
593 }
594 var indexSymbol = indexSignature.parameters[0].symbol;
595 var indexType = this.tc.getTypeOfSymbolAtLocation(indexSymbol, node);
596 var isStringIndexed = (indexType.flags === ts.TypeFlags.String);
597 if (indexType.flags !== ts.TypeFlags.Number && !isStringIndexed) {
598 throw new Error("Not supported: IndexSignatureDeclaration with index symbol other than a number or a string");
599 }
600 var typ = this.tc.getTypeAtLocation(indexSignature.type);
601 var def = this.getTypeDefinition(typ, undefined, "anyOf");
602 if (isStringIndexed) {
603 definition.type = "object";
604 definition.additionalProperties = def;
605 }
606 else {
607 definition.type = "array";
608 definition.items = def;
609 }
610 }
611 }
612 var propertyDefinitions = props.reduce(function (all, prop) {
613 var propertyName = prop.getName();
614 var propDef = _this.getDefinitionForProperty(prop, node);
615 if (propDef != null) {
616 all[propertyName] = propDef;
617 }
618 return all;
619 }, {});
620 if (definition.type === undefined) {
621 definition.type = "object";
622 }
623 if (definition.type === "object" && Object.keys(propertyDefinitions).length > 0) {
624 definition.properties = propertyDefinitions;
625 }
626 if (this.args.defaultProps) {
627 definition.defaultProperties = [];
628 }
629 if (this.args.noExtraProps && definition.additionalProperties === undefined) {
630 definition.additionalProperties = false;
631 }
632 if (this.args.propOrder) {
633 var propertyOrder = props.reduce(function (order, prop) {
634 order.push(prop.getName());
635 return order;
636 }, []);
637 definition.propertyOrder = propertyOrder;
638 }
639 if (this.args.required) {
640 var requiredProps = props.reduce(function (required, prop) {
641 var def = {};
642 _this.parseCommentsIntoDefinition(prop, def, {});
643 if (!(prop.flags & ts.SymbolFlags.Optional) && !(prop.flags & ts.SymbolFlags.Method) && !prop.mayBeUndefined && !def.hasOwnProperty("ignore")) {
644 required.push(prop.getName());
645 }
646 return required;
647 }, []);
648 if (requiredProps.length > 0) {
649 definition.required = unique(requiredProps).sort();
650 }
651 }
652 }
653 return definition;
654 };
655 JsonSchemaGenerator.prototype.getTypeName = function (typ) {
656 var id = typ.id;
657 if (this.typeNamesById[id]) {
658 return this.typeNamesById[id];
659 }
660 return this.makeTypeNameUnique(typ, this.tc.typeToString(typ, undefined, ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.UseFullyQualifiedType).replace(REGEX_FILE_NAME_OR_SPACE, ""));
661 };
662 JsonSchemaGenerator.prototype.makeTypeNameUnique = function (typ, baseName) {
663 var id = typ.id;
664 var name = baseName;
665 for (var i = 1; this.typeIdsByName[name] !== undefined && this.typeIdsByName[name] !== id; ++i) {
666 name = baseName + "_" + i;
667 }
668 this.typeNamesById[id] = name;
669 this.typeIdsByName[name] = id;
670 return name;
671 };
672 JsonSchemaGenerator.prototype.getTypeDefinition = function (typ, asRef, unionModifier, prop, reffedType, pairedSymbol) {
673 if (asRef === void 0) { asRef = this.args.ref; }
674 if (unionModifier === void 0) { unionModifier = "anyOf"; }
675 var definition = {};
676 while (typ.aliasSymbol && (typ.aliasSymbol.escapedName === "Readonly" || typ.aliasSymbol.escapedName === "Mutable") && typ.aliasTypeArguments && typ.aliasTypeArguments[0]) {
677 typ = typ.aliasTypeArguments[0];
678 reffedType = undefined;
679 }
680 if (this.args.typeOfKeyword && (typ.flags & ts.TypeFlags.Object) && (typ.objectFlags & ts.ObjectFlags.Anonymous)) {
681 definition.typeof = "function";
682 return definition;
683 }
684 var returnedDefinition = definition;
685 var symbol = typ.getSymbol();
686 var isRawType = (!symbol || this.tc.getFullyQualifiedName(symbol) === "Date" || symbol.name === "integer" || this.tc.getIndexInfoOfType(typ, ts.IndexKind.Number) !== undefined);
687 var isStringEnum = false;
688 if (typ.flags & ts.TypeFlags.Union) {
689 var unionType = typ;
690 isStringEnum = (unionType.types.every(function (propType) {
691 return (propType.getFlags() & ts.TypeFlags.StringLiteral) !== 0;
692 }));
693 }
694 var asTypeAliasRef = asRef && reffedType && (this.args.aliasRef || isStringEnum);
695 if (!asTypeAliasRef) {
696 if (isRawType || typ.getFlags() & ts.TypeFlags.Object && typ.objectFlags & ts.ObjectFlags.Anonymous) {
697 asRef = false;
698 }
699 }
700 var fullTypeName = "";
701 if (asTypeAliasRef) {
702 fullTypeName = this.makeTypeNameUnique(typ, this.tc.getFullyQualifiedName(reffedType.getFlags() & ts.SymbolFlags.Alias ?
703 this.tc.getAliasedSymbol(reffedType) :
704 reffedType).replace(REGEX_FILE_NAME_OR_SPACE, ""));
705 }
706 else if (asRef) {
707 fullTypeName = this.getTypeName(typ);
708 }
709 if (asRef) {
710 returnedDefinition = {
711 $ref: this.args.id + "#/definitions/" + fullTypeName
712 };
713 }
714 var otherAnnotations = {};
715 this.parseCommentsIntoDefinition(reffedType, definition, otherAnnotations);
716 this.parseCommentsIntoDefinition(symbol, definition, otherAnnotations);
717 if (prop) {
718 this.parseCommentsIntoDefinition(prop, returnedDefinition, otherAnnotations);
719 }
720 if (!asRef || !this.reffedDefinitions[fullTypeName]) {
721 if (asRef) {
722 var reffedDefinition = void 0;
723 if (asTypeAliasRef && reffedType && typ.symbol !== reffedType && symbol) {
724 reffedDefinition = this.getTypeDefinition(typ, true, undefined, symbol, symbol);
725 }
726 else {
727 reffedDefinition = definition;
728 }
729 this.reffedDefinitions[fullTypeName] = reffedDefinition;
730 if (this.args.titles && fullTypeName) {
731 definition.title = fullTypeName;
732 }
733 }
734 var node = symbol && symbol.getDeclarations() !== undefined ? symbol.getDeclarations()[0] : null;
735 if (definition.type === undefined) {
736 if (typ.flags & ts.TypeFlags.Union) {
737 this.getUnionDefinition(typ, prop, unionModifier, definition);
738 }
739 else if (typ.flags & ts.TypeFlags.Intersection) {
740 if (this.args.noExtraProps) {
741 if (this.args.noExtraProps) {
742 definition.additionalProperties = false;
743 }
744 var types = typ.types;
745 for (var _i = 0, types_1 = types; _i < types_1.length; _i++) {
746 var member = types_1[_i];
747 var other = this.getTypeDefinition(member, false);
748 definition.type = other.type;
749 definition.properties = extend(definition.properties || {}, other.properties);
750 if (Object.keys(other.default || {}).length > 0) {
751 definition.default = extend(definition.default || {}, other.default);
752 }
753 if (other.required) {
754 definition.required = unique((definition.required || []).concat(other.required)).sort();
755 }
756 }
757 }
758 else {
759 this.getIntersectionDefinition(typ, definition);
760 }
761 }
762 else if (isRawType) {
763 if (pairedSymbol) {
764 this.parseCommentsIntoDefinition(pairedSymbol, definition, {});
765 }
766 this.getDefinitionForRootType(typ, reffedType, definition);
767 }
768 else if (node && (node.kind === ts.SyntaxKind.EnumDeclaration || node.kind === ts.SyntaxKind.EnumMember)) {
769 this.getEnumDefinition(typ, definition);
770 }
771 else if (symbol && symbol.flags & ts.SymbolFlags.TypeLiteral && symbol.members.size === 0 && !(node && (node.kind === ts.SyntaxKind.MappedType))) {
772 definition.type = "object";
773 definition.properties = {};
774 }
775 else {
776 this.getClassDefinition(typ, definition);
777 }
778 }
779 }
780 if (otherAnnotations["nullable"]) {
781 makeNullable(returnedDefinition);
782 }
783 return returnedDefinition;
784 };
785 JsonSchemaGenerator.prototype.setSchemaOverride = function (symbolName, schema) {
786 this.reffedDefinitions[symbolName] = schema;
787 };
788 JsonSchemaGenerator.prototype.getSchemaForSymbol = function (symbolName, includeReffedDefinitions) {
789 if (includeReffedDefinitions === void 0) { includeReffedDefinitions = true; }
790 if (!this.allSymbols[symbolName]) {
791 throw new Error("type " + symbolName + " not found");
792 }
793 var def = this.getTypeDefinition(this.allSymbols[symbolName], this.args.topRef, undefined, undefined, undefined, this.userSymbols[symbolName] || undefined);
794 if (this.args.ref && includeReffedDefinitions && Object.keys(this.reffedDefinitions).length > 0) {
795 def.definitions = this.reffedDefinitions;
796 }
797 def["$schema"] = "http://json-schema.org/draft-07/schema#";
798 var id = this.args.id;
799 if (id) {
800 def["$id"] = this.args.id;
801 }
802 return def;
803 };
804 JsonSchemaGenerator.prototype.getSchemaForSymbols = function (symbolNames, includeReffedDefinitions) {
805 if (includeReffedDefinitions === void 0) { includeReffedDefinitions = true; }
806 var root = {
807 $schema: "http://json-schema.org/draft-07/schema#",
808 definitions: {}
809 };
810 var id = this.args.id;
811 if (id) {
812 root["$id"] = id;
813 }
814 for (var _i = 0, symbolNames_1 = symbolNames; _i < symbolNames_1.length; _i++) {
815 var symbolName = symbolNames_1[_i];
816 root.definitions[symbolName] = this.getTypeDefinition(this.allSymbols[symbolName], this.args.topRef, undefined, undefined, undefined, this.userSymbols[symbolName]);
817 }
818 if (this.args.ref && includeReffedDefinitions && Object.keys(this.reffedDefinitions).length > 0) {
819 root.definitions = __assign({}, root.definitions, this.reffedDefinitions);
820 }
821 return root;
822 };
823 JsonSchemaGenerator.prototype.getSymbols = function (name) {
824 if (name === void 0) {
825 return this.symbols;
826 }
827 return this.symbols.filter(function (symbol) { return symbol.typeName === name; });
828 };
829 JsonSchemaGenerator.prototype.getUserSymbols = function () {
830 return Object.keys(this.userSymbols);
831 };
832 JsonSchemaGenerator.prototype.getMainFileSymbols = function (program, onlyIncludeFiles) {
833 var _this = this;
834 function includeFile(file) {
835 if (onlyIncludeFiles === undefined) {
836 return !file.isDeclarationFile;
837 }
838 return onlyIncludeFiles.indexOf(file.fileName) >= 0;
839 }
840 var files = program.getSourceFiles().filter(includeFile);
841 if (files.length) {
842 return Object.keys(this.userSymbols).filter(function (key) {
843 var symbol = _this.userSymbols[key];
844 if (!symbol || !symbol.declarations || !symbol.declarations.length) {
845 return false;
846 }
847 var node = symbol.declarations[0];
848 while (node && node.parent) {
849 node = node.parent;
850 }
851 return files.indexOf(node.getSourceFile()) > -1;
852 });
853 }
854 return [];
855 };
856 return JsonSchemaGenerator;
857}());
858exports.JsonSchemaGenerator = JsonSchemaGenerator;
859function getProgramFromFiles(files, jsonCompilerOptions, basePath) {
860 if (jsonCompilerOptions === void 0) { jsonCompilerOptions = {}; }
861 if (basePath === void 0) { basePath = "./"; }
862 var compilerOptions = ts.convertCompilerOptionsFromJson(jsonCompilerOptions, basePath).options;
863 var options = {
864 noEmit: true, emitDecoratorMetadata: true, experimentalDecorators: true, target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS, allowUnusedLabels: true,
865 };
866 for (var k in compilerOptions) {
867 if (compilerOptions.hasOwnProperty(k)) {
868 options[k] = compilerOptions[k];
869 }
870 }
871 return ts.createProgram(files, options);
872}
873exports.getProgramFromFiles = getProgramFromFiles;
874function generateHashOfNode(node, relativePath) {
875 return crypto_1.createHash("md5").update(relativePath).update(node.pos.toString()).digest("hex").substring(0, 8);
876}
877function buildGenerator(program, args, onlyIncludeFiles) {
878 if (args === void 0) { args = {}; }
879 function isUserFile(file) {
880 if (onlyIncludeFiles === undefined) {
881 return !file.hasNoDefaultLib;
882 }
883 return onlyIncludeFiles.indexOf(file.fileName) >= 0;
884 }
885 var settings = getDefaultArgs();
886 for (var pref in args) {
887 if (args.hasOwnProperty(pref)) {
888 settings[pref] = args[pref];
889 }
890 }
891 var diagnostics = [];
892 if (!args.ignoreErrors) {
893 diagnostics = ts.getPreEmitDiagnostics(program);
894 }
895 if (diagnostics.length === 0) {
896 var typeChecker_1 = program.getTypeChecker();
897 var symbols_1 = [];
898 var allSymbols_1 = {};
899 var userSymbols_1 = {};
900 var inheritingTypes_1 = {};
901 var workingDir_1 = program.getCurrentDirectory();
902 program.getSourceFiles().forEach(function (sourceFile, _sourceFileIdx) {
903 var relativePath = path.relative(workingDir_1, sourceFile.fileName);
904 function inspect(node, tc) {
905 if (node.kind === ts.SyntaxKind.ClassDeclaration
906 || node.kind === ts.SyntaxKind.InterfaceDeclaration
907 || node.kind === ts.SyntaxKind.EnumDeclaration
908 || node.kind === ts.SyntaxKind.TypeAliasDeclaration) {
909 var symbol = node.symbol;
910 var nodeType = tc.getTypeAtLocation(node);
911 var fullyQualifiedName = tc.getFullyQualifiedName(symbol);
912 var typeName = fullyQualifiedName.replace(/".*"\./, "");
913 var name_1 = !args.uniqueNames ? typeName : typeName + "." + generateHashOfNode(node, relativePath);
914 symbols_1.push({ name: name_1, typeName: typeName, fullyQualifiedName: fullyQualifiedName, symbol: symbol });
915 if (!userSymbols_1[name_1]) {
916 allSymbols_1[name_1] = nodeType;
917 }
918 if (isUserFile(sourceFile)) {
919 userSymbols_1[name_1] = symbol;
920 }
921 var baseTypes = nodeType.getBaseTypes() || [];
922 baseTypes.forEach(function (baseType) {
923 var baseName = tc.typeToString(baseType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
924 if (!inheritingTypes_1[baseName]) {
925 inheritingTypes_1[baseName] = [];
926 }
927 inheritingTypes_1[baseName].push(name_1);
928 });
929 }
930 else {
931 ts.forEachChild(node, function (n) { return inspect(n, tc); });
932 }
933 }
934 inspect(sourceFile, typeChecker_1);
935 });
936 return new JsonSchemaGenerator(symbols_1, allSymbols_1, userSymbols_1, inheritingTypes_1, typeChecker_1, settings);
937 }
938 else {
939 diagnostics.forEach(function (diagnostic) {
940 var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
941 if (diagnostic.file) {
942 var _a = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start), line = _a.line, character = _a.character;
943 console.error(diagnostic.file.fileName + " (" + (line + 1) + "," + (character + 1) + "): " + message);
944 }
945 else {
946 console.error(message);
947 }
948 });
949 return null;
950 }
951}
952exports.buildGenerator = buildGenerator;
953function generateSchema(program, fullTypeName, args, onlyIncludeFiles) {
954 if (args === void 0) { args = {}; }
955 var generator = buildGenerator(program, args, onlyIncludeFiles);
956 if (generator === null) {
957 return null;
958 }
959 if (fullTypeName === "*") {
960 return generator.getSchemaForSymbols(generator.getMainFileSymbols(program, onlyIncludeFiles));
961 }
962 else {
963 return generator.getSchemaForSymbol(fullTypeName);
964 }
965}
966exports.generateSchema = generateSchema;
967function programFromConfig(configFileName, onlyIncludeFiles) {
968 var result = ts.parseConfigFileTextToJson(configFileName, ts.sys.readFile(configFileName));
969 var configObject = result.config;
970 var configParseResult = ts.parseJsonConfigFileContent(configObject, ts.sys, path.dirname(configFileName), {}, path.basename(configFileName));
971 var options = configParseResult.options;
972 options.noEmit = true;
973 delete options.out;
974 delete options.outDir;
975 delete options.outFile;
976 delete options.declaration;
977 delete options.declarationDir;
978 delete options.declarationMap;
979 var program = ts.createProgram({
980 rootNames: onlyIncludeFiles || configParseResult.fileNames,
981 options: options,
982 projectReferences: configParseResult.projectReferences
983 });
984 return program;
985}
986exports.programFromConfig = programFromConfig;
987function normalizeFileName(fn) {
988 while (fn.substr(0, 2) === "./") {
989 fn = fn.substr(2);
990 }
991 return fn;
992}
993function exec(filePattern, fullTypeName, args) {
994 var _a;
995 if (args === void 0) { args = getDefaultArgs(); }
996 var program;
997 var onlyIncludeFiles = undefined;
998 if (REGEX_TSCONFIG_NAME.test(path.basename(filePattern))) {
999 if (args.include && args.include.length > 0) {
1000 var globs = args.include.map(function (f) { return glob.sync(f); });
1001 onlyIncludeFiles = (_a = []).concat.apply(_a, globs).map(normalizeFileName);
1002 }
1003 program = programFromConfig(filePattern, onlyIncludeFiles);
1004 }
1005 else {
1006 onlyIncludeFiles = glob.sync(filePattern);
1007 program = getProgramFromFiles(onlyIncludeFiles, {
1008 strictNullChecks: args.strictNullChecks
1009 });
1010 onlyIncludeFiles = onlyIncludeFiles.map(normalizeFileName);
1011 }
1012 var definition = generateSchema(program, fullTypeName, args, onlyIncludeFiles);
1013 if (definition === null) {
1014 throw new Error("No output definition. Probably caused by errors prior to this?");
1015 }
1016 var json = stringify(definition, { space: 4 }) + "\n\n";
1017 if (args.out) {
1018 require("fs").writeFile(args.out, json, function (err) {
1019 if (err) {
1020 throw new Error("Unable to write output file: " + err.message);
1021 }
1022 });
1023 }
1024 else {
1025 process.stdout.write(json);
1026 }
1027}
1028exports.exec = exec;
1029//# sourceMappingURL=typescript-json-schema.js.map
\No newline at end of file