Skip to content

Commit

Permalink
Using a different service to renderize UML
Browse files Browse the repository at this point in the history
  • Loading branch information
remojansen committed Nov 17, 2016
1 parent 2d4244c commit dda2323
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 110 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ source files.

The `getDiagram` function uses other core components:

- **Parser**: Traversed the TypeScript AST to create a `ClassDetails` data structure.
- **Serializer**: Transfroms `ClassDetails` data structure into the UML DSL.
- **Parser**: Traversed the TypeScript AST to create a `EntityDetails` data structure.
- **Serializer**: Transfroms `EntityDetails` data structure into the UML DSL.
- **Renderer**: Transforms the DSL into a SVG class diagram.

The function returns a `Promise<string>`. If the promise
Expand All @@ -51,7 +51,7 @@ Feel free to send PRs:
## Resources
I used the following links during the hackathon:
- https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API
- https://github.com/skanaar/nomnoml
- http://yuml.me/
- https://code.visualstudio.com/docs/extensionAPI/overview
- https://code.visualstudio.com/Docs/extensions/example-hello-world
- https://code.visualstudio.com/docs/extensionAPI/vscode-api
77 changes: 40 additions & 37 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
{
"name": "tsuml",
"displayName": "TsUML",
"description": "Generate class diagrams for TypeScript",
"version": "0.0.1",
"publisher": "owerreloaded",
"engines": {
"vscode": "^1.5.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onCommand:extension.tsuml"
],
"main": "./out/src/extension",
"contributes": {
"commands": [{
"command": "extension.tsuml",
"title": "Show Class Diagram"
}]
},
"scripts": {
"vscode:prepublish": "tsc -p ./",
"compile": "tsc -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install"
},
"dependencies": {
"nomnoml": "0.0.4"
},
"devDependencies": {
"typescript": "^2.0.3",
"vscode": "^1.0.0",
"mocha": "^2.3.3",
"@types/node": "^6.0.40",
"@types/mocha": "^2.2.32"
}
}
"name": "tsuml",
"displayName": "TsUML",
"description": "Generate class diagrams for TypeScript",
"version": "0.0.1",
"publisher": "owerreloaded",
"engines": {
"vscode": "^1.5.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onCommand:extension.tsuml"
],
"main": "./out/src/extension",
"contributes": {
"commands": [
{
"command": "extension.tsuml",
"title": "Show Class Diagram"
}
]
},
"scripts": {
"vscode:prepublish": "tsc -p ./",
"compile": "tsc -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install"
},
"dependencies": {
"request": "^2.78.0"
},
"devDependencies": {
"@types/mocha": "^2.2.32",
"@types/node": "^6.0.40",
"@types/request": "0.0.33",
"mocha": "^2.3.3",
"typescript": "^2.0.3",
"vscode": "^1.0.0"
}
}
12 changes: 0 additions & 12 deletions src/core/dts.d.ts

This file was deleted.

26 changes: 11 additions & 15 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,21 @@ import serialize from "./serializer/serializer";
import render from "./renderer/renderer";

function getDiagram(tsFilePaths: string[]): Promise<string> {
return new Promise<string>((resolve, reject) => {

if (tsFilePaths.length === 0) {
reject("Missing input files!");
}

let options = {
target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.CommonJS
};
if (tsFilePaths.length === 0) {
Promise.reject("Missing input files!");
}

let classesDetails = parse(tsFilePaths, options);
let umlStr = classesDetails.map((classDetails) => serialize(classDetails)).reduce((p, c) => `${p}\n${c}`, "");
let options = {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
};

render(umlStr).then((svg) => {
resolve(svg);
});
let entitiesDetails = parse(tsFilePaths, options);
let dsl = serialize(entitiesDetails);

return render(dsl);

});
}

export default getDiagram;
14 changes: 12 additions & 2 deletions src/core/interfaces/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,23 @@ module interfaces {
args: ArgDetails[];
}

export interface ClassDetails {
export type EntityKind = "class" | "interface" | "abstract_class";

export interface EntityDetails {
kind: EntityKind;
name: string;
props: PropDetails[];
methods: MethodDetails[];
}

export type EntityRelationshipKind = "implementation" | "extension" | "composition";

export interface EntityRelationShip {
kind: EntityRelationshipKind;
left: EntityDetails;
right: EntityDetails;
}

}

export default interfaces;

13 changes: 7 additions & 6 deletions src/core/parser/parser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as ts from "typescript";
import interfaces from "../interfaces/interfaces";

function parse(fileNames: string[], options: ts.CompilerOptions): interfaces.ClassDetails[] {
function parse(fileNames: string[], options: ts.CompilerOptions): interfaces.EntityDetails[] {

// Build a program using the set of root file names in fileNames
let program = ts.createProgram(fileNames, options);
Expand All @@ -10,7 +10,7 @@ function parse(fileNames: string[], options: ts.CompilerOptions): interfaces.Cla
let checker = program.getTypeChecker();

// The final result
let output: interfaces.ClassDetails[] = [];
let output: interfaces.EntityDetails[] = [];

// visit nodes finding exported classes
function visit(node: ts.Node) {
Expand All @@ -35,15 +35,16 @@ function parse(fileNames: string[], options: ts.CompilerOptions): interfaces.Cla
// Serialize a class symbol infomration
function serializeClass(symbol: ts.Symbol) {

let classDetails: interfaces.ClassDetails = {
let EntityDetails: interfaces.EntityDetails = {
kind: "class",
name: symbol.getName(),
props: [],
methods: []
};

classDetails.props = serializeProperties(symbol);
classDetails.methods = serializeMethods(symbol);
return classDetails;
EntityDetails.props = serializeProperties(symbol);
EntityDetails.methods = serializeMethods(symbol);
return EntityDetails;

}

Expand Down
22 changes: 14 additions & 8 deletions src/core/renderer/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { activate } from '../../extension';
import interfaces from "../interfaces/interfaces";
import * as http from "http";
import * as fs from "fs";
import * as nomnoml from "nomnoml";
import * as request from "request";

function render(umlString: string): Promise<string> {
function render(dsl: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
try {
let svg = nomnoml.renderSvg(umlString);
resolve(svg);
} catch(e) {
reject(e);
}
request.post(
"http://yuml.me/diagram/plain/class/",
{ json: { dsl_text: dsl } },
function (error, response, body) {
if (!error && response.statusCode == 200) {
resolve(body);
} else {
reject(body);
}
}
);
});
}

Expand Down
71 changes: 58 additions & 13 deletions src/core/serializer/serializer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import * as ts from "typescript";
import * as fs from "fs";
import interfaces from "../interfaces/interfaces";

function endFactory(separator: string, defaultSeparator: string = ``) {
function endFactory(separator: string, defaultSeparator = ``) {
return (index: number, collection: any[]) => {
let isLastElement = (index === (collection.length - 1));
let end = isLastElement ? defaultSeparator : `${separator}`;
Expand All @@ -11,12 +9,10 @@ function endFactory(separator: string, defaultSeparator: string = ``) {
}

let commaSparator = endFactory(",", "");
let semicolonSeparator = endFactory(";");

function serializeProps(propsDetails: interfaces.PropDetails[]) {
return propsDetails.map((propDetails, index, arr) => {
let end = semicolonSeparator(index, arr);
return `${propDetails.name}:${propDetails.type}${end}`;
return `${propDetails.name}:${propDetails.type};`;
}).reduce((prev, val) => prev + val, "");
}

Expand All @@ -30,16 +26,65 @@ function serializeArgs(argsDetails: interfaces.ArgDetails[]) {
function serializeMethods(methodsDetails: interfaces.MethodDetails[]) {
return methodsDetails.map((methodDetails, index, arr) => {
let argsStr = serializeArgs(methodDetails.args);
let end = semicolonSeparator(index, arr);
return `${methodDetails.name}(${argsStr}):${methodDetails.returnType}${end}`;
return `${methodDetails.name}(${argsStr}):${methodDetails.returnType};`;
}).reduce((prev, val) => prev + val, "");
}

function serialize(classDetails: interfaces.ClassDetails) {
let props = serializeProps(classDetails.props);
let methods = serializeMethods(classDetails.methods);
let result = `[${classDetails.name}|${props}|${methods}]\n`;
return result;
function serializeClass(EntityDetails: interfaces.EntityDetails) {
let props = serializeProps(EntityDetails.props);
let methods = serializeMethods(EntityDetails.methods);
return `[${EntityDetails.name}|${props}|${methods}{bg:steelblue}]\n`;
}

function serializeInterface(EntityDetails: interfaces.EntityDetails) {
let props = serializeProps(EntityDetails.props);
let methods = serializeMethods(EntityDetails.methods);
return `[${EntityDetails.name}|${props}|${methods}{bg:wheat}]\n`;
}

function serializeAbstractClass(EntityDetails: interfaces.EntityDetails) {
let props = serializeProps(EntityDetails.props);
let methods = serializeMethods(EntityDetails.methods);
return `[${EntityDetails.name}|${props}|${methods}{bg:plum}]\n`;
}

function serializeInheritanceRelationships(EntityDetails: interfaces.EntityDetails) {
// TODO
return "[CHILD]^-.-[PARENT]";
}

function serializeInterfaceInheritanceRelationships(EntityDetails: interfaces.EntityDetails) {
// TODO
return "[CHILD]^-[PARENT]";
}

function serializeCompositionRelationships(EntityDetails: interfaces.EntityDetails) {
// TODO
return "[CHILD]++-1>[PARENT]";
}

function serialize(entities: interfaces.EntityDetails[]) {

// Add entitites
let dsl = entities.map((entity) => {
switch(entity.kind) {
case "class":
return serializeClass(entity);
case "interface":
return serializeInterface(entity);
case "abstract_class":
return serializeAbstractClass(entity);
default:
throw new Error("Entity Kind Not Supported!");
}
}).reduce((a, b) => `${a}${b}`, "").split("<").join("-").split(">").join("-");

// Add relationships
dsl += entities.map(serializeInheritanceRelationships).reduce((a, b) => `${a}${b}`, "");
dsl += entities.map(serializeInterfaceInheritanceRelationships).reduce((a, b) => `${a}${b}`, "");
dsl += entities.map(serializeCompositionRelationships).reduce((a, b) => `${a}${b}`, "");

return `${dsl}`;
}

export default serialize;
Loading

0 comments on commit dda2323

Please sign in to comment.