typescript-json-schema
Advanced tools
Comparing version 0.1.0 to 0.1.1
{ | ||
"name": "typescript-json-schema", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "typescript-json-schema generates JSON Schema files from your Typescript sources", | ||
@@ -5,0 +5,0 @@ "main": "typescript-json-schema.js", |
@@ -14,5 +14,3 @@ { | ||
"type": "array", | ||
"items": { | ||
"type": "object" | ||
} | ||
"items": {} | ||
} | ||
@@ -19,0 +17,0 @@ }, |
@@ -12,3 +12,2 @@ { | ||
"type": "string", | ||
"title": "Enum", | ||
"enum": [ | ||
@@ -15,0 +14,0 @@ "x", |
@@ -5,12 +5,15 @@ { | ||
"foo": { | ||
"$ref": "#/definitions/Enum" | ||
"$ref": "#/definitions/result" | ||
} | ||
}, | ||
"required": [ | ||
"foo" | ||
], | ||
"definitions": { | ||
"Enum": { | ||
"result": { | ||
"type": "string", | ||
"title": "Enum", | ||
"enum": [ | ||
"x", | ||
"y" | ||
"ok", | ||
"fail", | ||
"abort" | ||
] | ||
@@ -17,0 +20,0 @@ } |
@@ -42,13 +42,16 @@ import {assert} from "chai"; | ||
assertSchema("enums-string", "main.ts", "MyObject"); | ||
// not yet working | ||
// assertSchema("string-literals", "main.ts", "result"); | ||
assertSchema("string-literals", "main.ts", "MyObject"); | ||
assertSchema("array-types", "main.ts", "MyArray"); | ||
assertSchema("map-types", "main.ts", "MyObject"); | ||
assertSchema("type-union", "main.ts", "MyType"); | ||
assertSchema("type-aliases", "main.ts", "MyString"); | ||
assertSchema("type-aliases-fixed-size-array", "main.ts", "MyFixedSizeArray"); | ||
assertSchema("type-anonymous", "main.ts", "MyObject"); | ||
assertSchema("type-primitives", "main.ts", "MyObject"); | ||
assertSchema("optionals", "main.ts", "MyObject"); | ||
assertSchema("comments", "main.ts", "MyObject"); | ||
}); |
@@ -39,6 +39,3 @@ { | ||
"typings/typescript/typescript.d.ts" | ||
], | ||
"atom": { | ||
"rewriteTsconfig": true | ||
} | ||
] | ||
} |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
var ts = require("typescript"); | ||
@@ -10,7 +11,10 @@ var glob = require("glob"); | ||
useRef: true, | ||
useTypeAliasRef: false, | ||
useRootRef: false, | ||
useTitle: false, | ||
useDefaultProperties: false, | ||
disableExtraProperties: false, | ||
usePropertyOrder: false, | ||
generateRequired: false | ||
generateRequired: false, | ||
out: undefined | ||
}; | ||
@@ -77,3 +81,7 @@ } | ||
}; | ||
JsonSchemaGenerator.prototype.parseCommentsIntoDefinition = function (comments, definition) { | ||
JsonSchemaGenerator.prototype.parseCommentsIntoDefinition = function (symbol, definition) { | ||
if (!symbol) { | ||
return; | ||
} | ||
var comments = symbol.getDocumentationComment(); | ||
if (!comments || !comments.length) { | ||
@@ -86,13 +94,5 @@ return; | ||
}; | ||
JsonSchemaGenerator.prototype.getDefinitionForRootType = function (propertyType, tc, definition, unionModifier) { | ||
JsonSchemaGenerator.prototype.getDefinitionForRootType = function (propertyType, tc, reffedType, definition) { | ||
var _this = this; | ||
if (unionModifier === void 0) { unionModifier = "oneOf"; } | ||
if (propertyType.flags & ts.TypeFlags.Union) { | ||
var unionType = propertyType; | ||
var types = unionType.types.map(function (propType) { | ||
return _this.getTypeDefinition(propType, tc); | ||
}); | ||
definition[unionModifier] = types; | ||
return definition; | ||
} | ||
var symbol = propertyType.getSymbol(); | ||
var propertyTypeString = tc.typeToString(propertyType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
@@ -104,3 +104,4 @@ switch (propertyTypeString.toLowerCase()) { | ||
case "number": | ||
definition.type = "number"; | ||
var isInteger = (definition.type == "integer" || (reffedType && reffedType.getName() == "integer")); | ||
definition.type = isInteger ? "integer" : "number"; | ||
break; | ||
@@ -111,4 +112,7 @@ case "boolean": | ||
case "any": | ||
definition.type = "object"; | ||
break; | ||
case "date": | ||
definition.type = "string"; | ||
definition.format = "date-time"; | ||
break; | ||
default: | ||
@@ -125,3 +129,7 @@ if (propertyType.flags & ts.TypeFlags.Tuple) { | ||
} | ||
else if (propertyType.getSymbol().getName() == "Array") { | ||
else if (propertyType.flags & ts.TypeFlags.StringLiteral) { | ||
definition.type = "string"; | ||
definition.enum = [propertyType.text]; | ||
} | ||
else if (symbol && symbol.getName() == "Array") { | ||
var arrayType = propertyType.typeArguments[0]; | ||
@@ -132,3 +140,3 @@ definition.type = "array"; | ||
else { | ||
definition = this.getTypeDefinition(propertyType, tc); | ||
console.error("Unsupported type: ", propertyType); | ||
} | ||
@@ -138,2 +146,12 @@ } | ||
}; | ||
JsonSchemaGenerator.prototype.getReferencedTypeSymbol = function (prop, tc) { | ||
var decl = prop.getDeclarations(); | ||
if (decl && decl.length) { | ||
var type = decl[0].type; | ||
if (type && (type.kind & ts.SyntaxKind.TypeReference) && type.typeName) { | ||
return tc.getSymbolAtLocation(type.typeName); | ||
} | ||
} | ||
return null; | ||
}; | ||
JsonSchemaGenerator.prototype.getDefinitionForProperty = function (prop, tc, node) { | ||
@@ -143,8 +161,7 @@ var propertyName = prop.getName(); | ||
var propertyTypeString = tc.typeToString(propertyType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
var definition = this.getTypeDefinition(propertyType, tc); | ||
var reffedType = this.getReferencedTypeSymbol(prop, tc); | ||
var definition = this.getTypeDefinition(propertyType, tc, undefined, undefined, prop, reffedType); | ||
if (this.args.useTitle) { | ||
definition.title = propertyName; | ||
} | ||
var comments = prop.getDocumentationComment(); | ||
this.parseCommentsIntoDefinition(comments, definition); | ||
if (definition.hasOwnProperty("ignore")) { | ||
@@ -166,8 +183,6 @@ return null; | ||
initial = sandbox.sandboxvar; | ||
if (initial == null) { | ||
} | ||
else if (typeof (initial) === "string" || typeof (initial) === "number" || typeof (initial) === "boolean" || Object.prototype.toString.call(initial) === '[object Array]') { | ||
if (initial === null || typeof (initial) === "string" || typeof (initial) === "number" || typeof (initial) === "boolean" || Object.prototype.toString.call(initial) === '[object Array]') { | ||
definition.default = initial; | ||
} | ||
else { | ||
else if (initial) { | ||
console.warn("unknown initializer for property " + propertyName + ": " + initial); | ||
@@ -209,3 +224,2 @@ } | ||
definition.type = "string"; | ||
definition.title = fullName; | ||
if (enumValues.length > 0) { | ||
@@ -229,9 +243,16 @@ definition["enum"] = enumValues; | ||
var indexType = tc.getTypeOfSymbolAtLocation(indexSymbol, node); | ||
if (indexType.flags != ts.TypeFlags.Number) { | ||
throw "Not supported: IndexSignatureDeclaration with non-number index symbol"; | ||
var isStringIndexed = (indexType.flags == ts.TypeFlags.String); | ||
if (indexType.flags != ts.TypeFlags.Number && !isStringIndexed) { | ||
throw "Not supported: IndexSignatureDeclaration with index symbol other than a number or a string"; | ||
} | ||
var typ = tc.getTypeAtLocation(indexSignature.type); | ||
var def = this.getTypeDefinition(typ, tc, undefined, "anyOf"); | ||
definition.type = "array"; | ||
definition.items = def; | ||
if (isStringIndexed) { | ||
definition.type = "object"; | ||
definition.additionalProperties = def; | ||
} | ||
else { | ||
definition.type = "array"; | ||
definition.items = def; | ||
} | ||
return definition; | ||
@@ -257,8 +278,8 @@ } | ||
definition.properties = propertyDefinitions; | ||
if (this.args.useTitle) { | ||
definition.title = fullName; | ||
} | ||
if (this.args.useDefaultProperties) { | ||
definition.defaultProperties = []; | ||
} | ||
if (this.args.disableExtraProperties && definition.additionalProperties === undefined) { | ||
definition.additionalProperties = false; | ||
} | ||
if (this.args.usePropertyOrder) { | ||
@@ -284,16 +305,63 @@ var propertyOrder = props.reduce(function (order, prop) { | ||
}; | ||
JsonSchemaGenerator.prototype.getTypeDefinition = function (typ, tc, asRef, unionModifier) { | ||
JsonSchemaGenerator.prototype.getTypeDefinition = function (typ, tc, asRef, unionModifier, prop, reffedType) { | ||
var _this = this; | ||
if (asRef === void 0) { asRef = this.args.useRef; } | ||
if (unionModifier === void 0) { unionModifier = "oneOf"; } | ||
var fullName = tc.typeToString(typ, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
var definition = {}; | ||
if (!typ.getSymbol() || typ.getSymbol().name == "Array") { | ||
return this.getDefinitionForRootType(typ, tc, definition, unionModifier); | ||
var returnedDefinition = definition; | ||
var symbol = typ.getSymbol(); | ||
var isRawType = (!symbol || symbol.name == "integer" || symbol.name == "Array" || symbol.name == "Date"); | ||
var isStringEnum = false; | ||
if (typ.flags & ts.TypeFlags.Union) { | ||
var unionType = typ; | ||
isStringEnum = (unionType.types.every(function (propType, i, r) { | ||
return (propType.getFlags() & ts.TypeFlags.StringLiteral) != 0; | ||
})); | ||
} | ||
if (!asRef || !this.reffedDefinitions[fullName]) { | ||
var asTypeAliasRef = asRef && ((reffedType && this.args.useTypeAliasRef) || isStringEnum); | ||
if (!asTypeAliasRef) { | ||
if (isRawType || (typ.getFlags() & ts.TypeFlags.Anonymous)) { | ||
asRef = false; | ||
} | ||
} | ||
var fullTypeName = ""; | ||
if (asTypeAliasRef) { | ||
fullTypeName = tc.getFullyQualifiedName(reffedType); | ||
} | ||
else if (asRef) { | ||
fullTypeName = tc.typeToString(typ, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
} | ||
if (asRef) { | ||
returnedDefinition = { | ||
"$ref": "#/definitions/" + fullTypeName | ||
}; | ||
} | ||
this.parseCommentsIntoDefinition(reffedType, definition); | ||
this.parseCommentsIntoDefinition(prop || symbol, returnedDefinition); | ||
if (!asRef || !this.reffedDefinitions[fullTypeName]) { | ||
if (asRef) { | ||
this.reffedDefinitions[fullName] = definition; | ||
this.reffedDefinitions[fullTypeName] = definition; | ||
if (this.args.useTitle && fullTypeName) { | ||
definition.title = fullTypeName; | ||
} | ||
} | ||
var node = typ.getSymbol().getDeclarations()[0]; | ||
if (node.kind == ts.SyntaxKind.EnumDeclaration) { | ||
var node = symbol ? symbol.getDeclarations()[0] : null; | ||
if (typ.flags & ts.TypeFlags.Union) { | ||
var unionType = typ; | ||
if (isStringEnum) { | ||
definition.type = "string"; | ||
definition.enum = unionType.types.map(function (propType) { | ||
return propType.text; | ||
}); | ||
} | ||
else { | ||
definition[unionModifier] = unionType.types.map(function (propType) { | ||
return _this.getTypeDefinition(propType, tc); | ||
}); | ||
} | ||
} | ||
else if (isRawType) { | ||
this.getDefinitionForRootType(typ, tc, reffedType, definition); | ||
} | ||
else if (node.kind == ts.SyntaxKind.EnumDeclaration) { | ||
this.getEnumDefinition(typ, tc, definition); | ||
@@ -305,10 +373,3 @@ } | ||
} | ||
if (asRef) { | ||
return { | ||
"$ref": "#/definitions/" + fullName | ||
}; | ||
} | ||
else { | ||
return definition; | ||
} | ||
return returnedDefinition; | ||
}; | ||
@@ -334,3 +395,3 @@ JsonSchemaGenerator.prototype.getSchemaForSymbol = function (symbolName, includeReffedDefinitions) { | ||
return JsonSchemaGenerator; | ||
})(); | ||
}()); | ||
function getProgramFromFiles(files) { | ||
@@ -348,4 +409,4 @@ var options = { | ||
if (diagnostics.length == 0) { | ||
var allSymbols = {}; | ||
var inheritingTypes = {}; | ||
var allSymbols_1 = {}; | ||
var inheritingTypes_1 = {}; | ||
program.getSourceFiles().forEach(function (sourceFile) { | ||
@@ -358,12 +419,12 @@ function inspect(node, tc) { | ||
var nodeType = tc.getTypeAtLocation(node); | ||
var fullName = tc.getFullyQualifiedName(node.symbol); | ||
fullName = fullName.replace(/".*"\./, ""); | ||
allSymbols[fullName] = nodeType; | ||
var fullName_1 = tc.getFullyQualifiedName(node.symbol); | ||
fullName_1 = fullName_1.replace(/".*"\./, ""); | ||
allSymbols_1[fullName_1] = nodeType; | ||
var baseTypes = nodeType.getBaseTypes() || []; | ||
baseTypes.forEach(function (baseType) { | ||
var baseName = tc.typeToString(baseType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
if (!inheritingTypes[baseName]) { | ||
inheritingTypes[baseName] = []; | ||
if (!inheritingTypes_1[baseName]) { | ||
inheritingTypes_1[baseName] = []; | ||
} | ||
inheritingTypes[baseName].push(fullName); | ||
inheritingTypes_1[baseName].push(fullName_1); | ||
}); | ||
@@ -377,3 +438,3 @@ } | ||
}); | ||
var generator = new JsonSchemaGenerator(allSymbols, inheritingTypes, tc, args); | ||
var generator = new JsonSchemaGenerator(allSymbols_1, inheritingTypes_1, tc, args); | ||
var definition = generator.getSchemaForSymbol(fullTypeName); | ||
@@ -402,2 +463,6 @@ return definition; | ||
options.noEmit = true; | ||
delete options.out; | ||
delete options.outDir; | ||
delete options.outFile; | ||
delete options.declaration; | ||
var program = ts.createProgram(configParseResult.fileNames, options); | ||
@@ -417,3 +482,13 @@ return program; | ||
var definition = TJS.generateSchema(program, fullTypeName, args); | ||
process.stdout.write(JSON.stringify(definition, null, 4) + "\n"); | ||
var json = JSON.stringify(definition, null, 4) + "\n"; | ||
if (args.out) { | ||
require("fs").writeFile(args.out, json, function (err) { | ||
if (err) { | ||
console.error("Unable to write output file: " + err.message); | ||
} | ||
}); | ||
} | ||
else { | ||
process.stdout.write(json); | ||
} | ||
} | ||
@@ -429,2 +504,4 @@ TJS.exec = exec; | ||
.describe("refs", "Create shared ref definitions.") | ||
.boolean("aliasRefs").default("aliasRefs", defaultArgs.useTypeAliasRef) | ||
.describe("aliasRefs", "Create shared ref definitions for the type aliases.") | ||
.boolean("topRef").default("topRef", defaultArgs.useRootRef) | ||
@@ -436,2 +513,4 @@ .describe("topRef", "Create a top-level ref definition.") | ||
.describe("defaultProps", "Create default properties definitions.") | ||
.boolean("noExtraProps").default("noExtraProps", defaultArgs.disableExtraProperties) | ||
.describe("noExtraProps", "Disable additional properties in objects by default.") | ||
.boolean("propOrder").default("propOrder", defaultArgs.usePropertyOrder) | ||
@@ -441,10 +520,15 @@ .describe("propOrder", "Create property order definitions.") | ||
.describe("required", "Create required array for non-optional properties.") | ||
.alias("out", "o") | ||
.describe("out", "The output file, defaults to using stdout") | ||
.argv; | ||
exec(args._[0], args._[1], { | ||
useRef: args.refs, | ||
useTypeAliasRef: args.aliasRefs, | ||
useRootRef: args.topRef, | ||
useTitle: args.titles, | ||
useDefaultProperties: args.defaultProps, | ||
disableExtraProperties: args.noExtraProps, | ||
usePropertyOrder: args.propOrder, | ||
generateRequired: args.required | ||
generateRequired: args.required, | ||
out: args.out | ||
}); | ||
@@ -451,0 +535,0 @@ } |
@@ -13,7 +13,10 @@ import * as ts from "typescript"; | ||
useRef: true, | ||
useTypeAliasRef: false, | ||
useRootRef: false, | ||
useTitle: false, | ||
useDefaultProperties: false, | ||
disableExtraProperties: false, | ||
usePropertyOrder: false, | ||
generateRequired: false | ||
generateRequired: false, | ||
out: undefined | ||
}; | ||
@@ -114,3 +117,7 @@ } | ||
private parseCommentsIntoDefinition(comments: ts.SymbolDisplayPart[], definition: any): void { | ||
private parseCommentsIntoDefinition(symbol: ts.Symbol, definition: any): void { | ||
if (!symbol) { | ||
return; | ||
} | ||
const comments : ts.SymbolDisplayPart[] = symbol.getDocumentationComment(); | ||
if (!comments || !comments.length) { | ||
@@ -123,15 +130,7 @@ return; | ||
} | ||
private getDefinitionForRootType(propertyType: ts.Type, tc: ts.TypeChecker, definition: any, unionModifier: string = "oneOf") { | ||
if (propertyType.flags & ts.TypeFlags.Union) { | ||
const unionType = <ts.UnionType>propertyType; | ||
const types = unionType.types.map((propType) => { | ||
return this.getTypeDefinition(propType, tc); | ||
}); | ||
definition[unionModifier] = types; | ||
return definition; | ||
} | ||
private getDefinitionForRootType(propertyType: ts.Type, tc: ts.TypeChecker, reffedType: ts.Symbol, definition: any) { | ||
const symbol = propertyType.getSymbol(); | ||
const propertyTypeString = tc.typeToString(propertyType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
switch (propertyTypeString.toLowerCase()) { | ||
@@ -142,3 +141,4 @@ case "string": | ||
case "number": | ||
definition.type = "number"; | ||
const isInteger = (definition.type == "integer" || (reffedType && reffedType.getName() == "integer")); | ||
definition.type = isInteger ? "integer" : "number"; | ||
break; | ||
@@ -149,4 +149,8 @@ case "boolean": | ||
case "any": | ||
definition.type = "object"; | ||
// no type restriction, so that anything will match | ||
break; | ||
case "date": | ||
definition.type = "string"; | ||
definition.format = "date-time"; | ||
break; | ||
default: | ||
@@ -162,3 +166,6 @@ if(propertyType.flags & ts.TypeFlags.Tuple) { // tuple | ||
}; | ||
} else if (propertyType.getSymbol().getName() == "Array") { | ||
} else if (propertyType.flags & ts.TypeFlags.StringLiteral) { | ||
definition.type = "string"; | ||
definition.enum = [ (<ts.StringLiteralType> propertyType).text ]; | ||
} else if (symbol && symbol.getName() == "Array") { | ||
const arrayType = (<ts.TypeReference>propertyType).typeArguments[0]; | ||
@@ -170,7 +177,20 @@ definition.type = "array"; | ||
// TODO | ||
definition = this.getTypeDefinition(propertyType, tc); | ||
console.error("Unsupported type: ", propertyType); | ||
//definition = this.getTypeDefinition(propertyType, tc); | ||
} | ||
} | ||
return definition; | ||
} | ||
private getReferencedTypeSymbol(prop: ts.Symbol, tc: ts.TypeChecker) : ts.Symbol { | ||
const decl = prop.getDeclarations(); | ||
if (decl && decl.length) { | ||
const type = (<ts.TypeReferenceNode> (<any> decl[0]).type); | ||
if (type && (type.kind & ts.SyntaxKind.TypeReference) && type.typeName) { | ||
return tc.getSymbolAtLocation(type.typeName); | ||
} | ||
} | ||
return null; | ||
} | ||
@@ -182,3 +202,5 @@ private getDefinitionForProperty(prop: ts.Symbol, tc: ts.TypeChecker, node: ts.Node) { | ||
let definition: any = this.getTypeDefinition(propertyType, tc); | ||
const reffedType = this.getReferencedTypeSymbol(prop, tc); | ||
let definition: any = this.getTypeDefinition(propertyType, tc, undefined, undefined, prop, reffedType); | ||
if (this.args.useTitle) { | ||
@@ -188,5 +210,2 @@ definition.title = propertyName; | ||
const comments = prop.getDocumentationComment(); | ||
this.parseCommentsIntoDefinition(comments, definition); | ||
if (definition.hasOwnProperty("ignore")) { | ||
@@ -210,7 +229,5 @@ return null; | ||
initial = sandbox.sandboxvar; | ||
if (initial == null) { | ||
} else if (typeof (initial) === "string" || typeof (initial) === "number" || typeof (initial) === "boolean" || Object.prototype.toString.call(initial) === '[object Array]') { | ||
if (initial === null || typeof (initial) === "string" || typeof (initial) === "number" || typeof (initial) === "boolean" || Object.prototype.toString.call(initial) === '[object Array]') { | ||
definition.default = initial; | ||
} else { | ||
} else if (initial) { | ||
console.warn("unknown initializer for property " + propertyName + ": " + initial); | ||
@@ -229,3 +246,3 @@ } | ||
const node = clazzType.getSymbol().getDeclarations()[0]; | ||
const fullName = tc.typeToString(clazzType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
const fullName = tc.typeToString(clazzType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
const enm = <ts.EnumDeclaration>node; | ||
@@ -260,3 +277,2 @@ const values = tc.getIndexTypeOfType(clazzType, ts.IndexKind.String); | ||
definition.type = "string"; | ||
definition.title = fullName; | ||
@@ -284,4 +300,5 @@ if (enumValues.length > 0) { | ||
const indexType = tc.getTypeOfSymbolAtLocation(indexSymbol, node); | ||
if(indexType.flags != ts.TypeFlags.Number) { | ||
throw "Not supported: IndexSignatureDeclaration with non-number index symbol"; | ||
const isStringIndexed = (indexType.flags == ts.TypeFlags.String); | ||
if(indexType.flags != ts.TypeFlags.Number && !isStringIndexed) { | ||
throw "Not supported: IndexSignatureDeclaration with index symbol other than a number or a string"; | ||
} | ||
@@ -292,4 +309,9 @@ | ||
definition.type = "array"; | ||
definition.items = def; | ||
if(isStringIndexed) { | ||
definition.type = "object"; | ||
definition.additionalProperties = def; | ||
} else { | ||
definition.type = "array"; | ||
definition.items = def; | ||
} | ||
return definition; | ||
@@ -317,8 +339,8 @@ } else if (clazz.flags & ts.NodeFlags.Abstract) { | ||
if (this.args.useTitle) { | ||
definition.title = fullName; | ||
} | ||
if (this.args.useDefaultProperties) { | ||
definition.defaultProperties = []; | ||
} | ||
if (this.args.disableExtraProperties && definition.additionalProperties === undefined) { | ||
definition.additionalProperties = false; | ||
} | ||
if (this.args.usePropertyOrder) { | ||
@@ -349,29 +371,76 @@ // propertyOrder is non-standard, but useful: | ||
private getTypeDefinition(typ: ts.Type, tc: ts.TypeChecker, asRef = this.args.useRef, unionModifier: string = "oneOf"): any { | ||
const fullName = tc.typeToString(typ, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
let definition = {}; | ||
private getTypeDefinition(typ: ts.Type, tc: ts.TypeChecker, asRef = this.args.useRef, unionModifier: string = "oneOf", prop? : ts.Symbol, reffedType?: ts.Symbol): any { | ||
const definition : any = {}; // real definition | ||
let returnedDefinition = definition; // returned definition, may be a $ref | ||
if(!typ.getSymbol() || typ.getSymbol().name == "Array") { // this is a type alias or raw type, don't do refs for these types | ||
return this.getDefinitionForRootType(typ, tc, definition, unionModifier); | ||
const symbol = typ.getSymbol(); | ||
const isRawType = (!symbol || symbol.name == "integer" || symbol.name == "Array" || symbol.name == "Date"); | ||
// special case: an union where all child are string literals -> make an enum instead | ||
let isStringEnum = false; | ||
if (typ.flags & ts.TypeFlags.Union) { | ||
const unionType = <ts.UnionType>typ; | ||
isStringEnum = (unionType.types.every((propType, i, r) => { | ||
return (propType.getFlags() & ts.TypeFlags.StringLiteral) != 0; | ||
})); | ||
} | ||
if (!asRef || !this.reffedDefinitions[fullName]) { | ||
if(asRef) { | ||
this.reffedDefinitions[fullName] = definition; | ||
// aliased types must be handled slightly different | ||
const asTypeAliasRef = asRef && ((reffedType && this.args.useTypeAliasRef) || isStringEnum); | ||
if (!asTypeAliasRef) { | ||
if (isRawType || (typ.getFlags() & ts.TypeFlags.Anonymous)) { | ||
asRef = false; // raw types and inline types cannot be reffed, | ||
// unless we are handling a type alias | ||
} | ||
} | ||
let fullTypeName = ""; | ||
if (asTypeAliasRef) { | ||
fullTypeName = tc.getFullyQualifiedName(reffedType); | ||
} else if (asRef) { | ||
fullTypeName = tc.typeToString(typ, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
} | ||
if (asRef) { | ||
returnedDefinition = { | ||
"$ref": "#/definitions/" + fullTypeName | ||
}; | ||
} | ||
// Parse comments | ||
this.parseCommentsIntoDefinition(reffedType, definition); // handle comments in the type alias declaration | ||
this.parseCommentsIntoDefinition(prop || symbol, returnedDefinition); | ||
if (!asRef || !this.reffedDefinitions[fullTypeName]) { | ||
if (asRef) { // must be here to prevent recursivity problems | ||
this.reffedDefinitions[fullTypeName] = definition; | ||
if (this.args.useTitle && fullTypeName) { | ||
definition.title = fullTypeName; | ||
} | ||
} | ||
const node = typ.getSymbol().getDeclarations()[0]; | ||
if (node.kind == ts.SyntaxKind.EnumDeclaration) { | ||
const node = symbol ? symbol.getDeclarations()[0] : null; | ||
if (typ.flags & ts.TypeFlags.Union) { | ||
const unionType = <ts.UnionType>typ; | ||
if (isStringEnum) { | ||
definition.type = "string"; | ||
definition.enum = unionType.types.map((propType) => { | ||
return (<ts.StringLiteralType> propType).text; | ||
}); | ||
} else { | ||
definition[unionModifier] = unionType.types.map((propType) => { | ||
return this.getTypeDefinition(propType, tc); | ||
}); | ||
} | ||
} else if (isRawType) { | ||
this.getDefinitionForRootType(typ, tc, reffedType, definition); | ||
} else if (node.kind == ts.SyntaxKind.EnumDeclaration) { | ||
this.getEnumDefinition(typ, tc, definition); | ||
} else { | ||
this.getClassDefinition(typ, tc, definition); | ||
} | ||
} | ||
} | ||
if (asRef) { | ||
return { | ||
"$ref": "#/definitions/" + fullName | ||
}; | ||
} else { | ||
return definition; | ||
} | ||
return returnedDefinition; | ||
} | ||
@@ -477,2 +546,6 @@ | ||
options.noEmit = true; | ||
delete options.out; | ||
delete options.outDir; | ||
delete options.outFile; | ||
delete options.declaration; | ||
@@ -493,3 +566,13 @@ const program = ts.createProgram(configParseResult.fileNames, options); | ||
const definition = TJS.generateSchema(program, fullTypeName, args); | ||
process.stdout.write(JSON.stringify(definition, null, 4) + "\n"); | ||
const json = JSON.stringify(definition, null, 4) + "\n"; | ||
if(args.out) { | ||
require("fs").writeFile(args.out, json, function(err) { | ||
if(err) { | ||
console.error("Unable to write output file: " + err.message); | ||
} | ||
}); | ||
} else { | ||
process.stdout.write(json); | ||
} | ||
} | ||
@@ -505,2 +588,4 @@ | ||
.describe("refs", "Create shared ref definitions.") | ||
.boolean("aliasRefs").default("aliasRefs", defaultArgs.useTypeAliasRef) | ||
.describe("aliasRefs", "Create shared ref definitions for the type aliases.") | ||
.boolean("topRef").default("topRef", defaultArgs.useRootRef) | ||
@@ -511,3 +596,5 @@ .describe("topRef", "Create a top-level ref definition.") | ||
.boolean("defaultProps").default("defaultProps", defaultArgs.useDefaultProperties) | ||
.describe("defaultProps", "Create default properties definitions.") | ||
.describe("defaultProps", "Create default properties definitions.") | ||
.boolean("noExtraProps").default("noExtraProps", defaultArgs.disableExtraProperties) | ||
.describe("noExtraProps", "Disable additional properties in objects by default.") | ||
.boolean("propOrder").default("propOrder", defaultArgs.usePropertyOrder) | ||
@@ -517,2 +604,4 @@ .describe("propOrder", "Create property order definitions.") | ||
.describe("required", "Create required array for non-optional properties.") | ||
.alias("out", "o") | ||
.describe("out", "The output file, defaults to using stdout") | ||
.argv; | ||
@@ -522,7 +611,10 @@ | ||
useRef: args.refs, | ||
useTypeAliasRef: args.aliasRefs, | ||
useRootRef: args.topRef, | ||
useTitle: args.titles, | ||
useDefaultProperties: args.defaultProps, | ||
disableExtraProperties: args.noExtraProps, | ||
usePropertyOrder: args.propOrder, | ||
generateRequired: args.required | ||
generateRequired: args.required, | ||
out: args.out | ||
}); | ||
@@ -529,0 +621,0 @@ } |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
263234
57
5799
2