diff --git a/lib/typson-schema.js b/lib/typson-schema.js index e3154a9..0672cda 100755 --- a/lib/typson-schema.js +++ b/lib/typson-schema.js @@ -28,15 +28,16 @@ var TypeScript, _, traverse; // from global scope if (underscore) { _ = underscore; } - if( _traverse) { + if (_traverse) { traverse = _traverse; } var api = {}; - var primitiveTypes = [ "string", "number", "boolean", "any" ]; - var validationKeywords = [ "type", "minimum", "exclusiveMinimum", "maximum", "exclusiveMaximum", "multipleOf", "minLength", "maxLength", "format", "pattern", "minItems", "maxItems", "uniqueItems", "default", "additionalProperties" ]; + var primitiveTypes = ["string", "number", "boolean", "any"]; + var validationKeywords = ["type", "minimum", "exclusiveMinimum", "maximum", "exclusiveMaximum", "multipleOf", "minLength", "maxLength", "format", "pattern", "minItems", "maxItems", "uniqueItems", "default", "additionalProperties"]; var annotedValidationKeywordPattern = /@[a-z.]+\s*[^@\s]+/gi; - var TypescriptASTFlags = { "optionalName" : 4, "arrayType" : 8 }; - var defaultProperties = { additionalProperties: false}; + var variableWithDash = /^'([A-z0-9]+-[A-z0-9]+)'$/gi; + var TypescriptASTFlags = {"optionalName": 4, "arrayType": 8}; + var defaultProperties = {additionalProperties: false}; /** * Creates json-schema type definitions from a type script. @@ -49,20 +50,20 @@ var TypeScript, _, traverse; // from global scope typson.tree(script).done(function (tree) { TypeScript = typson.TypeScript; var definitions = { - interfaces: {}, - enums: {} - }; + interfaces: {}, + enums: {} + }; _.each(tree, function (script) { _.each(script.moduleElements.members, function (decl) { handleDeclaration(decl, definitions, refPath, ""); }); }); - traverse(definitions.interfaces).forEach(function(item) { - if(this.key === "$ref") { + traverse(definitions.interfaces).forEach(function (item) { + if (this.key === "$ref") { var currentSchema = this.path[0]; var lastDot = currentSchema.lastIndexOf("."); - if(lastDot > -1) { + if (lastDot > -1) { this.update(fullModulePath(currentSchema.substring(0, lastDot), item, definitions.interfaces)); } } @@ -82,12 +83,12 @@ var TypeScript, _, traverse; // from global scope */ api.schema = function (script, type, id) { return Q.promise(function (resolve) { - api.definitions(script, "#/definitions").done(function(definitions) { + api.definitions(script, "#/definitions").done(function (definitions) { var schema = {}; schema.$schema = "http://json-schema.org/draft-04/schema#"; _.extend(schema, definitions[type]); delete definitions[type]; - if(id) { + if (id) { schema.id = id; } resolve(schema); @@ -98,7 +99,7 @@ var TypeScript, _, traverse; // from global scope function handleDeclaration(decl, definitions, refPath, modulePath) { if (decl.nodeType() === TypeScript.NodeType.InterfaceDeclaration || - decl.nodeType() === TypeScript.NodeType.ClassDeclaration) { + decl.nodeType() === TypeScript.NodeType.ClassDeclaration) { handleInterfaceDeclaration(decl, definitions, refPath, modulePath); } else if (decl.nodeType() === TypeScript.NodeType.ModuleDeclaration) { @@ -107,8 +108,8 @@ var TypeScript, _, traverse; // from global scope } else { _.each(decl.members.members, function (subDecl) { var subModulePath = modulePath; - if(!(decl.getModuleFlags() & TypeScript.ModuleFlags.IsWholeFile)) { - subModulePath+=(decl.name.actualText+"."); + if (!(decl.getModuleFlags() & TypeScript.ModuleFlags.IsWholeFile)) { + subModulePath += (decl.name.actualText + "."); } handleDeclaration(subDecl, definitions, refPath, subModulePath); }); @@ -128,7 +129,7 @@ var TypeScript, _, traverse; // from global scope definition.id = name; definition.type = "object"; copyComment(type, definition); - + definition.properties = {}; mergeInheritedProperties(type, definition, definitions); handlePropertyDeclaration(type, definition, definitions, refPath); @@ -137,8 +138,8 @@ var TypeScript, _, traverse; // from global scope /** - * Handles property or interface declarations and adds them to the interface definition. - * Recursive, if the property has child properties, i.e. inline object declarations, + * Handles property or interface declarations and adds them to the interface definition. + * Recursive, if the property has child properties, i.e. inline object declarations, * this function will recurse down to get all the definitions in the schema. * * @param type {object} the TypeScript AST node associated to the property declaration @@ -147,7 +148,12 @@ var TypeScript, _, traverse; // from global scope */ function handlePropertyDeclaration(type, definition, definitions, refPath) { _.each(type.members.members, function (variable) { - var property = definition.properties[variable.id.actualText] = {}; + var variableName = variable.id.actualText; + var testPath = variableWithDash.exec(variableName); + if(testPath) { + variableName = testPath[1]; + } + var property = definition.properties[variableName] = {}; copyComment(variable, property); var overridenType = property.type; var variableType = extractFullTypeName(variable.typeExpr.term); @@ -158,7 +164,7 @@ var TypeScript, _, traverse; // from global scope if (!definition.required) { definition.required = []; } - definition.required.push(variable.id.actualText); + definition.required.push(variableName); } //arrays if (variable.typeExpr.getFlags() & TypescriptASTFlags.arrayType) { @@ -193,7 +199,7 @@ var TypeScript, _, traverse; // from global scope handlePropertyDeclaration(variable.typeExpr.term, property, definitions); variableType = "any"; } - } + } //other else { propertyType = property; @@ -206,12 +212,12 @@ var TypeScript, _, traverse; // from global scope propertyType.id = definitions.enums[fullTypeName].id; propertyType.enum = _.keys(definitions.enums[fullTypeName].enumeration); addEnumDescription(definitions.enums[fullTypeName].enumeration, property); - } + } //other else if (primitiveTypes.indexOf(variableType) === -1) { - propertyType.$ref = refPath? refPath+"/"+fullTypeName: fullTypeName; + propertyType.$ref = refPath ? refPath + "/" + fullTypeName : fullTypeName; } else { - if(variableType !== "any") { + if (variableType !== "any") { propertyType.type = overridenType || variableType; } } @@ -219,20 +225,19 @@ var TypeScript, _, traverse; // from global scope } function extractFullTypeName(term) { - - if(term.actualText) { + if (term.actualText) { return term.actualText; } else { - if(term.nodeType() === TypeScript.NodeType.MemberAccessExpression) { - return extractFullTypeName(term.operand1)+"."+extractFullTypeName(term.operand2); + if (term.nodeType() === TypeScript.NodeType.MemberAccessExpression) { + return extractFullTypeName(term.operand1) + "." + extractFullTypeName(term.operand2); } } } /** * Visits every super type extended by the given type recursively and provisions the given definition with the properties of the associated super definitions. - * - * @param type {object} the TypeScript AST node associated to the interface declaration + * + * @param type {object} the TypeScript AST node associated to the interface declaration * @param definition {object} the definition to be provisionned * @param definitions {object} the set of handled interface and enum definitions */ @@ -245,7 +250,7 @@ var TypeScript, _, traverse; // from global scope // recursive call mergeInheritedProperties(superType, definition, definitions); // merges properties - for(var superKey in superDefinition.properties) { + for (var superKey in superDefinition.properties) { definition.properties[superKey] = superDefinition.properties[superKey]; } // merges required @@ -259,9 +264,9 @@ var TypeScript, _, traverse; // from global scope } } }); - } + } } - + /** * Handles enum declaration as new definition and registers it in the global set of enum definitions * @@ -356,14 +361,16 @@ var TypeScript, _, traverse; // from global scope function addEnumDescription(enumeration, property) { - if(enumeration && _.values(enumeration).join("")) { + if (enumeration && _.values(enumeration).join("")) { var values = ["Value"]; values = values.concat(_.keys(enumeration)); - var max = _.max(values, function(value) { return value.length; }).length; - var table = "\n\n| "+values[0] + new Array(max+3-values[0].length).join(" ") + "| Description"; + var max = _.max(values, function (value) { + return value.length; + }).length; + var table = "\n\n| " + values[0] + new Array(max + 3 - values[0].length).join(" ") + "| Description"; table += "\n|-"; - _.each(enumeration, function(comment, value) { - table += "\n| `"+value+"`"+ new Array(max+1-value.length).join(" ") + "|" + (comment ? " "+comment : "|"); + _.each(enumeration, function (comment, value) { + table += "\n| `" + value + "`" + new Array(max + 1 - value.length).join(" ") + "|" + (comment ? " " + comment : "|"); }); property.description = property.description || ""; property.description += table; @@ -381,27 +388,27 @@ var TypeScript, _, traverse; // from global scope */ function fullModulePath(refModule, target, types) { var path = target.split("/"); - var prefix = path.splice(0, path.length-1).join("/"); + var prefix = path.splice(0, path.length - 1).join("/"); target = path.splice(-1)[0]; var refPath = refModule.split("."); - while(refPath.length > 0) { - var candidate = refPath.join(".")+"."+target; - if(candidate in types) { + while (refPath.length > 0) { + var candidate = refPath.join(".") + "." + target; + if (candidate in types) { target = candidate; break; } - refPath = refPath.slice(0, refPath.length-1); + refPath = refPath.slice(0, refPath.length - 1); } return prefix.length > 0 ? prefix + "/" + target : target; } - api.exec = function(script, type) { - if(type) { - api.schema(script, type).done(function(schema) { + api.exec = function (script, type) { + if (type) { + api.schema(script, type).done(function (schema) { console.log(JSON.stringify(schema, null, 2)); }); } else { - api.definitions(script).done(function(definitions) { + api.definitions(script).done(function (definitions) { console.log(JSON.stringify(definitions, null, 2)); }); }