Repository: remojansen/TsUML
Branch: master
Commit: 44694fe847ce
Files: 19
Total size: 12.5 KB
Directory structure:
gitextract_uoq8f55i/
├── .gitignore
├── .npmignore
├── .vscodeignore
├── LICENSE
├── README.md
├── package.json
├── src/
│ ├── bin/
│ │ └── index.ts
│ ├── core/
│ │ ├── emitter.ts
│ │ ├── index.ts
│ │ ├── interfaces.ts
│ │ ├── io.ts
│ │ ├── parser.ts
│ │ └── templates.ts
│ ├── demo/
│ │ ├── interfaces.ts
│ │ ├── katana.ts
│ │ ├── main.ts
│ │ └── ninja.ts
│ └── lib/
│ └── index.ts
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
out
node_modules
dist
================================================
FILE: .npmignore
================================================
src
tsconfig.json
assets
.vscodeignore
================================================
FILE: .vscodeignore
================================================
.vscode/**
.vscode-test/**
out/test/**
test/**
src/**
**/*.map
.gitignore
tsconfig.json
vsc-extension-quickstart.md
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2016-2018 Remo H. Jansen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<img src="/assets/logo.png" width="150" align="right" />
# TsUML
:construction: WORK IN PROGRESS :construction:
Generate UML diagram for your TypeScript applications powered by https://yuml.me/

## Installation
```sh
npm install -g tsuml
```
## Usage
```
tsuml --glob ./src/**/*.ts
```
The diagram generated for the code under the [demo folder](https://github.com/remojansen/TsUML/tree/master/src/demo) looks as follows:

================================================
FILE: package.json
================================================
{
"name": "tsuml",
"version": "0.0.1-alpha.8",
"description": "UML diagrams for TypeScript",
"main": "dist/lib/index.js",
"bin": {
"tsuml": "dist/bin/index.js"
},
"devDependencies": {
"@types/glob": "5.0.35",
"@types/lodash": "4.14.106",
"@types/node": "9.6.0",
"@types/opn": "5.1.0",
"@types/request": "2.47.0",
"@types/yargs": "11.0.0"
},
"dependencies": {
"chalk": "2.3.2",
"glob": "7.1.2",
"lodash": "4.17.5",
"opn": "5.3.0",
"request": "2.85.0",
"ts-simple-ast": "9.5.0",
"typescript": "2.7.2",
"yargs": "11.0.0"
},
"scripts": {
"test": "tsc -p tsconfig.json"
},
"repository": {
"type": "git",
"url": "git+https://github.com/remojansen/TsUML.git"
},
"keywords": [
"typescript",
"uml",
"diagram",
"generator",
"class"
],
"author": "Remo H. Jansen",
"license": "MIT",
"bugs": {
"url": "https://github.com/remojansen/TsUML/issues"
},
"homepage": "https://github.com/remojansen/TsUML#readme"
}
================================================
FILE: src/bin/index.ts
================================================
#! /usr/bin/env node
import chalk from "chalk";
import * as yargs from "yargs";
import { getUrl } from "../core";
(async () => {
try {
if (yargs.argv.help) {
console.log(chalk.yellowBright("tsuml --glob ./src/**/*.ts"));
}
const pattern = yargs.argv.glob;
if (!pattern) {
console.log(chalk.redBright("Missing --glob"));
} else {
const url = await getUrl("./tsconfig.json", pattern);
const opn = require("opn");
opn(url);
}
} catch(e) {
console.log(chalk.redBright(e));
}
})();
================================================
FILE: src/core/emitter.ts
================================================
import Ast, * as SimpleAST from "ts-simple-ast";
import * as ts from "typescript";
import { flatten, join } from "lodash";
import * as path from "path";
import { PropertyDetails, MethodDetails, HeritageClause } from "./interfaces";
import { templates }from "./templates";
import { download } from "./io";
export function emitSingleClass(name: string, properties: PropertyDetails[], methods: MethodDetails[]) {
return templates.class(name, properties, methods);
}
export function emitSingleInterface(name: string, properties: PropertyDetails[], methods: MethodDetails[]) {
return templates.interface(name, properties, methods);
}
export function emitHeritageClauses(heritageClauses: HeritageClause[]) {
return heritageClauses.map((heritageClause) =>
templates.implementsOrExtends(heritageClause.clause, heritageClause.className)
);
}
================================================
FILE: src/core/index.ts
================================================
import * as fs from "fs";
import chalk from "chalk";
import { flatten, join } from "lodash";
import { findFilesByGlob, download } from "./io";
import { getAst, parseClasses, parseInterfaces, parseHeritageClauses } from "./parser";
import { emitSingleClass, emitSingleInterface, emitHeritageClauses } from "./emitter";
async function getDsl(tsConfigPath: string, pattern: string) {
const sourceFilesPaths = await findFilesByGlob(pattern);
console.log(
chalk.yellowBright(
"Matched files:\n" + sourceFilesPaths.reduce((p, c) => `${p}${c}\n`, "")
)
);
const ast = getAst(tsConfigPath, sourceFilesPaths);
const files = ast.getSourceFiles();
// parser
const declarations = files.map(f => {
const classes = f.getClasses();
const interfaces = f.getInterfaces();
const path = f.getFilePath();
return {
fileName: path,
classes: classes.map(parseClasses),
heritageClauses: classes.map(parseHeritageClauses),
interfaces: interfaces.map(parseInterfaces)
};
});
// emitter
const entities = declarations.map(d => {
const classes = d.classes.map((c) => emitSingleClass(c.className, c.properties, c.methods));
const interfaces = d.interfaces.map((i) => emitSingleInterface(i.interfaceName, i.properties, i.methods));
const heritageClauses = d.heritageClauses.map(emitHeritageClauses);
return [...classes, ...interfaces, ...heritageClauses];
});
return join(flatten(entities), ",");
}
export async function getUrl(tsConfigPath: string, pattern: string) {
const dsl = await getDsl(tsConfigPath, pattern);
return await download(dsl);
}
================================================
FILE: src/core/interfaces.ts
================================================
export interface MethodDetails {
name: string;
}
export interface PropertyDetails {
name: string;
}
export interface HeritageClause {
clause: string;
className: string;
}
================================================
FILE: src/core/io.ts
================================================
import * as glob from "glob";
import * as request from "request";
import * as fs from "fs";
export async function findFilesByGlob(pattern: string) {
return new Promise<string[]>((res, rej) => {
glob(pattern, (err, files) => {
if (err) {
rej(err);
} else {
res(files);
}
});
});
}
export async function download(dsl: string) {
return new Promise<string>((resolve, reject) => {
const url = "https://yuml.me/diagram/plain/class/";
const options = {
form: {
dsl_text: dsl
}
};
request.post(url, options, (err, res, body) => {
if (err) {
reject(err);
}
const svgFileName = body.replace(".png", ".svg");
const diagramUrl = `${url}${svgFileName}`;
resolve(diagramUrl);
});
});
};
================================================
FILE: src/core/parser.ts
================================================
import Ast, * as SimpleAST from "ts-simple-ast";
import * as ts from "typescript";
import { flatten, join } from "lodash";
import { PropertyDetails, MethodDetails, HeritageClause } from "./interfaces";
export function getAst(tsConfigPath: string, sourceFilesPaths?: string[]) {
const ast = new Ast({
tsConfigFilePath: tsConfigPath,
addFilesFromTsConfig: !Array.isArray(sourceFilesPaths)
});
if (sourceFilesPaths) {
ast.addExistingSourceFiles(sourceFilesPaths);
}
return ast;
}
export function parseClasses(classDeclaration: SimpleAST.ClassDeclaration) {
const className = classDeclaration.getSymbol()!.getName();
const propertyDeclarations = classDeclaration.getProperties();
const methodDeclarations = classDeclaration.getMethods();
const properties = propertyDeclarations.map(property => {
const sym = property.getSymbol();
if (sym) {
return {
name: sym.getName()
};
}
}).filter((p) => p !== undefined) as PropertyDetails[];
const methods = methodDeclarations.map(method => {
const sym = method.getSymbol();
if (sym) {
return {
name: sym.getName()
}
}
}).filter((p) => p !== undefined) as MethodDetails[];
return { className, properties, methods };
}
export function parseInterfaces(interfaceDeclaration: SimpleAST.InterfaceDeclaration) {
const interfaceName = interfaceDeclaration.getSymbol()!.getName();
const propertyDeclarations = interfaceDeclaration.getProperties();
const methodDeclarations = interfaceDeclaration.getMethods();
const properties = propertyDeclarations.map(property => {
const sym = property.getSymbol();
if (sym) {
return {
name: sym.getName()
}
}
}).filter((p) => p !== undefined) as PropertyDetails[];
const methods = methodDeclarations.map(method => {
const sym = method.getSymbol();
if (sym) {
return {
name: sym.getName()
}
}
}).filter((p) => p !== undefined) as MethodDetails[];
return { interfaceName, properties, methods };
}
export function parseHeritageClauses(classDeclaration: SimpleAST.ClassDeclaration) {
const className = classDeclaration.getSymbol()!.getName();
const extended = classDeclaration.getExtends();
const implemented = classDeclaration.getImplements();
let heritageClauses: HeritageClause[] = [];
if (extended) {
const identifier = extended.getChildrenOfKind(ts.SyntaxKind.Identifier)[0];
if (identifier) {
const sym = identifier.getSymbol();
if (sym) {
heritageClauses.push(
{
clause: sym.getName(),
className
}
);
}
}
}
if (implemented) {
implemented.forEach(i => {
const identifier = i.getChildrenOfKind(ts.SyntaxKind.Identifier)[0];
if (identifier) {
const sym = identifier.getSymbol();
if (sym) {
heritageClauses.push(
{
clause: sym.getName(),
className
}
);
}
}
});
}
return heritageClauses;
}
================================================
FILE: src/core/templates.ts
================================================
import { PropertyDetails, MethodDetails} from "./interfaces";
export const templates = {
composition: "+->",
implementsOrExtends: (abstraction: string, implementation: string) => {
return (
`${templates.plainClassOrInterface(abstraction)}` +
`^-${templates.plainClassOrInterface(implementation)}`
);
},
plainClassOrInterface: (name: string) => `[${name}]`,
colorClass: (name: string) => `[${name}{bg:skyblue}]`,
colorInterface: (name: string) => `[${name}{bg:palegreen}]`,
class: (name: string, props: PropertyDetails[], methods: MethodDetails[]) => {
const pTemplate = (property: PropertyDetails) => `${property.name};`;
const mTemplate = (method: MethodDetails) => `${method.name}();`;
return (
`${templates.colorClass(name)}` +
`[${name}|${props.map(pTemplate).join("")}|${methods.map(mTemplate).join("")}]`
);
},
interface: (
name: string,
props: PropertyDetails[],
methods: MethodDetails[]
) => {
const pTemplate = (property: PropertyDetails) => `${property.name};`;
const mTemplate = (method: MethodDetails) => `${method.name}();`;
return (
`${templates.colorInterface(name)}` +
`[${name}|${props.map(pTemplate).join("")}|${methods.map(mTemplate).join("")}]`
);
}
};
================================================
FILE: src/demo/interfaces.ts
================================================
export interface Weapon {
tryHit(fromDistance: number): boolean;
}
export interface Named {
name: string;
}
================================================
FILE: src/demo/katana.ts
================================================
import { Weapon, Named } from "./interfaces";
export class BaseWeapon {
damage = 25;
}
export class Katana extends BaseWeapon implements Weapon, Named {
name = "Katana";
public tryHit(fromDistance: number) {
return fromDistance <= 2;
}
}
================================================
FILE: src/demo/main.ts
================================================
import { Ninja } from "./ninja";
import { Katana } from "./katana";
const ninja = new Ninja(new Katana());
ninja.fight(5);
================================================
FILE: src/demo/ninja.ts
================================================
import { Weapon } from "./interfaces";
export class Ninja {
private _weapon: Weapon;
public constructor(weapon: Weapon) {
this._weapon = weapon;
}
public fight(fromDistance: number) {
return this._weapon.tryHit(fromDistance);
}
}
================================================
FILE: src/lib/index.ts
================================================
export { getUrl } from "../core";
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["dom", "es2015"],
"strict": true,
"outDir": "dist"
}
}
gitextract_uoq8f55i/ ├── .gitignore ├── .npmignore ├── .vscodeignore ├── LICENSE ├── README.md ├── package.json ├── src/ │ ├── bin/ │ │ └── index.ts │ ├── core/ │ │ ├── emitter.ts │ │ ├── index.ts │ │ ├── interfaces.ts │ │ ├── io.ts │ │ ├── parser.ts │ │ └── templates.ts │ ├── demo/ │ │ ├── interfaces.ts │ │ ├── katana.ts │ │ ├── main.ts │ │ └── ninja.ts │ └── lib/ │ └── index.ts └── tsconfig.json
SYMBOL INDEX (22 symbols across 8 files)
FILE: src/core/emitter.ts
function emitSingleClass (line 9) | function emitSingleClass(name: string, properties: PropertyDetails[], me...
function emitSingleInterface (line 13) | function emitSingleInterface(name: string, properties: PropertyDetails[]...
function emitHeritageClauses (line 17) | function emitHeritageClauses(heritageClauses: HeritageClause[]) {
FILE: src/core/index.ts
function getDsl (line 8) | async function getDsl(tsConfigPath: string, pattern: string) {
function getUrl (line 46) | async function getUrl(tsConfigPath: string, pattern: string) {
FILE: src/core/interfaces.ts
type MethodDetails (line 1) | interface MethodDetails {
type PropertyDetails (line 5) | interface PropertyDetails {
type HeritageClause (line 9) | interface HeritageClause {
FILE: src/core/io.ts
function findFilesByGlob (line 5) | async function findFilesByGlob(pattern: string) {
function download (line 17) | async function download(dsl: string) {
FILE: src/core/parser.ts
function getAst (line 6) | function getAst(tsConfigPath: string, sourceFilesPaths?: string[]) {
function parseClasses (line 17) | function parseClasses(classDeclaration: SimpleAST.ClassDeclaration) {
function parseInterfaces (line 44) | function parseInterfaces(interfaceDeclaration: SimpleAST.InterfaceDeclar...
function parseHeritageClauses (line 71) | function parseHeritageClauses(classDeclaration: SimpleAST.ClassDeclarati...
FILE: src/demo/interfaces.ts
type Weapon (line 1) | interface Weapon {
type Named (line 5) | interface Named {
FILE: src/demo/katana.ts
class BaseWeapon (line 3) | class BaseWeapon {
class Katana (line 7) | class Katana extends BaseWeapon implements Weapon, Named {
method tryHit (line 9) | public tryHit(fromDistance: number) {
FILE: src/demo/ninja.ts
class Ninja (line 3) | class Ninja {
method constructor (line 5) | public constructor(weapon: Weapon) {
method fight (line 8) | public fight(fromDistance: number) {
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (14K chars).
[
{
"path": ".gitignore",
"chars": 21,
"preview": "out\nnode_modules\ndist"
},
{
"path": ".npmignore",
"chars": 38,
"preview": "src\ntsconfig.json\nassets\n.vscodeignore"
},
{
"path": ".vscodeignore",
"chars": 116,
"preview": ".vscode/**\n.vscode-test/**\nout/test/**\ntest/**\nsrc/**\n**/*.map\n.gitignore\ntsconfig.json\nvsc-extension-quickstart.md\n"
},
{
"path": "LICENSE",
"chars": 1076,
"preview": "MIT License\n\nCopyright (c) 2016-2018 Remo H. Jansen\n\nPermission is hereby granted, free of charge, to any person obtaini"
},
{
"path": "README.md",
"chars": 488,
"preview": "<img src=\"/assets/logo.png\" width=\"150\" align=\"right\" />\n\n# TsUML\n\n:construction: WORK IN PROGRESS :construction:\n\nGener"
},
{
"path": "package.json",
"chars": 1033,
"preview": "{\n \"name\": \"tsuml\",\n \"version\": \"0.0.1-alpha.8\",\n \"description\": \"UML diagrams for TypeScript\",\n \"main\": \"dist/lib/i"
},
{
"path": "src/bin/index.ts",
"chars": 613,
"preview": "#! /usr/bin/env node\n\nimport chalk from \"chalk\";\nimport * as yargs from \"yargs\";\nimport { getUrl } from \"../core\";\n\n(asy"
},
{
"path": "src/core/emitter.ts",
"chars": 862,
"preview": "import Ast, * as SimpleAST from \"ts-simple-ast\";\nimport * as ts from \"typescript\";\nimport { flatten, join } from \"lodash"
},
{
"path": "src/core/index.ts",
"chars": 1628,
"preview": "import * as fs from \"fs\";\nimport chalk from \"chalk\";\nimport { flatten, join } from \"lodash\";\nimport { findFilesByGlob, d"
},
{
"path": "src/core/interfaces.ts",
"chars": 189,
"preview": "export interface MethodDetails {\n name: string;\n}\n\nexport interface PropertyDetails {\n name: string;\n}\n\nexport int"
},
{
"path": "src/core/io.ts",
"chars": 931,
"preview": "import * as glob from \"glob\";\nimport * as request from \"request\";\nimport * as fs from \"fs\";\n\nexport async function findF"
},
{
"path": "src/core/parser.ts",
"chars": 3505,
"preview": "import Ast, * as SimpleAST from \"ts-simple-ast\";\nimport * as ts from \"typescript\";\nimport { flatten, join } from \"lodash"
},
{
"path": "src/core/templates.ts",
"chars": 1370,
"preview": "import { PropertyDetails, MethodDetails} from \"./interfaces\";\n\nexport const templates = {\n composition: \"+->\",\n im"
},
{
"path": "src/demo/interfaces.ts",
"chars": 117,
"preview": "export interface Weapon {\n tryHit(fromDistance: number): boolean;\n}\n\nexport interface Named {\n name: string;\n}\n"
},
{
"path": "src/demo/katana.ts",
"chars": 265,
"preview": "import { Weapon, Named } from \"./interfaces\";\n\nexport class BaseWeapon {\n damage = 25;\n}\n\nexport class Katana extends"
},
{
"path": "src/demo/main.ts",
"chars": 125,
"preview": "import { Ninja } from \"./ninja\";\nimport { Katana } from \"./katana\";\n\nconst ninja = new Ninja(new Katana());\n\nninja.fight"
},
{
"path": "src/demo/ninja.ts",
"chars": 267,
"preview": "import { Weapon } from \"./interfaces\";\n\nexport class Ninja {\n private _weapon: Weapon;\n public constructor(weapon:"
},
{
"path": "src/lib/index.ts",
"chars": 34,
"preview": "export { getUrl } from \"../core\";\n"
},
{
"path": "tsconfig.json",
"chars": 149,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"module\": \"commonjs\",\n \"lib\": [\"dom\", \"es2015\"],\n \"strict\": true"
}
]
About this extraction
This page contains the full source code of the remojansen/TsUML GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (12.5 KB), approximately 3.5k tokens, and a symbol index with 22 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.