UNPKG

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