From 7ebb0c552fa0725d1f2480d47485865145de9434 Mon Sep 17 00:00:00 2001 From: demike Date: Tue, 3 May 2022 13:47:44 +0200 Subject: [PATCH] feat: provide an option for nomnoml output --- .vscode/launch.json | 44 +-- README.md | 81 ++++-- assets/uml_diagram.dsl | 29 ++ assets/uml_diagram.svg | 532 ++++++++++++++++++++++++++++-------- src/core/index.ts | 11 + src/core/tsuml2-settings.ts | 14 + 6 files changed, 558 insertions(+), 153 deletions(-) create mode 100644 assets/uml_diagram.dsl diff --git a/.vscode/launch.json b/.vscode/launch.json index 2fa9395..6a59ce1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,23 +1,23 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "skipFiles": [ - "/**" - ], - "program": "${workspaceFolder}/dist/bin/index.js", - "args": ["--glob","./src/demo/**/*.ts","-o", "./assets/uml_diagram.svg"], - "outFiles": [ - "${workspaceFolder}/dist/**/*.js" - ], - "sourceMaps": true, - "console": "integratedTerminal" - } - ] +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/dist/bin/index.js", + "args": ["--glob","./src/demo/**/*.ts","-o", "./assets/uml_diagram.svg", "--outDsl", "./assets/uml_diagram.dsl" ], + "outFiles": [ + "${workspaceFolder}/dist/**/*.js" + ], + "sourceMaps": true, + "console": "integratedTerminal" + } + ] } \ No newline at end of file diff --git a/README.md b/README.md index 6d9d481..4d69174 100644 --- a/README.md +++ b/README.md @@ -27,29 +27,32 @@ if this is not the case use the tsconfig parameter: To avoid getting unwanted interfaces / classes you might want to exclude d.ts and spec.ts files: -``` +```sh tsuml2 --glob "./src/**/!(*.d|*.spec).ts" ``` ### Options ``` - --help Show help [boolean] - --version Show version number [boolean] - --glob, -g pattern to match the source files (i.e.: ./src/**/*.ts) + --help Show help [boolean] + --version Show version number [boolean] + -g, --glob pattern to match the source files (i.e.: ./src/**/*.ts) [string] [required] - --tsconfig the path to tsconfig.json file [default: "./tsconfig.json"] - --outFile, -o the path to the output file [default: "out.svg"] - --propertyTypes show property types and method return types + --tsconfig the path to tsconfig.json file + [default: "./tsconfig.json"] + -o, --outFile the path to the output file [default: "out.svg"] + --propertyTypes show property types and method return types [boolean] [default: true] - --modifiers show modifiers like public,protected,private,static + --modifiers show modifiers like public,protected,private,static [boolean] [default: true] - --typeLinks add links for classes, interface, enums that point to the - source files [boolean] [default: true] - --nomnoml nomnoml layouting and styling options (an array of strings, - each representing a nomnoml line), i.e.: --nomnoml - "#arrowSize: 1" "#.interface: fill=#8f8 dashed" [array] - --config path to a json config file (command line options can be - provided as keys in it) [string] + --typeLinks add links for classes, interface, enums that point to the + source files [boolean] [default: true] + --nomnoml nomnoml layouting and styling options (an array of + strings, each representing a nomnoml line), i.e.: + --nomnoml "#arrowSize: 1" "#.interface: fill=#8f8 + dashed" [array] + --outDsl the path to the output DSL file (nomnoml) [string] + --config path to a json config file (command line options can be + provided as keys in it) [string] ``` an example config.json could look like: @@ -65,17 +68,61 @@ an example config.json could look like: ## Examples +### Demo Diagram + +```sh +tsuml2 --glob "./src/demo/**/*.ts" -o "./assets/uml_diagram.svg" +``` + The diagram generated for the code under the [demo folder](https://github.com/demike/TsUML2/tree/master/src/demo) looks as follows: ![](/assets/uml_diagram.svg?sanitize=true) -A complex command line parameter example: +### A complex command line parameter example: ``` ./tsuml2 --glob=./src/demo/**/*.ts --nomnoml "#arrowSize: 1.5" "#.interface: fill=#8f8 dashed" --modifiers false --propertyTypes false ``` ![](/assets/alt_uml_diagram.svg?sanitize=true) -With type links enabled: [live example](https://raw.githubusercontent.com/demike/TsUML2/master/assets/uml_diagram.svg) +### With type links enabled: [live example](https://raw.githubusercontent.com/demike/TsUML2/master/assets/uml_diagram.svg) ![](/assets/type_links.gif) + +### Generate nomnoml DSL output + +```sh +tsuml2 --glob "./src/demo/**/*.ts" --outDsl "./assets/uml_diagram.dsl" +``` +results in the following nomnoml code stored in `uml_diagram.dsl`: +```nomnoml +#.interface: fill=lightblue +#.enumeration: fill=lightgreen +[Weapon||+tryHit(): boolean] +[Named|+name: string|] +[Magic|+kind: string|] +[BlackMagic||+paintItBlack(): boolean] +[MagicWeapon|+magic: MT|+tryMagicHit(): boolean] +[BlackMagicWeapon||] +[Gender|Male;Female;Else] +[Magic]<:--[BlackMagic] +[Weapon]<:--[MagicWeapon] +[MagicWeapon]<:--[BlackMagicWeapon] +[BaseWeapon|+damage: number;#durability: number;+attributes: string\[\]|] +[Katana|+name: string|+tryHit(): boolean] +[MagicKatana|+magic: MT|+tryMagicHit(): boolean] +[BlackMagicKatana||+tryBlackMagicHit(): boolean] +[BaseWeapon]<:-[Katana] +[Weapon]<:--[Katana] +[Named]<:--[Katana] +[Katana]<:-[MagicKatana] +[MagicWeapon]<:--[MagicKatana] +[MagicKatana]<:-[BlackMagicKatana] +[MagicWeapon]<:--[BlackMagicKatana] +[Ninja|+gender: Gender;+static IdCnt: number;-_weapon: Weapon;+id: number|+fight(): boolean] +[Viking|+gender: Gender;+weapon: WT|+fight(): boolean] +[UberViking||] +[VikingWithKatana||] +[Viking]<:-[UberViking] +[Viking]<:-[VikingWithKatana] +``` \ No newline at end of file diff --git a/assets/uml_diagram.dsl b/assets/uml_diagram.dsl new file mode 100644 index 0000000..6d46ee5 --- /dev/null +++ b/assets/uml_diagram.dsl @@ -0,0 +1,29 @@ +#.interface: fill=lightblue +#.enumeration: fill=lightgreen +[Weapon||+tryHit(): boolean] +[Named|+name: string|] +[Magic|+kind: string|] +[BlackMagic||+paintItBlack(): boolean] +[MagicWeapon|+magic: MT|+tryMagicHit(): boolean] +[BlackMagicWeapon||] +[Gender|Male;Female;Else] +[Magic]<:--[BlackMagic] +[Weapon]<:--[MagicWeapon] +[MagicWeapon]<:--[BlackMagicWeapon] +[BaseWeapon|+damage: number;#durability: number;+attributes: string\[\]|] +[Katana|+name: string|+tryHit(): boolean] +[MagicKatana|+magic: MT|+tryMagicHit(): boolean] +[BlackMagicKatana||+tryBlackMagicHit(): boolean] +[BaseWeapon]<:-[Katana] +[Weapon]<:--[Katana] +[Named]<:--[Katana] +[Katana]<:-[MagicKatana] +[MagicWeapon]<:--[MagicKatana] +[MagicKatana]<:-[BlackMagicKatana] +[MagicWeapon]<:--[BlackMagicKatana] +[Ninja|+gender: Gender;+static IdCnt: number;-_weapon: Weapon;+id: number|+fight(): boolean] +[Viking|+gender: Gender;+weapon: WT|+fight(): boolean] +[UberViking||] +[VikingWithKatana||] +[Viking]<:-[UberViking] +[Viking]<:-[VikingWithKatana] \ No newline at end of file diff --git a/assets/uml_diagram.svg b/assets/uml_diagram.svg index f9e1459..91a91ef 100644 --- a/assets/uml_diagram.svg +++ b/assets/uml_diagram.svg @@ -1,5 +1,4 @@ - -nomnoml + #.interface: fill=lightblue #.enumeration: fill=lightgreen [<interface>Weapon||+tryHit(): boolean] @@ -9,132 +8,437 @@ [<interface>MagicWeapon<MT>|+magic: MT|+tryMagicHit(): boolean] [<interface>BlackMagicWeapon||] [<enumeration>Gender|Male;Female;Else] -[Weapon]<:--[Katana] -[Named]<:--[Katana] [Magic]<:--[BlackMagic] [Weapon]<:--[MagicWeapon<MT>] -[MagicWeapon<MT>]<:--[MagicKatana<MT>] -[MagicWeapon<MT>]<:--[BlackMagicKatana] [MagicWeapon<MT>]<:--[BlackMagicWeapon] [BaseWeapon|+damage: number;#durability: number;+attributes: string\[\]|] [Katana|+name: string|+tryHit(): boolean] [MagicKatana<MT>|+magic: MT|+tryMagicHit(): boolean] [BlackMagicKatana||+tryBlackMagicHit(): boolean] [BaseWeapon]<:-[Katana] +[Weapon]<:--[Katana] +[Named]<:--[Katana] [Katana]<:-[MagicKatana<MT>] +[MagicWeapon<MT>]<:--[MagicKatana<MT>] [MagicKatana<MT>]<:-[BlackMagicKatana] +[MagicWeapon<MT>]<:--[BlackMagicKatana] [Ninja|+gender: Gender;+static IdCnt: number;-_weapon: Weapon;+id: number|+fight(): boolean] [Viking<WT>|+gender: Gender;+weapon: WT|+fight(): boolean] [UberViking<WT>||] [VikingWithKatana||] [Viking<WT>]<:-[UberViking<WT>] [Viking<WT>]<:-[VikingWithKatana] - - - - - - - - - - - - - - - - - - - - - - - - - -Weapon - - -+tryHit(): boolean - -Named - -+name: string - - -Magic - -+kind: string - - -BlackMagic - - -+paintItBlack(): boolean - -MagicWeapon<MT> - -+magic: MT - -+tryMagicHit(): boolean - -BlackMagicWeapon - - - -BaseWeapon - -+damage: number -#durability: number -+attributes: string[] - - -Katana - -+name: string - -+tryHit(): boolean - -MagicKatana<MT> - -+magic: MT - -+tryMagicHit(): boolean - -BlackMagicKatana - - -+tryBlackMagicHit(): boolean - -Ninja - -+gender: Gender -+static IdCnt: number --_weapon: Weapon -+id: number - -+fight(): boolean - -Viking<WT> - -+gender: Gender -+weapon: WT - -+fight(): boolean - -UberViking<WT> - - - -VikingWithKatana - - - -Gender - -Male -Female -Else + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Weapon + + + + + + + + + + ++tryHit(): boolean + + + + + + + + + + + + +Named + + + + + ++name: string + + + + + + + + + + + + + + + + + +Magic + + + + + ++kind: string + + + + + + + + + + + + + + + + + +BlackMagic + + + + + + + + + + ++paintItBlack(): boolean + + + + + + + + + + + + +MagicWeapon<MT> + + + + + ++magic: MT + + + + + ++tryMagicHit(): boolean + + + + + + + + + + + + +BlackMagicWeapon + + + + + + + + + + + + + + + + + + + + + + +BaseWeapon + + + + + ++damage: number +#durability: number ++attributes: string[] + + + + + + + + + + + + + + + + + +Katana + + + + + ++name: string + + + + + ++tryHit(): boolean + + + + + + + + + + + + +MagicKatana<MT> + + + + + ++magic: MT + + + + + ++tryMagicHit(): boolean + + + + + + + + + + + + +BlackMagicKatana + + + + + + + + + + ++tryBlackMagicHit(): boolean + + + + + + + + + + + + +Ninja + + + + + ++gender: Gender ++static IdCnt: number +-_weapon: Weapon ++id: number + + + + + ++fight(): boolean + + + + + + + + + + + + +Viking<WT> + + + + + ++gender: Gender ++weapon: WT + + + + + ++fight(): boolean + + + + + + + + + + + + +UberViking<WT> + + + + + + + + + + + + + + + + + + + + + + +VikingWithKatana + + + + + + + + + + + + + + + + + + + + + +Gender + + + + + +Male +Female +Else + + + + + + + + diff --git a/src/core/index.ts b/src/core/index.ts index 777b011..f82ed0f 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -60,13 +60,24 @@ export function createNomnomlSVG(settings: TsUML2Settings) { console.log(chalk.yellow("\nemitting declarations:")); const dsl = emit(declarations); + if(SETTINGS.outDsl !== "") { + console.log(chalk.green("\nwriting DSL")); + fs.writeFile(SETTINGS.outDsl,dsl,(err) => { + if(err) { + console.log(chalk.redBright("Error writing DSL file: " + err)); + } + }); + } + //render console.log(chalk.yellow("\nrender to svg")); let svg = renderNomnomlSVG(dsl); if(settings.typeLinks) { + console.log(chalk.yellow("\nadding type links to svg")); svg = postProcessSvg(svg,settings.outFile, declarations); } + console.log(chalk.green("\nwriting SVG")); fs.writeFile(SETTINGS.outFile,svg,(err) => { if(err) { console.log(chalk.redBright("Error writing file: " + err)); diff --git a/src/core/tsuml2-settings.ts b/src/core/tsuml2-settings.ts index 5dbbb21..f6e3c6f 100644 --- a/src/core/tsuml2-settings.ts +++ b/src/core/tsuml2-settings.ts @@ -39,6 +39,12 @@ export class TsUML2Settings { nomnoml: string[] = []; + /** + * output file contain the DSL (nomnoml) + */ + outDsl: string = "" + + /** * parses a json file and merges in the provided options * @param json @@ -77,6 +83,10 @@ export class TsUML2Settings { describe: "nomnoml layouting and styling options (an array of strings, each representing a nomnoml line), i.e.: --nomnoml \"#arrowSize: 1\" \"#.interface: fill=#8f8 dashed\" ", array: true, string: true + }).option('outDsl', { + describe: "the path to the output DSL file (nomnoml)", + string: true, + required: false, }).option('config', { describe: "path to a json config file (command line options can be provided as keys in it)", string: true @@ -115,6 +125,10 @@ export class TsUML2Settings { if(argv.typeLinks != null && !(yargs.parsed as any).defaulted.typeLinks) { this.typeLinks = argv.typeLinks; } + + if(argv.outDsl != null && !(yargs.parsed as any).defaulted.outDsl) { + this.outDsl = argv.outDsl; + } } }