Skip to content

Commit

Permalink
Fuhrmanator/issue109 (#121)
Browse files Browse the repository at this point in the history
* CRLF setting for windows (tests pass), latest npm pkgs

* Since join(os.EOL) is used, we have to fix it for Windows
Repo is set to not use CRLF on clone

* Latest commander and glob usage in TypeScript

* Support customization file

* Fix bug with Functions being presented as methods in UML
Unfortunately, functions are not representable in PlantUML.
It's misleading to show them as methods, and there's a bug when a function is exported in a module that has no classes -- it shows in the PlantUML as a method without being inside a class (PlantUML crashes).

* fix tests, functions are not methods

* Update .editorconfig

Co-authored-by: Brian Folts <[email protected]>

---------

Co-authored-by: Brian Folts <[email protected]>
  • Loading branch information
fuhrmanator and bafolts authored Mar 6, 2024
1 parent bfc2aed commit 67ceef6
Show file tree
Hide file tree
Showing 13 changed files with 1,835 additions and 1,825 deletions.
3 changes: 1 addition & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
trim_trailing_whitespace = false
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto
3,510 changes: 1,749 additions & 1,761 deletions package-lock.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,21 @@
"test": "jest"
},
"dependencies": {
"commander": "^6.1.0",
"glob": "^7.1.6",
"commander": "^12.0.0",
"glob": "^10.3.10",
"node-plantuml": "0.9.0",
"plantuml-encoder": "^1.4.0",
"typescript": "5.2.2"
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/glob": "^7.1.1",
"@types/jest": "^26.0.24",
"@types/node": "^12.0.0",
"@typescript-eslint/eslint-plugin": "^6.5.0",
"@typescript-eslint/parser": "^6.5.0",
"eslint": "^8.48.0",
"jest": "^29.1.1",
"ts-jest": "^29.1.1"
"@types/glob": "^8.1.0",
"@types/jest": "^29.5.12",
"@types/node": "^20.11.19",
"@typescript-eslint/eslint-plugin": "^7.0.1",
"@typescript-eslint/parser": "^7.0.1",
"eslint": "^8.56.0",
"jest": "^29.7.0",
"ts-jest": "^29.1.2"
},
"jest": {
"preset": "ts-jest",
Expand Down
22 changes: 12 additions & 10 deletions src/Factories/ComponentFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,19 @@ export function create(fileName: string, node: ts.Node, checker: ts.TypeChecker)
componentComposites.push(EnumFactory.create(enumSymbol));

return;
} else if (childNode.kind === ts.SyntaxKind.FunctionDeclaration) {
const currentNode: ts.FunctionDeclaration = <ts.FunctionDeclaration>childNode;
if (currentNode.name === undefined) {
return;
}
const functionSymbol: ts.Symbol | undefined = checker.getSymbolAtLocation(currentNode.name);
if (functionSymbol === undefined) {
return;
}
componentComposites.push(MethodFactory.create(functionSymbol, currentNode, checker));
}
// Functions are not methods in PlantUML! The (fake) methods (that are functions) get placed in the PlantUML outside of a class, and it won't render.
// else if (childNode.kind === ts.SyntaxKind.FunctionDeclaration) {
// const currentNode: ts.FunctionDeclaration = <ts.FunctionDeclaration>childNode;
// if (currentNode.name === undefined) {
// return;
// }
// const functionSymbol: ts.Symbol | undefined = checker.getSymbolAtLocation(currentNode.name);
// if (functionSymbol === undefined) {
// return;
// }
// componentComposites.push(MethodFactory.create(functionSymbol, currentNode, checker));
// }
});

return componentComposites;
Expand Down
8 changes: 7 additions & 1 deletion src/Formatter/PlantUMLFormat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import { IComponentComposite } from '../Models/IComponentComposite';
export class PlantUMLFormat extends Formatter {

public header(): string[] {
return ['@startuml'];
const result: string[] = ['@startuml'];

if (this.options.customization !== undefined) {
result.push(`!include ${this.options.customization}`);
}

return result;
}

public footer(): string[] {
Expand Down
1 change: 1 addition & 0 deletions src/Models/ICommandOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export interface ICommandOptions {
onlyInterfaces: boolean;
format?: string;
onlyClasses: boolean;
customization?: string; // optional customization file (plantuml include file)
}
79 changes: 47 additions & 32 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env node

import commander from 'commander';
import Commander from 'commander';
import fs from 'fs';
import G from 'glob';
import { GlobOptions, globSync } from 'glob'
import os from 'os';
import path from 'path';
import ts from 'typescript';
Expand All @@ -14,6 +14,8 @@ const AVAILABLE_PLANTUML_EXTENSIONS: string[] = ['svg', 'png', 'txt'];
/* eslint-disable @typescript-eslint/no-var-requires */
const plantuml = require("node-plantuml");

const commander = new Commander.Command();

commander
.version("3.1.2")
.option('-i, --input <path>', 'Define the path of the Typescript file')
Expand All @@ -30,54 +32,67 @@ commander
.option('-C, --only-classes', 'Only output classes')
.option('-f, --format <path>', 'Define the format of output')
.option('-T, --targetClass <className>', 'Display class hierarchy diagram')
.option('-c, --customize <path>', 'Customize the output diagram with an included plantuml file')
.parse(process.argv);

if (!commander.input) {
const options: Commander.OptionValues = commander.opts();

if (!options.input) {
console.error('Missing input file');
process.exit(1);
}

const globOptions: G.IOptions = {};
const globOptions: GlobOptions = {};

if (commander.exclude !== undefined) {
globOptions.ignore = <string>commander.exclude;
if (options.exclude !== undefined) {
globOptions.ignore = <string>options.exclude;
}

G(<string>commander.input, globOptions, (err: Error | null, matches: string[]): void => {
if (err !== null) {
throw err;
}
const matches = globSync(<string>options.input, globOptions) as string[];

const tsConfigFile: string | undefined = findTsConfigFile(<string>commander.input, <string | undefined>commander.tsconfig);
const tsConfigFile: string | undefined = findTsConfigFile(<string>options.input, <string | undefined>options.tsconfig);

const plantUMLDocument: string = tplant.convertToPlant(
tplant.generateDocumentation(matches, getCompilerOptions(tsConfigFile)),
{
associations: <boolean>commander.associations,
onlyInterfaces: <boolean>commander.onlyInterfaces,
format: <string> commander.format,
targetClass: <string> commander.targetClass,
onlyClasses: <boolean> commander.onlyClasses
}
);
if (matches.length === 0) {
console.error('No files found');
process.exit(1);
}

if (commander.output === undefined) {
console.log(plantUMLDocument);
// check to see if include file exists
if (options.customize !== undefined) {
if (!fs.existsSync(<string>options.customize)) {
console.error(`Warning: Customization file ${<string>options.customize} not found.`);
process.exit(1);
}
}
return;
const plantUMLDocument: string = tplant.convertToPlant(
tplant.generateDocumentation(matches, getCompilerOptions(tsConfigFile)),
{
associations: <boolean>options.associations,
onlyInterfaces: <boolean>options.onlyInterfaces,
format: <string> options.format,
targetClass: <string> options.targetClass,
onlyClasses: <boolean> options.onlyClasses,
customization: <string> options.customize
}
);
const extension: string = path.extname(<string>commander.output)
.replace(/^\./gm, '');
if (options.output === undefined) {
console.log(plantUMLDocument);
if (AVAILABLE_PLANTUML_EXTENSIONS.includes(extension)) {
requestImageFile(<string>commander.output, plantUMLDocument, extension);
process.exit(0);
}
return;
}
const extension: string = path.extname(<string>options.output)
.replace(/^\./gm, '');
if (AVAILABLE_PLANTUML_EXTENSIONS.includes(extension)) {
requestImageFile(<string>options.output, plantUMLDocument, extension);
process.exit(0);
}
fs.writeFileSync(<string>commander.output, plantUMLDocument, 'utf-8');
});
fs.writeFileSync(<string>options.output, plantUMLDocument, 'utf-8');
function findTsConfigFile(inputPath: string, tsConfigPath?: string): string | undefined {
if (tsConfigPath !== undefined) {
Expand Down
4 changes: 2 additions & 2 deletions test/arguments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ describe('Test commander options', () => {
' -getNaturalColor(thing: Thing, pos: Vector, norm: Vector, rd: Vector, scene: Scene): any',
' +render(scene: any, ctx: any, screenWidth: any, screenHeight: any): void',
'}',
'+defaultScene(): Scene',
'+exec(): void',
// '+defaultScene(): Scene',
// '+exec(): void',
'Ray --> "1" Vector',
'Intersection --> "1" Thing',
'Intersection --> "1" Ray',
Expand Down
2 changes: 1 addition & 1 deletion test/handbook.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ describe('Parse Handbook codes', () => {
});

it('generate PlantUML for Interfaces/ExtendingInterfaces.ts', () => {
expect(tplant.convertToPlant(tplant.generateDocumentation(['test/Handbook/Interfaces/ExtendingInterfaces.ts'])))
expect(tplant.convertToPlant(tplant.generateDocumentation(['test/Handbook/Interfaces/ExtendingInterfaces.ts'])).replace(/(\r)/gm,""))
.toEqual(`@startuml
interface Shape {
+color: string
Expand Down
2 changes: 0 additions & 2 deletions test/mermaid/results/playground_raytracer
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ class RayTracer {
-getNaturalColor(thing: Thing, pos: Vector, norm: Vector, rd: Vector, scene: Scene): any
+render(scene: any, ctx: any, screenWidth: any, screenHeight: any): void
}
+defaultScene(): Scene
+exec(): void
Ray ..> "1" Vector
Intersection ..> "1" Thing
Intersection ..> "1" Ray
Expand Down
4 changes: 2 additions & 2 deletions test/playground.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,8 @@ describe('Parse Playground codes', () => {
' -getNaturalColor(thing: Thing, pos: Vector, norm: Vector, rd: Vector, scene: Scene): any',
' +render(scene: any, ctx: any, screenWidth: any, screenHeight: any): void',
'}',
'+defaultScene(): Scene',
'+exec(): void',
// '+defaultScene(): Scene',
// '+exec(): void',
'@enduml'].join(os.EOL));
});
});
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"target": "ES2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
Expand Down

0 comments on commit 67ceef6

Please sign in to comment.