bafolts / tplant

Typescript to plantuml
https://segmentationfaults.com/tplant/default.html
GNU General Public License v3.0
267 stars 34 forks source link

Namespaces not supported? #13

Closed JirkaDellOro closed 5 years ago

JirkaDellOro commented 5 years ago

Great work, I'm eager to use it!

I'm working on a large browser based project with namespaces. It looks like tplant outputs only the startuml and enduml tags when hitting the keyword namespace or module,..

JirkaDellOro commented 5 years ago

When using namespaces in a browserbased project, there is no need to use import. How could tplant support this?

bafolts commented 5 years ago

Can you give an example of what you sample input and what you would expect the output to be? That will be a good starting point.

JirkaDellOro commented 5 years ago

I created a sample with a description and a desireable diagram for you. Maybe you want to integrate it, comes as pull request

bafolts commented 5 years ago

The script will need to be updated to detect namespace usage within a typescript file and to include it in the generated plantuml output. Once that is done what is in the PR looks great to go with it. This will be a new feature as the current version isn't looking for namespaces.

https://github.com/bafolts/tplant/blob/master/src/generateDocumentation.ts#L48

Somewhere in here we will need to look for namespaces to keep track of them to then output them with the plantuml later.

JirkaDellOro commented 5 years ago

Great, I'm looking forward to it

Plagiatus commented 5 years ago

In addition I'd like to point out that references should work cross-namespace as well and differentiate between Namespace internal and external classes.

Also that Namespaces can be nested.

Example ts

namespace NameOne {
    export namespace SubNameOne{
        export class A {
            doSomething() { console.log("this is NameOne A"); }
        }
    }
}

namespace NameTwo {
    class A {
        thisIsSomething() { console.log("this is NameTwo A"); }
    }
    class B extends NameOne.SubNameOne.A {
        doSomethingElse() { console.log("this is NameTwo B"); }
    }
    class C extends A {
        somethingSomething() {console.log("this is NameTwo C");}
    }
}

Expected puml:

@startuml
namespace NameOne {
    namespace SubNameOne{
        class A {
            doSomething()
        }
    }
}

namespace NameTwo {
    class A {
        thisIsSomething()
    }
    class B extends NameOne.SubNameOne.A {
        doSomethingElse()
    }
    class C extends A {
        somethingSomething()
    }
}
@enduml
bafolts commented 5 years ago

These examples are helpful. I will include them with the tests when implemented.

bafolts commented 5 years ago

@marcosvrs do you have a strategy in mind for this? With how the parser currently works it may be difficult to implement namespaces since they can be nested.

marcosvrs commented 5 years ago

With the current implementation, this is really hard to achive without creating spaghetti code (Which we're almost there). I have an idea, but we would need a complete refactoring. Something like: https://refactoring.guru/design-patterns/iterator

JirkaDellOro commented 5 years ago

Four weeks from now we could probably help a little, if possible and desired

bafolts commented 5 years ago

I agree a refactor would be best. Right now one of the problems is the intermediate objects that are created know a lot about the plantuml they are going to generate and it is difficult to fit namespace into that paradigm as unlike what we have now a namespace could contain a namespace. It would be nice if we created an intermediate class and then had a template language used to turn those intermediate classes into the final output. Then we would have a namespace template file somewhere that would generate the namespace in plantuml using instances of the intermediate class. It looks like the iterator pattern could be used for this intermediate class. Once we have the iterators it would nice to then have a templates directory where we could have a namespace, interface, class, and enum template. The templates would use the iterator classes for their dictionaries. I am not sure how popular handlebars still is but using that or some other template system along with the iterator pattern for the intermediate classes should work well.

marcosvrs commented 5 years ago

Nice description. This was my idea too. Just that I didn't though all the way through like you :) About handlebars, our templates would not be that complex. I think a simple Transfer object would be enough. Since we're talking about patterns, a Transfer object with hierarchical structure should be a Composite.

bafolts commented 5 years ago

I should have time to start on this next week. I don't think there is any other large features planned that would have to be merged with it once started. The tests in place should help with ensuring old functionality doesn't break.

JirkaDellOro commented 5 years ago

Assuming you write your code in JS/TS, I'd just like to hint that this provides iterators out of the box. Just recently I created an iterator for a tree structure consisting of nodes with child nodes attached to them. The relevant methods for iterations are these:

    export class Node {
        private name: string;
        private children: Node[] = []; // Associative array nodes appended to this node.

        /**
         * Generator yielding the node and all successors in the branch below for iteration
         */
        public get branch(): IterableIterator<Node> {
            return this.getBranchGenerator();
        }

        private *getBranchGenerator(): IterableIterator<Node> {
            yield this;
            for (let child of this.children)
            yield* child.branch;
        }    

This can then easily be used like this:

        for (let iter of node.branch)
            console.log(iter.name);

Which prints the names of all the nodes in the branch. According to the article @marcosvrs linked to, this is a depth-first-iterator, but you could easily create a breadth-first one.