forked from remojansen/TsUML
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ad7900a
commit a9c10b7
Showing
15 changed files
with
492 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,2 @@ | ||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
|
||
# Runtime data | ||
pids | ||
*.pid | ||
*.seed | ||
|
||
# Directory for instrumented libs generated by jscoverage/JSCover | ||
lib-cov | ||
|
||
# Coverage directory used by tools like istanbul | ||
coverage | ||
|
||
# nyc test coverage | ||
.nyc_output | ||
|
||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) | ||
.grunt | ||
|
||
# node-waf configuration | ||
.lock-wscript | ||
|
||
# Compiled binary addons (http://nodejs.org/api/addons.html) | ||
build/Release | ||
|
||
# Dependency directories | ||
out | ||
node_modules | ||
jspm_packages | ||
|
||
# Optional npm cache directory | ||
.npm | ||
|
||
# Optional REPL history | ||
.node_repl_history |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,64 @@ | ||
# MVPSummit2016Hackathon | ||
# 2016 Global MVP Summit Hackathon | ||
This VS Code extension was developed during a hackathon at the 2016 Global MVP Summit. | ||
|
||
This extension allows you to auto-generate UML diagrams for TypeScript applications: | ||
|
||
![](preview.gif) | ||
|
||
## About this repo | ||
The application was finished during my flight back from Seattle to Ireland. Please do not expect this to be a production-ready extension: **IT IS JUST AN EXPERIMENT**. | ||
|
||
I decided to share the repo because it could be used as a reference in the development | ||
of VS Code extensions. | ||
|
||
You can use this repo to find out the following: | ||
|
||
- How to use the TypeScript language service to access the AST generagted by the TypeScript compiler. | ||
- How to traverse the AST. | ||
- How to access user-geerated source code from a VS Code extension. | ||
- How to create custom VS Code commands. | ||
- How to perform web request from a VS Code extension. | ||
- How to render a new page after triggering custom VS Code commands. | ||
|
||
## About the source code | ||
This repo is divided in two main components. | ||
|
||
### /src/core | ||
The `core` folder exposes a function named `getDiagram`. | ||
|
||
This function expects an array of paths to be passed as | ||
an argument. The paths should point to some TypeScript | ||
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. | ||
- **Renderer**: Transforms the DSL into a SVG class diagram. | ||
|
||
The function returns a `Promise<string>`. If the promise | ||
is fulfilled, the returned string value will contain a svg diagram. | ||
|
||
### /src/extension | ||
Contains the actial VS Code extension. It declares a new custom | ||
command. When the command is executed the current source file is | ||
passed to the code to get a class diagram. The diagram is then | ||
displayed in a new panel. | ||
|
||
### /test/ | ||
TODO | ||
|
||
### /test/data | ||
TODO | ||
|
||
## Missing features | ||
Feel free to send PRs: | ||
- Display inheritance relationships | ||
- Display composition relationships | ||
- Display "implements" | ||
|
||
## Resources | ||
I used the following links during the hackathon: | ||
- https://code.visualstudio.com/docs/extensionAPI/overview | ||
- https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API | ||
- http://www.nomnoml.com/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"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" | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
declare module "nomnoml" { | ||
|
||
interface nomnoml { | ||
renderSvg(umlString: string): string; | ||
} | ||
|
||
module nomnoml {} | ||
|
||
var nomnoml: nomnoml; | ||
export = nomnoml; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import * as ts from "typescript"; | ||
import parse from "./parser/parser"; | ||
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 | ||
}; | ||
|
||
let classesDetails = parse(tsFilePaths, options); | ||
let umlStr = classesDetails.map((classDetails) => serialize(classDetails)).reduce((p, c) => `${p}\n${c}`, ""); | ||
|
||
render(umlStr).then((svg) => { | ||
resolve(svg); | ||
}); | ||
|
||
}); | ||
} | ||
|
||
export default getDiagram; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
module interfaces { | ||
|
||
export interface PropDetails { | ||
name: string; | ||
type: string; | ||
} | ||
|
||
export interface ArgDetails { | ||
name: string; | ||
type: string; | ||
} | ||
|
||
export interface MethodDetails { | ||
name: string; | ||
returnType: string; | ||
args: ArgDetails[]; | ||
} | ||
|
||
export interface ClassDetails { | ||
name: string; | ||
props: PropDetails[]; | ||
methods: MethodDetails[]; | ||
} | ||
|
||
} | ||
|
||
export default interfaces; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import * as ts from "typescript"; | ||
import interfaces from "../interfaces/interfaces"; | ||
|
||
function parse(fileNames: string[], options: ts.CompilerOptions): interfaces.ClassDetails[] { | ||
|
||
// Build a program using the set of root file names in fileNames | ||
let program = ts.createProgram(fileNames, options); | ||
|
||
// Get the checker, we will use it to find more about classes | ||
let checker = program.getTypeChecker(); | ||
|
||
// The final result | ||
let output: interfaces.ClassDetails[] = []; | ||
|
||
// visit nodes finding exported classes | ||
function visit(node: ts.Node) { | ||
|
||
// Only consider exported nodes | ||
if (!isNodeExported(node)) { | ||
return; | ||
} | ||
|
||
if (node.kind === ts.SyntaxKind.ClassDeclaration) { | ||
// This is a top level class, get its symbol | ||
let symbol = checker.getSymbolAtLocation((<ts.ClassDeclaration>node).name); | ||
output.push(serializeClass(symbol)); | ||
} | ||
else if (node.kind === ts.SyntaxKind.ModuleDeclaration) { | ||
// This is a namespace, visit its children | ||
ts.forEachChild(node, visit); | ||
} | ||
|
||
} | ||
|
||
// Serialize a class symbol infomration | ||
function serializeClass(symbol: ts.Symbol) { | ||
|
||
let classDetails: interfaces.ClassDetails = { | ||
name: symbol.getName(), | ||
props: [], | ||
methods: [] | ||
}; | ||
|
||
classDetails.props = serializeProperties(symbol); | ||
classDetails.methods = serializeMethods(symbol); | ||
return classDetails; | ||
|
||
} | ||
|
||
function serializeProperties(symbol: ts.Symbol): interfaces.PropDetails[] { | ||
|
||
let props: interfaces.PropDetails[] = []; | ||
|
||
ts.forEachChild(symbol.valueDeclaration, (node) => { | ||
|
||
if (node.kind === ts.SyntaxKind.PropertyDeclaration) { | ||
|
||
let symbol = checker.getSymbolAtLocation((<ts.PropertyDeclaration>node).name); | ||
let propertyType = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration); | ||
|
||
props.push({ | ||
name: symbol.name, | ||
type: checker.typeToString(propertyType) | ||
}); | ||
|
||
} | ||
|
||
}); | ||
|
||
return props; | ||
|
||
} | ||
|
||
function serializeMethods(symbol: ts.Symbol): interfaces.MethodDetails[] { | ||
|
||
let methods: interfaces.MethodDetails[] = []; | ||
|
||
ts.forEachChild(symbol.valueDeclaration, (node) => { | ||
|
||
if (node.kind === ts.SyntaxKind.MethodDeclaration) { | ||
|
||
let symbol = checker.getSymbolAtLocation((<ts.MethodDeclaration>node).name); | ||
let methodType = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration); | ||
let methodSignature = methodType.getCallSignatures()[0]; | ||
|
||
methods.push({ | ||
name: symbol.name, | ||
returnType: checker.typeToString(methodSignature.getReturnType()), | ||
args: methodSignature.getParameters().map((parameter) => { | ||
let parameterType = checker.getTypeOfSymbolAtLocation(parameter, parameter.valueDeclaration); | ||
return { | ||
name: parameter.getName(), | ||
type: checker.typeToString(parameterType) | ||
}; | ||
}) | ||
}); | ||
} | ||
|
||
}); | ||
|
||
return methods; | ||
|
||
} | ||
|
||
// True if this is visible outside this file, false otherwise | ||
function isNodeExported(node: ts.Node): boolean { | ||
return (node.flags & ts.NodeFlags.Export) !== 0 || (node.parent && node.parent.kind === ts.SyntaxKind.SourceFile); | ||
} | ||
|
||
// Visit every sourceFile in the program | ||
for (const sourceFile of program.getSourceFiles()) { | ||
// Walk the tree to search for classes | ||
ts.forEachChild(sourceFile, visit); | ||
} | ||
|
||
return output; | ||
} | ||
|
||
export default parse; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import interfaces from "../interfaces/interfaces"; | ||
import * as http from "http"; | ||
import * as fs from "fs"; | ||
import * as nomnoml from "nomnoml"; | ||
|
||
function render(umlString: string): Promise<string> { | ||
return new Promise<string>((resolve, reject) => { | ||
try { | ||
let svg = nomnoml.renderSvg(umlString); | ||
resolve(svg); | ||
} catch(e) { | ||
reject(e); | ||
} | ||
}); | ||
} | ||
|
||
export default render; |
Oops, something went wrong.