typescript-json-schema
Advanced tools
Comparing version 0.1.1 to 0.2.0
{ | ||
"name": "typescript-json-schema", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "typescript-json-schema generates JSON Schema files from your Typescript sources", | ||
@@ -31,3 +31,3 @@ "main": "typescript-json-schema.js", | ||
"dependencies": { | ||
"typescript": "~1.8.0", | ||
"typescript": "~2.0.3", | ||
"glob": "~7.0.0", | ||
@@ -34,0 +34,0 @@ "yargs": "^4.1.0" |
@@ -20,5 +20,5 @@ # typescript-json-schema | ||
* Generate schema from a typescript type: `typescript-json-schema project/directory/**/*.ts fully.qualified.type.to.generate` | ||
* Generate schema from a typescript type: `typescript-json-schema "project/directory/**/*.ts" fully.qualified.type.to.generate` | ||
## Background | ||
Inspired and builds upon [Typson](https://github.com/lbovet/typson/), but typescript-json-schema is compatible with more recent Typescript versions. Also, since it uses the Typescript compiler internally, more advanced scenarios are possible. | ||
Inspired and builds upon [Typson](https://github.com/lbovet/typson/), but typescript-json-schema is compatible with more recent Typescript versions. Also, since it uses the Typescript compiler internally, more advanced scenarios are possible. |
@@ -0,0 +0,0 @@ export interface MyObject { |
@@ -0,0 +0,0 @@ { |
interface MyArray { | ||
[index: number]: string | number; | ||
} |
@@ -5,7 +5,7 @@ { | ||
"items": { | ||
"anyOf": [ | ||
{"type": "string"}, | ||
{"type": "number"} | ||
"type": [ | ||
"string", | ||
"number" | ||
] | ||
} | ||
} |
@@ -0,0 +0,0 @@ { |
@@ -0,0 +0,0 @@ |
@@ -0,0 +0,0 @@ { |
@@ -8,2 +8,2 @@ enum Enum { | ||
foo: Enum | ||
} | ||
} |
@@ -0,0 +0,0 @@ { |
@@ -0,0 +0,0 @@ { |
@@ -0,0 +0,0 @@ interface MyObject { |
@@ -0,0 +0,0 @@ { |
@@ -0,0 +0,0 @@ { |
@@ -0,0 +0,0 @@ |
@@ -26,9 +26,5 @@ { | ||
"additionalProperties": { | ||
"anyOf": [ | ||
{ | ||
"type": "string" | ||
}, | ||
{ | ||
"type": "number" | ||
} | ||
"type": [ | ||
"string", | ||
"number" | ||
] | ||
@@ -35,0 +31,0 @@ }, |
@@ -0,0 +0,0 @@ { |
@@ -0,0 +0,0 @@ interface MyObject { |
@@ -0,0 +0,0 @@ { |
@@ -0,0 +0,0 @@ { |
type MyFixedSizeArray = [string, number]; | ||
@@ -0,0 +0,0 @@ { |
@@ -0,0 +0,0 @@ { |
@@ -0,0 +0,0 @@ interface MyObject { |
@@ -13,9 +13,5 @@ { | ||
"SubfieldB": { | ||
"oneOf": [ | ||
{ | ||
"type": "string" | ||
}, | ||
{ | ||
"type": "number" | ||
} | ||
"type": [ | ||
"string", | ||
"number" | ||
] | ||
@@ -22,0 +18,0 @@ }, |
@@ -0,0 +0,0 @@ |
@@ -0,0 +0,0 @@ { |
@@ -1,1 +0,12 @@ | ||
type MyType = string | number; | ||
// Simple union (generates "type": [...]) | ||
type MyType1 = string | number; | ||
// Non-simple union (generates a "oneOf"/"anyOf") | ||
type MyType2 = string | number[]; | ||
interface MyObject | ||
{ | ||
var1 : MyType1; | ||
var2 : MyType2; | ||
} |
{ | ||
"$schema": "http://json-schema.org/draft-04/schema#", | ||
"oneOf": [ | ||
{ | ||
"type": "string" | ||
}, { | ||
"type": "number" | ||
"type": "object", | ||
"properties": { | ||
"var1": { | ||
"type": [ | ||
"string", | ||
"number" | ||
] | ||
}, | ||
"var2": { | ||
"anyOf": [ | ||
{ | ||
"type": "array", | ||
"items": { | ||
"type": "number" | ||
} | ||
}, | ||
{ | ||
"type": "string" | ||
} | ||
] | ||
} | ||
] | ||
}, | ||
"required": [ | ||
"var1", | ||
"var2" | ||
], | ||
"$schema": "http://json-schema.org/draft-04/schema#" | ||
} |
import {assert} from "chai"; | ||
import {version as TypescriptVersion, CompilerOptions} from "typescript"; | ||
import {TJS} from "../typescript-json-schema"; | ||
@@ -8,3 +9,3 @@ import {readFileSync} from 'fs'; | ||
export function assertSchema(group: string, name: string, type: string, settings?: any) { | ||
export function assertSchema(group: string, name: string, type: string, settings?: any, compilerOptions?: CompilerOptions) { | ||
it(group + " should create correct schema", function() { | ||
@@ -16,3 +17,3 @@ if(!settings) { | ||
const actual = TJS.generateSchema(TJS.getProgramFromFiles([resolve(base + group + "/" + name)]), type, settings); | ||
const actual = TJS.generateSchema(TJS.getProgramFromFiles([resolve(base + group + "/" + name)], compilerOptions), type, settings); | ||
@@ -33,7 +34,7 @@ const file = readFileSync(base + group + "/schema.json", "utf8") | ||
assertSchema("interface-multi", "main.ts", "MyObject"); | ||
let settings = TJS.getDefaultArgs(); | ||
settings.useRootRef = true; | ||
assertSchema("interface-recursion", "main.ts", "MyObject", settings); // this sample needs rootRef | ||
assertSchema("module-interface-single", "main.ts", "MyObject"); | ||
@@ -45,12 +46,23 @@ | ||
assertSchema("enums-string", "main.ts", "MyObject"); | ||
assertSchema("enums-number", "main.ts", "MyObject"); | ||
assertSchema("enums-number-initialized", "main.ts", "Enum"); | ||
assertSchema("enums-compiled-compute", "main.ts", "Enum"); | ||
assertSchema("enums-mixed", "main.ts", "MyObject"); | ||
assertSchema("string-literals", "main.ts", "MyObject"); | ||
assertSchema("string-literals-inline", "main.ts", "MyObject"); | ||
assertSchema("array-types", "main.ts", "MyArray"); | ||
assertSchema("map-types", "main.ts", "MyObject"); | ||
assertSchema("namespace", "main.ts", "Type"); | ||
assertSchema("type-union", "main.ts", "MyType"); | ||
assertSchema("type-union", "main.ts", "MyObject"); | ||
assertSchema("type-intersection", "main.ts", "MyObject"); | ||
assertSchema("type-aliases", "main.ts", "MyString"); | ||
assertSchema("type-aliases-fixed-size-array", "main.ts", "MyFixedSizeArray"); | ||
assertSchema("type-aliases-multitype-array", "main.ts", "MyArray"); | ||
assertSchema("type-anonymous", "main.ts", "MyObject"); | ||
assertSchema("type-primitives", "main.ts", "MyObject"); | ||
assertSchema("type-nullable", "main.ts", "MyObject"); | ||
@@ -60,2 +72,9 @@ assertSchema("optionals", "main.ts", "MyObject"); | ||
assertSchema("comments", "main.ts", "MyObject"); | ||
assertSchema("comments-override", "main.ts", "MyObject"); | ||
assertSchema("type-union-tagged", "main.ts", "Shape"); | ||
assertSchema("strict-null-checks", "main.ts", "MyObject", undefined, { | ||
strictNullChecks: true | ||
}); | ||
}); |
@@ -14,9 +14,8 @@ { | ||
"preserveConstEnums": true, | ||
"suppressImplicitAnyIndexErrors": true, | ||
"sourceMap": true, | ||
"watch": false | ||
}, | ||
"filesGlob": [ | ||
"include": [ | ||
"**/*.ts", | ||
"**/*.tsx" | ||
"typings/**/*.d.ts" | ||
], | ||
@@ -29,14 +28,3 @@ "exclude": [ | ||
"compileOnSave": true, | ||
"buildOnSave": false, | ||
"files": [ | ||
"test/schema.test.ts", | ||
"typescript-json-schema.ts", | ||
"typings/assertion-error/assertion-error.d.ts", | ||
"typings/chai/chai.d.ts", | ||
"typings/glob/glob.d.ts", | ||
"typings/minimatch/minimatch.d.ts", | ||
"typings/mocha/mocha.d.ts", | ||
"typings/node/node.d.ts", | ||
"typings/typescript/typescript.d.ts" | ||
] | ||
"buildOnSave": false | ||
} |
@@ -18,2 +18,3 @@ "use strict"; | ||
generateRequired: false, | ||
strictNullChecks: false, | ||
out: undefined | ||
@@ -29,2 +30,6 @@ }; | ||
this.reffedDefinitions = {}; | ||
this.simpleTypesAllowedProperties = [ | ||
"type", | ||
"description" | ||
]; | ||
this.allSymbols = allSymbols; | ||
@@ -41,3 +46,3 @@ this.inheritingTypes = inheritingTypes; | ||
}); | ||
JsonSchemaGenerator.prototype.copyValidationKeywords = function (comment, to) { | ||
JsonSchemaGenerator.prototype.copyValidationKeywords = function (comment, to, otherAnnotations) { | ||
JsonSchemaGenerator.annotedValidationKeywordPattern.lastIndex = 0; | ||
@@ -72,2 +77,5 @@ var annotation; | ||
} | ||
else { | ||
otherAnnotations[keyword] = true; | ||
} | ||
} | ||
@@ -84,3 +92,3 @@ }; | ||
}; | ||
JsonSchemaGenerator.prototype.parseCommentsIntoDefinition = function (symbol, definition) { | ||
JsonSchemaGenerator.prototype.parseCommentsIntoDefinition = function (symbol, definition, otherAnnotations) { | ||
if (!symbol) { | ||
@@ -95,48 +103,87 @@ return; | ||
joined = this.copyDescription(joined, definition); | ||
this.copyValidationKeywords(joined, definition); | ||
this.copyValidationKeywords(joined, definition, otherAnnotations); | ||
}; | ||
JsonSchemaGenerator.prototype.extractLiteralValue = function (typ) { | ||
if (typ.flags & ts.TypeFlags.EnumLiteral) { | ||
var str = typ.text; | ||
var num = parseFloat(str); | ||
return isNaN(num) ? str : num; | ||
} | ||
else if (typ.flags & ts.TypeFlags.StringLiteral) { | ||
return typ.text; | ||
} | ||
else if (typ.flags & ts.TypeFlags.NumberLiteral) { | ||
return parseFloat(typ.text); | ||
} | ||
else if (typ.flags & ts.TypeFlags.BooleanLiteral) { | ||
return typ.intrinsicName == "true"; | ||
} | ||
return undefined; | ||
}; | ||
JsonSchemaGenerator.prototype.resolveTupleType = function (propertyType) { | ||
if (!propertyType.getSymbol() && (propertyType.getFlags() & ts.TypeFlags.Reference)) | ||
return propertyType.target; | ||
if (!(propertyType.flags & ts.TypeFlags.Tuple)) | ||
return null; | ||
return propertyType; | ||
}; | ||
JsonSchemaGenerator.prototype.getDefinitionForRootType = function (propertyType, tc, reffedType, definition) { | ||
var _this = this; | ||
var symbol = propertyType.getSymbol(); | ||
var propertyTypeString = tc.typeToString(propertyType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
switch (propertyTypeString.toLowerCase()) { | ||
case "string": | ||
definition.type = "string"; | ||
break; | ||
case "number": | ||
var isInteger = (definition.type == "integer" || (reffedType && reffedType.getName() == "integer")); | ||
definition.type = isInteger ? "integer" : "number"; | ||
break; | ||
case "boolean": | ||
definition.type = "boolean"; | ||
break; | ||
case "any": | ||
break; | ||
case "date": | ||
definition.type = "string"; | ||
definition.format = "date-time"; | ||
break; | ||
default: | ||
if (propertyType.flags & ts.TypeFlags.Tuple) { | ||
var tupleType = propertyType; | ||
var fixedTypes = tupleType.elementTypes.map(function (elType) { return _this.getTypeDefinition(elType, tc); }); | ||
definition.type = "array"; | ||
definition.items = fixedTypes; | ||
definition.minItems = fixedTypes.length; | ||
definition.additionalItems = { | ||
"anyOf": fixedTypes | ||
}; | ||
} | ||
else if (propertyType.flags & ts.TypeFlags.StringLiteral) { | ||
var tupleType = this.resolveTupleType(propertyType); | ||
if (tupleType) { | ||
var elemTypes = tupleType.elementTypes || propertyType.typeArguments; | ||
var fixedTypes = elemTypes.map(function (elType) { return _this.getTypeDefinition(elType, tc); }); | ||
definition.type = "array"; | ||
definition.items = fixedTypes; | ||
definition.minItems = fixedTypes.length; | ||
definition.additionalItems = { | ||
"anyOf": fixedTypes | ||
}; | ||
} | ||
else { | ||
var propertyTypeString = tc.typeToString(propertyType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
switch (propertyTypeString.toLowerCase()) { | ||
case "string": | ||
definition.type = "string"; | ||
definition.enum = [propertyType.text]; | ||
} | ||
else if (symbol && symbol.getName() == "Array") { | ||
var arrayType = propertyType.typeArguments[0]; | ||
definition.type = "array"; | ||
definition.items = this.getTypeDefinition(arrayType, tc); | ||
} | ||
else { | ||
console.error("Unsupported type: ", propertyType); | ||
} | ||
break; | ||
case "number": | ||
var isInteger = (definition.type == "integer" || (reffedType && reffedType.getName() == "integer")); | ||
definition.type = isInteger ? "integer" : "number"; | ||
break; | ||
case "boolean": | ||
definition.type = "boolean"; | ||
break; | ||
case "null": | ||
definition.type = "null"; | ||
break; | ||
case "undefined": | ||
definition.type = "undefined"; | ||
break; | ||
case "any": | ||
break; | ||
case "date": | ||
definition.type = "string"; | ||
definition.format = "date-time"; | ||
break; | ||
default: | ||
var value = this.extractLiteralValue(propertyType); | ||
if (value !== undefined) { | ||
definition.type = typeof value; | ||
definition.enum = [value]; | ||
} | ||
else if (symbol && symbol.getName() == "Array") { | ||
var arrayType = propertyType.typeArguments[0]; | ||
definition.type = "array"; | ||
definition.items = this.getTypeDefinition(arrayType, tc); | ||
} | ||
else { | ||
var info = propertyType; | ||
try { | ||
info = JSON.stringify(propertyType); | ||
} | ||
catch (err) { } | ||
console.error("Unsupported type: ", info); | ||
} | ||
} | ||
} | ||
@@ -200,22 +247,45 @@ return definition; | ||
var enumValues = []; | ||
var enumTypes = []; | ||
var addType = function (type) { | ||
if (enumTypes.indexOf(type) == -1) | ||
enumTypes.push(type); | ||
}; | ||
enm.members.forEach(function (member) { | ||
var caseLabel = member.name.text; | ||
var initial = member.initializer; | ||
if (initial) { | ||
if (initial.expression) { | ||
var exp = initial.expression; | ||
var text = exp.text; | ||
if (text) { | ||
enumValues.push(text); | ||
var constantValue = tc.getConstantValue(member); | ||
if (constantValue !== undefined) { | ||
enumValues.push(constantValue); | ||
addType(typeof constantValue); | ||
} | ||
else { | ||
var initial = member.initializer; | ||
if (initial) { | ||
if (initial.expression) { | ||
var exp = initial.expression; | ||
var text = exp.text; | ||
if (text) { | ||
enumValues.push(text); | ||
addType("string"); | ||
} | ||
else if (exp.kind == ts.SyntaxKind.TrueKeyword || exp.kind == ts.SyntaxKind.FalseKeyword) { | ||
enumValues.push((exp.kind == ts.SyntaxKind.TrueKeyword)); | ||
addType("boolean"); | ||
} | ||
else { | ||
console.warn("initializer is expression for enum: " + fullName + "." + caseLabel); | ||
} | ||
} | ||
else { | ||
console.warn("initializer is expression for enum: " + fullName + "." + caseLabel); | ||
else if (initial.kind == ts.SyntaxKind.NoSubstitutionTemplateLiteral) { | ||
enumValues.push(initial.getText()); | ||
addType("string"); | ||
} | ||
else if (initial.kind == ts.SyntaxKind.NullKeyword) { | ||
enumValues.push(null); | ||
addType("null"); | ||
} | ||
} | ||
else if (initial.kind && initial.kind == ts.SyntaxKind.NoSubstitutionTemplateLiteral) { | ||
enumValues.push(initial.getText()); | ||
} | ||
} | ||
}); | ||
definition.type = "string"; | ||
if (enumTypes.length) | ||
definition.type = (enumTypes.length == 1) ? enumTypes[0] : enumTypes; | ||
if (enumValues.length > 0) { | ||
@@ -226,2 +296,65 @@ definition["enum"] = enumValues; | ||
}; | ||
JsonSchemaGenerator.prototype.getUnionDefinition = function (unionType, prop, tc, unionModifier, definition) { | ||
var enumValues = []; | ||
var simpleTypes = []; | ||
var schemas = []; | ||
var addSimpleType = function (type) { | ||
if (simpleTypes.indexOf(type) == -1) | ||
simpleTypes.push(type); | ||
}; | ||
var addEnumValue = function (val) { | ||
if (enumValues.indexOf(val) == -1) | ||
enumValues.push(val); | ||
}; | ||
for (var i = 0; i < unionType.types.length; ++i) { | ||
var valueType = unionType.types[i]; | ||
var value = this.extractLiteralValue(valueType); | ||
if (value !== undefined) { | ||
addEnumValue(value); | ||
} | ||
else { | ||
var def = this.getTypeDefinition(unionType.types[i], tc); | ||
if (def.type === "undefined") { | ||
if (prop) | ||
prop.mayBeUndefined = true; | ||
} | ||
else { | ||
var keys = Object.keys(def); | ||
if (keys.length == 1 && keys[0] == "type") | ||
addSimpleType(def.type); | ||
else | ||
schemas.push(def); | ||
} | ||
} | ||
} | ||
if (enumValues.length > 0) { | ||
var isOnlyBooleans = enumValues.length == 2 && | ||
typeof enumValues[0] === "boolean" && | ||
typeof enumValues[1] === "boolean" && | ||
enumValues[0] !== enumValues[1]; | ||
if (isOnlyBooleans) { | ||
addSimpleType("boolean"); | ||
} | ||
else { | ||
var enumSchema = { enum: enumValues }; | ||
if (enumValues.every(function (x) { return typeof x == "string"; })) | ||
enumSchema.type = "string"; | ||
else if (enumValues.every(function (x) { return typeof x == "number"; })) | ||
enumSchema.type = "number"; | ||
else if (enumValues.every(function (x) { return typeof x == "boolean"; })) | ||
enumSchema.type = "boolean"; | ||
schemas.push(enumSchema); | ||
} | ||
} | ||
if (simpleTypes.length > 0) | ||
schemas.push({ type: simpleTypes.length == 1 ? simpleTypes[0] : simpleTypes }); | ||
if (schemas.length == 1) { | ||
for (var k in schemas[0]) | ||
definition[k] = schemas[0][k]; | ||
} | ||
else { | ||
definition[unionModifier] = schemas; | ||
} | ||
return definition; | ||
}; | ||
JsonSchemaGenerator.prototype.getClassDefinition = function (clazzType, tc, definition) { | ||
@@ -289,3 +422,3 @@ var _this = this; | ||
var requiredProps = props.reduce(function (required, prop) { | ||
if (!(prop.flags & ts.SymbolFlags.Optional)) { | ||
if (!(prop.flags & ts.SymbolFlags.Optional) && !prop.mayBeUndefined) { | ||
required.push(prop.getName()); | ||
@@ -301,6 +434,43 @@ } | ||
}; | ||
JsonSchemaGenerator.prototype.addSimpleType = function (def, type) { | ||
for (var k in def) { | ||
if (this.simpleTypesAllowedProperties.indexOf(k) == -1) | ||
return false; | ||
} | ||
if (!def.type) { | ||
def.type = type; | ||
} | ||
else if (def.type.push) { | ||
if (!def.type.every(function (val) { return typeof val == "string"; })) | ||
return false; | ||
if (def.type.indexOf('null') == -1) | ||
def.type.push('null'); | ||
} | ||
else { | ||
if (typeof def.type != "string") | ||
return false; | ||
if (def.type != 'null') | ||
def.type = [def.type, 'null']; | ||
} | ||
return true; | ||
}; | ||
JsonSchemaGenerator.prototype.makeNullable = function (def) { | ||
if (!this.addSimpleType(def, 'null')) { | ||
var union = def.oneOf || def.anyOf; | ||
if (union) | ||
union.push({ type: 'null' }); | ||
else { | ||
var subdef = {}; | ||
for (var k in def) { | ||
subdef[k] = def[k]; | ||
delete def[k]; | ||
} | ||
def.anyOf = [subdef, { type: 'null' }]; | ||
} | ||
} | ||
return def; | ||
}; | ||
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"; } | ||
if (unionModifier === void 0) { unionModifier = "anyOf"; } | ||
var definition = {}; | ||
@@ -317,3 +487,3 @@ var returnedDefinition = definition; | ||
} | ||
var asTypeAliasRef = asRef && ((reffedType && this.args.useTypeAliasRef) || isStringEnum); | ||
var asTypeAliasRef = asRef && reffedType && (this.args.useTypeAliasRef || isStringEnum); | ||
if (!asTypeAliasRef) { | ||
@@ -336,4 +506,8 @@ if (isRawType || (typ.getFlags() & ts.TypeFlags.Anonymous)) { | ||
} | ||
this.parseCommentsIntoDefinition(reffedType, definition); | ||
this.parseCommentsIntoDefinition(prop || symbol, returnedDefinition); | ||
var otherAnnotations = {}; | ||
this.parseCommentsIntoDefinition(reffedType, definition, otherAnnotations); | ||
if (prop) | ||
this.parseCommentsIntoDefinition(prop, returnedDefinition, otherAnnotations); | ||
else | ||
this.parseCommentsIntoDefinition(symbol, definition, otherAnnotations); | ||
if (!asRef || !this.reffedDefinitions[fullTypeName]) { | ||
@@ -348,14 +522,10 @@ if (asRef) { | ||
if (typ.flags & ts.TypeFlags.Union) { | ||
var unionType = typ; | ||
if (isStringEnum) { | ||
definition.type = "string"; | ||
definition.enum = unionType.types.map(function (propType) { | ||
return propType.text; | ||
}); | ||
this.getUnionDefinition(typ, prop, tc, unionModifier, definition); | ||
} | ||
else if (typ.flags & ts.TypeFlags.Intersection) { | ||
definition.allOf = []; | ||
var types = typ.types; | ||
for (var i = 0; i < types.length; ++i) { | ||
definition.allOf.push(this.getTypeDefinition(types[i], tc)); | ||
} | ||
else { | ||
definition[unionModifier] = unionType.types.map(function (propType) { | ||
return _this.getTypeDefinition(propType, tc); | ||
}); | ||
} | ||
} | ||
@@ -365,3 +535,3 @@ else if (isRawType) { | ||
} | ||
else if (node.kind == ts.SyntaxKind.EnumDeclaration) { | ||
else if (node && (node.kind == ts.SyntaxKind.EnumDeclaration || node.kind == ts.SyntaxKind.EnumMember)) { | ||
this.getEnumDefinition(typ, tc, definition); | ||
@@ -372,2 +542,4 @@ } | ||
} | ||
if (otherAnnotations['nullable']) | ||
this.makeNullable(definition); | ||
} | ||
@@ -388,2 +560,12 @@ return returnedDefinition; | ||
}; | ||
JsonSchemaGenerator.prototype.getSchemaForSymbols = function (symbols) { | ||
var root = { | ||
"$schema": "http://json-schema.org/draft-04/schema#", | ||
definitions: {} | ||
}; | ||
for (var id in symbols) { | ||
root.definitions[id] = this.getTypeDefinition(symbols[id], this.tc, this.args.useRootRef); | ||
} | ||
return root; | ||
}; | ||
JsonSchemaGenerator.validationKeywords = [ | ||
@@ -397,6 +579,9 @@ "ignore", "description", "type", "minimum", "exclusiveMinimum", "maximum", | ||
}()); | ||
function getProgramFromFiles(files) { | ||
function getProgramFromFiles(files, compilerOptions) { | ||
if (compilerOptions === void 0) { compilerOptions = {}; } | ||
var options = { | ||
noEmit: true, emitDecoratorMetadata: true, experimentalDecorators: true, target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS | ||
}; | ||
for (var k in compilerOptions) | ||
options[k] = compilerOptions[k]; | ||
return ts.createProgram(files, options); | ||
@@ -411,4 +596,5 @@ } | ||
var allSymbols_1 = {}; | ||
var userSymbols_1 = {}; | ||
var inheritingTypes_1 = {}; | ||
program.getSourceFiles().forEach(function (sourceFile) { | ||
program.getSourceFiles().forEach(function (sourceFile, sourceFileIdx) { | ||
function inspect(node, tc) { | ||
@@ -423,2 +609,4 @@ if (node.kind == ts.SyntaxKind.ClassDeclaration | ||
allSymbols_1[fullName_1] = nodeType; | ||
if (!sourceFile.hasNoDefaultLib) | ||
userSymbols_1[fullName_1] = nodeType; | ||
var baseTypes = nodeType.getBaseTypes() || []; | ||
@@ -440,3 +628,9 @@ baseTypes.forEach(function (baseType) { | ||
var generator = new JsonSchemaGenerator(allSymbols_1, inheritingTypes_1, tc, args); | ||
var definition = generator.getSchemaForSymbol(fullTypeName); | ||
var definition = void 0; | ||
if (fullTypeName == '*') { | ||
definition = generator.getSchemaForSymbols(userSymbols_1); | ||
} | ||
else { | ||
definition = generator.getSchemaForSymbol(fullTypeName); | ||
} | ||
return definition; | ||
@@ -479,3 +673,5 @@ } | ||
else { | ||
program = TJS.getProgramFromFiles(glob.sync(filePattern)); | ||
program = TJS.getProgramFromFiles(glob.sync(filePattern), { | ||
strictNullChecks: args.strictNullChecks | ||
}); | ||
} | ||
@@ -518,2 +714,4 @@ var definition = TJS.generateSchema(program, fullTypeName, args); | ||
.describe("required", "Create required array for non-optional properties.") | ||
.boolean("strictNullChecks").default("strictNullChecks", defaultArgs.strictNullChecks) | ||
.describe("strictNullChecks", "(TypeScript 2) Make values non-nullable by default.") | ||
.alias("out", "o") | ||
@@ -531,2 +729,3 @@ .describe("out", "The output file, defaults to using stdout") | ||
generateRequired: args.required, | ||
strictNullChecks: args.strictNullChecks, | ||
out: args.out | ||
@@ -533,0 +732,0 @@ }); |
@@ -20,2 +20,3 @@ import * as ts from "typescript"; | ||
generateRequired: false, | ||
strictNullChecks: false, | ||
out: undefined | ||
@@ -60,6 +61,6 @@ }; | ||
*/ | ||
private copyValidationKeywords(comment: string, to) { | ||
private copyValidationKeywords(comment: string, to: {}, otherAnnotations: {}) { | ||
JsonSchemaGenerator.annotedValidationKeywordPattern.lastIndex = 0; | ||
// TODO: to improve the use of the exec method: it could make the tokenization | ||
let annotation; | ||
let annotation: string[]; | ||
while ((annotation = JsonSchemaGenerator.annotedValidationKeywordPattern.exec(comment))) { | ||
@@ -96,2 +97,5 @@ const annotationTokens = annotation[0].split(" "); | ||
} | ||
else { | ||
otherAnnotations[keyword] = true; | ||
} | ||
} | ||
@@ -119,3 +123,3 @@ } | ||
private parseCommentsIntoDefinition(symbol: ts.Symbol, definition: any): void { | ||
private parseCommentsIntoDefinition(symbol: ts.Symbol, definition: any, otherAnnotations: {}): void { | ||
if (!symbol) { | ||
@@ -130,55 +134,95 @@ return; | ||
joined = this.copyDescription(joined, definition); | ||
this.copyValidationKeywords(joined, definition); | ||
this.copyValidationKeywords(joined, definition, otherAnnotations); | ||
} | ||
private extractLiteralValue(typ: ts.Type): string|number|boolean { | ||
if (typ.flags & (ts.TypeFlags as any).EnumLiteral) { | ||
let str = (typ as any /*ts.LiteralType*/).text; | ||
let num = parseFloat(str); | ||
return isNaN(num) ? str : num; | ||
} else if (typ.flags & ts.TypeFlags.StringLiteral) { | ||
return (/*<ts.StringLiteralType/ts.LiteralType>*/ typ as any).text; | ||
} else if (typ.flags & (ts.TypeFlags as any).NumberLiteral) { | ||
return parseFloat((typ as any).text); | ||
} else if (typ.flags & (ts.TypeFlags as any).BooleanLiteral) { | ||
return (typ as any).intrinsicName == "true"; | ||
} | ||
return undefined; | ||
} | ||
/** | ||
* Checks whether a type is a tuple type. | ||
*/ | ||
private resolveTupleType(propertyType: ts.Type): ts.TupleTypeNode { | ||
if (!propertyType.getSymbol() && (propertyType.getFlags() & ts.TypeFlags.Reference)) | ||
return (propertyType as ts.TypeReference).target as any; | ||
if (!(propertyType.flags & ts.TypeFlags.Tuple)) | ||
return null; | ||
return propertyType as any; | ||
} | ||
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); | ||
const tupleType = this.resolveTupleType(propertyType); | ||
switch (propertyTypeString.toLowerCase()) { | ||
case "string": | ||
definition.type = "string"; | ||
break; | ||
case "number": | ||
const isInteger = (definition.type == "integer" || (reffedType && reffedType.getName() == "integer")); | ||
definition.type = isInteger ? "integer" : "number"; | ||
break; | ||
case "boolean": | ||
definition.type = "boolean"; | ||
break; | ||
case "any": | ||
// no type restriction, so that anything will match | ||
break; | ||
case "date": | ||
definition.type = "string"; | ||
definition.format = "date-time"; | ||
break; | ||
default: | ||
if(propertyType.flags & ts.TypeFlags.Tuple) { // tuple | ||
const tupleType: ts.TupleType = <ts.TupleType>propertyType; | ||
const fixedTypes = tupleType.elementTypes.map(elType => this.getTypeDefinition(elType, tc)); | ||
definition.type = "array"; | ||
definition.items = fixedTypes; | ||
definition.minItems = fixedTypes.length; | ||
definition.additionalItems = { | ||
"anyOf": fixedTypes | ||
}; | ||
} else if (propertyType.flags & ts.TypeFlags.StringLiteral) { | ||
if (tupleType) { // tuple | ||
const elemTypes : ts.Type[] = tupleType.elementTypes || (propertyType as any).typeArguments; | ||
const fixedTypes = elemTypes.map(elType => this.getTypeDefinition(elType, tc)); | ||
definition.type = "array"; | ||
definition.items = fixedTypes; | ||
definition.minItems = fixedTypes.length; | ||
definition.additionalItems = { | ||
"anyOf": fixedTypes | ||
}; | ||
} else { | ||
const propertyTypeString = tc.typeToString(propertyType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
switch (propertyTypeString.toLowerCase()) { | ||
case "string": | ||
definition.type = "string"; | ||
definition.enum = [ (<ts.StringLiteralType> propertyType).text ]; | ||
} else if (symbol && symbol.getName() == "Array") { | ||
const arrayType = (<ts.TypeReference>propertyType).typeArguments[0]; | ||
definition.type = "array"; | ||
definition.items = this.getTypeDefinition(arrayType, tc); | ||
} else { | ||
// TODO | ||
console.error("Unsupported type: ", propertyType); | ||
//definition = this.getTypeDefinition(propertyType, tc); | ||
} | ||
break; | ||
case "number": | ||
const isInteger = (definition.type == "integer" || (reffedType && reffedType.getName() == "integer")); | ||
definition.type = isInteger ? "integer" : "number"; | ||
break; | ||
case "boolean": | ||
definition.type = "boolean"; | ||
break; | ||
case "null": | ||
definition.type = "null"; | ||
break; | ||
case "undefined": | ||
definition.type = "undefined"; | ||
break; | ||
case "any": | ||
// no type restriction, so that anything will match | ||
break; | ||
case "date": | ||
definition.type = "string"; | ||
definition.format = "date-time"; | ||
break; | ||
default: | ||
const value = this.extractLiteralValue(propertyType); | ||
if (value !== undefined) { | ||
definition.type = typeof value; | ||
definition.enum = [ value ]; | ||
} else if (symbol && symbol.getName() == "Array") { | ||
const arrayType = (<ts.TypeReference>propertyType).typeArguments[0]; | ||
definition.type = "array"; | ||
definition.items = this.getTypeDefinition(arrayType, tc); | ||
} else { | ||
// Report that type could not be processed | ||
let info : any = propertyType; | ||
try { info = JSON.stringify(propertyType); } | ||
catch(err) {} | ||
console.error("Unsupported type: ", info); | ||
//definition = this.getTypeDefinition(propertyType, tc); | ||
} | ||
} | ||
} | ||
return definition; | ||
} | ||
private getReferencedTypeSymbol(prop: ts.Symbol, tc: ts.TypeChecker) : ts.Symbol { | ||
@@ -201,3 +245,3 @@ const decl = prop.getDeclarations(); | ||
const reffedType = this.getReferencedTypeSymbol(prop, tc); | ||
let definition: any = this.getTypeDefinition(propertyType, tc, undefined, undefined, prop, reffedType); | ||
@@ -242,27 +286,45 @@ if (this.args.useTitle) { | ||
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; | ||
const values = tc.getIndexTypeOfType(clazzType, ts.IndexKind.String); | ||
var enumValues: string[] = []; | ||
var enumValues: any[] = []; | ||
let enumTypes: string[] = []; | ||
const addType = (type) => { | ||
if (enumTypes.indexOf(type) == -1) | ||
enumTypes.push(type); | ||
}; | ||
enm.members.forEach(member => { | ||
const caseLabel = (<ts.Identifier>member.name).text; | ||
// try to extract the enums value; it will probably by a cast expression | ||
let initial = <ts.Expression>member.initializer; | ||
if (initial) { | ||
if ((<any>initial).expression) { // node | ||
const exp = (<any>initial).expression; | ||
const text = (<any>exp).text; | ||
// if it is an expression with a text literal, chances are it is the enum convension: | ||
// CASELABEL = 'literal' as any | ||
if (text) { | ||
enumValues.push(text); | ||
} else { | ||
console.warn("initializer is expression for enum: " + fullName + "." + caseLabel); | ||
const constantValue = tc.getConstantValue(member); | ||
if (constantValue !== undefined) { | ||
enumValues.push(constantValue); | ||
addType(typeof constantValue); | ||
} else { | ||
// try to extract the enums value; it will probably by a cast expression | ||
let initial : ts.Expression = member.initializer; | ||
if (initial) { | ||
if ((<any>initial).expression) { // node | ||
const exp = (<any>initial).expression; | ||
const text = (<any>exp).text; | ||
// if it is an expression with a text literal, chances are it is the enum convension: | ||
// CASELABEL = 'literal' as any | ||
if (text) { | ||
enumValues.push(text); | ||
addType("string"); | ||
} else if (exp.kind == ts.SyntaxKind.TrueKeyword || exp.kind == ts.SyntaxKind.FalseKeyword) { | ||
enumValues.push((exp.kind == ts.SyntaxKind.TrueKeyword)); | ||
addType("boolean"); | ||
} else { | ||
console.warn("initializer is expression for enum: " + fullName + "." + caseLabel); | ||
} | ||
} else if (initial.kind == ts.SyntaxKind.NoSubstitutionTemplateLiteral) { | ||
enumValues.push(initial.getText()); | ||
addType("string"); | ||
} else if (initial.kind == ts.SyntaxKind.NullKeyword) { | ||
enumValues.push(null); | ||
addType("null"); | ||
} | ||
} else if ((<any>initial).kind && (<any>initial).kind == ts.SyntaxKind.NoSubstitutionTemplateLiteral) { | ||
enumValues.push(initial.getText()); | ||
} | ||
@@ -272,3 +334,4 @@ } | ||
definition.type = "string"; | ||
if (enumTypes.length) | ||
definition.type = (enumTypes.length == 1) ? enumTypes[0] : enumTypes; | ||
@@ -282,2 +345,76 @@ if (enumValues.length > 0) { | ||
private getUnionDefinition(unionType: ts.UnionType, prop: ts.Symbol, tc: ts.TypeChecker, unionModifier: string, definition: any): any { | ||
const enumValues = []; | ||
const simpleTypes = []; | ||
const schemas = []; | ||
const addSimpleType = (type) => { | ||
if (simpleTypes.indexOf(type) == -1) | ||
simpleTypes.push(type); | ||
}; | ||
const addEnumValue = (val) => { | ||
if (enumValues.indexOf(val) == -1) | ||
enumValues.push(val); | ||
}; | ||
for (let i = 0; i < unionType.types.length; ++i) { | ||
const valueType = unionType.types[i]; | ||
const value = this.extractLiteralValue(valueType); | ||
if (value !== undefined) { | ||
addEnumValue(value); | ||
} | ||
else { | ||
const def = this.getTypeDefinition(unionType.types[i], tc); | ||
if (def.type === "undefined") { | ||
if (prop) | ||
(<any>prop).mayBeUndefined = true; | ||
} | ||
else { | ||
const keys = Object.keys(def); | ||
if (keys.length == 1 && keys[0] == "type") | ||
addSimpleType(def.type); | ||
else | ||
schemas.push(def); | ||
} | ||
} | ||
} | ||
if (enumValues.length > 0) { | ||
// If the values are true and false, just add "boolean" as simple type | ||
const isOnlyBooleans = enumValues.length == 2 && | ||
typeof enumValues[0] === "boolean" && | ||
typeof enumValues[1] === "boolean" && | ||
enumValues[0] !== enumValues[1]; | ||
if (isOnlyBooleans) { | ||
addSimpleType("boolean"); | ||
} else { | ||
const enumSchema : any = { enum: enumValues }; | ||
// If all values are of the same primitive type, add a "type" field to the schema | ||
if (enumValues.every((x) => { return typeof x == "string"; })) | ||
enumSchema.type = "string"; | ||
else if (enumValues.every((x) => { return typeof x == "number"; })) | ||
enumSchema.type = "number"; | ||
else if (enumValues.every((x) => { return typeof x == "boolean"; })) | ||
enumSchema.type = "boolean"; | ||
schemas.push(enumSchema); | ||
} | ||
} | ||
if (simpleTypes.length > 0) | ||
schemas.push({ type: simpleTypes.length == 1 ? simpleTypes[0] : simpleTypes }); | ||
if (schemas.length == 1) { | ||
for (let k in schemas[0]) | ||
definition[k] = schemas[0][k]; | ||
} | ||
else { | ||
definition[unionModifier] = schemas; | ||
} | ||
return definition; | ||
} | ||
private getClassDefinition(clazzType: ts.Type, tc: ts.TypeChecker, definition: any): any { | ||
@@ -301,6 +438,6 @@ const node = clazzType.getSymbol().getDeclarations()[0]; | ||
} | ||
const typ = tc.getTypeAtLocation(indexSignature.type); | ||
const def = this.getTypeDefinition(typ, tc, undefined, "anyOf"); | ||
if(isStringIndexed) { | ||
@@ -353,3 +490,3 @@ definition.type = "object"; | ||
const requiredProps = props.reduce((required, prop) => { | ||
if (!(prop.flags & ts.SymbolFlags.Optional)) { | ||
if (!(prop.flags & ts.SymbolFlags.Optional) && !(<any>prop).mayBeUndefined) { | ||
required.push(prop.getName()); | ||
@@ -366,9 +503,54 @@ } | ||
} | ||
private getTypeDefinition(typ: ts.Type, tc: ts.TypeChecker, asRef = this.args.useRef, unionModifier: string = "oneOf", prop? : ts.Symbol, reffedType?: ts.Symbol): any { | ||
private simpleTypesAllowedProperties = [ | ||
"type", | ||
"description" | ||
]; | ||
private addSimpleType(def: any, type: string) { | ||
for (let k in def) { | ||
if (this.simpleTypesAllowedProperties.indexOf(k) == -1) | ||
return false; | ||
} | ||
if (!def.type) { | ||
def.type = type; | ||
} else if (def.type.push) { | ||
if (!(<Object[]>def.type).every((val) => { return typeof val == "string"; })) | ||
return false; | ||
if (def.type.indexOf('null') == -1) | ||
def.type.push('null'); | ||
} else { | ||
if (typeof def.type != "string") | ||
return false; | ||
if (def.type != 'null') | ||
def.type = [ def.type, 'null' ]; | ||
} | ||
return true; | ||
} | ||
private makeNullable(def: any) { | ||
if (!this.addSimpleType(def, 'null')) { | ||
let union = def.oneOf || def.anyOf; | ||
if (union) | ||
union.push({ type: 'null' }); | ||
else { | ||
const subdef = {}; | ||
for (var k in def) { | ||
subdef[k] = def[k]; | ||
delete def[k]; | ||
} | ||
def.anyOf = [ subdef, { type: 'null' } ]; | ||
} | ||
} | ||
return def; | ||
} | ||
private getTypeDefinition(typ: ts.Type, tc: ts.TypeChecker, asRef = this.args.useRef, unionModifier: string = "anyOf", prop? : ts.Symbol, reffedType?: ts.Symbol): any { | ||
const definition : any = {}; // real definition | ||
let returnedDefinition = definition; // returned definition, may be a $ref | ||
const symbol = typ.getSymbol(); | ||
const isRawType = (!symbol || symbol.name == "integer" || symbol.name == "Array" || symbol.name == "Date"); | ||
@@ -384,5 +566,5 @@ | ||
} | ||
// aliased types must be handled slightly different | ||
const asTypeAliasRef = asRef && ((reffedType && this.args.useTypeAliasRef) || isStringEnum); | ||
const asTypeAliasRef = asRef && reffedType && (this.args.useTypeAliasRef || isStringEnum); | ||
if (!asTypeAliasRef) { | ||
@@ -394,3 +576,3 @@ if (isRawType || (typ.getFlags() & ts.TypeFlags.Anonymous)) { | ||
} | ||
let fullTypeName = ""; | ||
@@ -402,3 +584,3 @@ if (asTypeAliasRef) { | ||
} | ||
if (asRef) { | ||
@@ -409,7 +591,13 @@ returnedDefinition = { | ||
} | ||
// Parse comments | ||
this.parseCommentsIntoDefinition(reffedType, definition); // handle comments in the type alias declaration | ||
this.parseCommentsIntoDefinition(prop || symbol, returnedDefinition); | ||
const otherAnnotations = {}; | ||
this.parseCommentsIntoDefinition(reffedType, definition, otherAnnotations); // handle comments in the type alias declaration | ||
if (prop) | ||
this.parseCommentsIntoDefinition(prop, returnedDefinition, otherAnnotations); | ||
else | ||
this.parseCommentsIntoDefinition(symbol, definition, otherAnnotations); | ||
// Create the actual definition only if is an inline definition, or | ||
// if it will be a $ref and it is not yet created | ||
if (!asRef || !this.reffedDefinitions[fullTypeName]) { | ||
@@ -422,19 +610,14 @@ if (asRef) { // must be here to prevent recursivity problems | ||
} | ||
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); | ||
}); | ||
this.getUnionDefinition(typ as ts.UnionType, prop, tc, unionModifier, definition); | ||
} else if (typ.flags & ts.TypeFlags.Intersection) { | ||
definition.allOf = []; | ||
const types = (<ts.IntersectionType> typ).types; | ||
for (let i = 0; i < types.length; ++i) { | ||
definition.allOf.push(this.getTypeDefinition(types[i], tc)); | ||
} | ||
} else if (isRawType) { | ||
this.getDefinitionForRootType(typ, tc, reffedType, definition); | ||
} else if (node.kind == ts.SyntaxKind.EnumDeclaration) { | ||
} else if (node && (node.kind == ts.SyntaxKind.EnumDeclaration || node.kind == ts.SyntaxKind.EnumMember)) { | ||
this.getEnumDefinition(typ, tc, definition); | ||
@@ -444,4 +627,7 @@ } else { | ||
} | ||
if (otherAnnotations['nullable']) | ||
this.makeNullable(definition); | ||
} | ||
return returnedDefinition; | ||
@@ -463,12 +649,25 @@ } | ||
} | ||
public getSchemaForSymbols(symbols: { [name: string]: ts.Type }): any { | ||
const root = { | ||
"$schema": "http://json-schema.org/draft-04/schema#", | ||
definitions: {} | ||
}; | ||
for (const id in symbols) { | ||
root.definitions[id] = this.getTypeDefinition(symbols[id], this.tc, this.args.useRootRef); | ||
} | ||
return root; | ||
} | ||
} | ||
export function getProgramFromFiles(files: string[]): ts.Program { | ||
export function getProgramFromFiles(files: string[], compilerOptions: ts.CompilerOptions = {}): ts.Program { | ||
// use built-in default options | ||
const options: ts.CompilerOptions = { | ||
const options: ts.CompilerOptions = { | ||
noEmit: true, emitDecoratorMetadata: true, experimentalDecorators: true, target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS | ||
}; | ||
return ts.createProgram(files, options); | ||
for (const k in compilerOptions) | ||
options[k] = compilerOptions[k]; | ||
return ts.createProgram(files, options); | ||
} | ||
export function generateSchema(program: ts.Program, fullTypeName: string, args = getDefaultArgs()) { | ||
@@ -482,11 +681,12 @@ const tc = program.getTypeChecker(); | ||
const allSymbols: { [name: string]: ts.Type } = {}; | ||
const userSymbols: { [name: string]: ts.Type } = {}; | ||
const inheritingTypes: { [baseName: string]: string[] } = {}; | ||
program.getSourceFiles().forEach(sourceFile => { | ||
/*console.log(sourceFile.fileName); | ||
program.getSourceFiles().forEach((sourceFile, sourceFileIdx) => { | ||
/*console.log(sourceFile.fileName); | ||
if(sourceFile.fileName.indexOf("main.ts") > -1) { | ||
debugger; | ||
} */ | ||
} */ | ||
function inspect(node: ts.Node, tc: ts.TypeChecker) { | ||
if (node.kind == ts.SyntaxKind.ClassDeclaration | ||
@@ -499,14 +699,17 @@ || node.kind == ts.SyntaxKind.InterfaceDeclaration | ||
let fullName = tc.getFullyQualifiedName((<any>node).symbol) | ||
// remove file name | ||
// TODO: we probably don't want this eventually, | ||
// TODO: we probably don't want this eventually, | ||
// as same types can occur in different files and will override eachother in allSymbols | ||
// This means atm we can't generate all types in large programs. | ||
fullName = fullName.replace(/".*"\./, ""); | ||
allSymbols[fullName] = nodeType; | ||
//if (sourceFileIdx == 0) | ||
if (!sourceFile.hasNoDefaultLib) | ||
userSymbols[fullName] = nodeType; | ||
const baseTypes = nodeType.getBaseTypes() || []; | ||
baseTypes.forEach(baseType => { | ||
@@ -527,4 +730,9 @@ var baseName = tc.typeToString(baseType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType); | ||
const generator = new JsonSchemaGenerator(allSymbols, inheritingTypes, tc, args); | ||
let definition = generator.getSchemaForSymbol(fullTypeName); | ||
let definition : any; | ||
if (fullTypeName == '*') { // All types in file(s) | ||
definition = generator.getSchemaForSymbols(userSymbols); | ||
} | ||
else { // Use specific type as root object | ||
definition = generator.getSchemaForSymbol(fullTypeName); | ||
} | ||
return definition; | ||
@@ -548,3 +756,3 @@ } else { | ||
const configObject = result.config; | ||
const configParseResult = ts.parseJsonConfigFileContent(configObject, ts.sys, path.dirname(configFileName), {}, configFileName); | ||
@@ -557,6 +765,6 @@ const options = configParseResult.options; | ||
delete options.declaration; | ||
const program = ts.createProgram(configParseResult.fileNames, options); | ||
return program; | ||
//const conf = ts.convertCompilerOptionsFromJson(null, path.dirname(filePattern), "tsconfig.json"); | ||
@@ -569,7 +777,9 @@ } | ||
} else { | ||
program = TJS.getProgramFromFiles(glob.sync(filePattern)); | ||
program = TJS.getProgramFromFiles(glob.sync(filePattern), { | ||
strictNullChecks: args.strictNullChecks | ||
}); | ||
} | ||
const definition = TJS.generateSchema(program, fullTypeName, args); | ||
const json = JSON.stringify(definition, null, 4) + "\n"; | ||
@@ -581,3 +791,3 @@ if(args.out) { | ||
} | ||
}); | ||
}); | ||
} else { | ||
@@ -610,2 +820,4 @@ process.stdout.write(json); | ||
.describe("required", "Create required array for non-optional properties.") | ||
.boolean("strictNullChecks").default("strictNullChecks", defaultArgs.strictNullChecks) | ||
.describe("strictNullChecks", "(TypeScript 2) Make values non-nullable by default.") | ||
.alias("out", "o") | ||
@@ -624,2 +836,3 @@ .describe("out", "The output file, defaults to using stdout") | ||
generateRequired: args.required, | ||
strictNullChecks: args.strictNullChecks, | ||
out: args.out | ||
@@ -640,2 +853,2 @@ }); | ||
console.log(JSON.stringify(result)); | ||
*/ | ||
*/ |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
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
298994
81
6721
24
1
+ Addedtypescript@2.0.10(transitive)
- Removedtypescript@1.8.10(transitive)
Updatedtypescript@~2.0.3