OpenHausIO / documentation

Documentation about Frontend, Backend, Installation & Administration
https://docs.open-haus.io
MIT License
4 stars 1 forks source link

Create custom jsdoc/markdown template #2

Closed mStirner closed 2 years ago

mStirner commented 2 years ago

To automate the building process of the documentation/markdown output, we need a custom template. The jsdoc template is then converted to a markdown output which can be included in docsify.

For creating the markdown this grunt snippet can be used:

   const jsdoc2md = require("jsdoc-to-markdown");
    grunt.registerTask("docs:helper", () => {

        let files = fs.readdirSync(path.resolve(process.cwd(), "helper"));

        files.forEach((file) => {

            let filename = path.resolve("helper", file);

            //var output = fs.readFileSync('doc/README.header.md');
            let output = jsdoc2md.renderSync({
                files: [filename]
            });

            //output += fs.readFileSync('doc/README.footer.md');
            fs.writeFileSync(path.resolve(process.cwd(), "docs-test/helper", `${file}.md`), output);

        });

    });

See related backend issue: https://github.com/OpenHausIO/backend/issues/109

The dmd is the package/template that produce the markdown output. Not sure if we need to modifie/fork the dmd template or build a custom jsdoc template. (Or even jsdoc here is needed and not just the source code comments are renderd).

Perhaps add some more/custom documentation tags: https://github.com/jsdoc2md/jsdoc-parse

mStirner commented 2 years ago

Alternative, generate from source code json file and use that for custom teplates/outputs?

jsdoc -X ./mylib/ > jsdoc-ast.json

https://stackoverflow.com/a/26528555/5781499

mStirner commented 2 years ago

Last link is currently in development. Anything else makes little sense.

mStirner commented 2 years ago

playground.js:

const fs = require("fs");
const { parse } = require('comment-parser');
const Mustache = require("mustache");
const util = require("util");

//Mustache.escape = function (text) { return text; };

console.clear();

const source = fs.readFileSync("class.js", "utf8");
const template = fs.readFileSync("../templates/class.mst", "utf8");
const parsed = parse(source, {
    spacing: "preserve"
});

const fillings = {
    filename: "class.base.js",
    className: "",
    description: "",
    properties: [],
    examples: [],
    events: [],
    methods: [],
    args: function () {

        let output = "";

        // TODO handle optional parameter
        // TODO handle default values
        this.params.forEach(({ name, optional }, i, arr) => {

            output += `${name}`;

            if (i + 1 < arr.length) {
                output += ", ";
            }

        });

        return output;

    }
};

parsed.forEach((block, i) => {

    // each block = /** <what ever here> */

    // delete unecassary stuff
    delete block.source;

    // check if block of comment is class declaration
    // check if block is a function documentation    

    if (block.tags.some(({ tag }) => { return tag === "class" })) {

        block.tags.forEach(({ tag, name, type, optional, description }) => {
            if (tag === "description") {

                fillings.description = description;

            } else if (tag === "property") {

                fillings.properties.push({
                    name,
                    type,
                    optional,
                    description
                });

            } else if (tag === "emits") {

                fillings.events.push({
                    name,
                    description
                });

            } else if (tag === "class") {

                fillings.className = name;

            } else if (tag === "exmaple") {

                fillings.examples.push(description)

            } else {

                console.error("> Unsporrted tag found in class declaration:", tag);

            }
        });

    } else if (block.tags.some(({ tag }) => { return tag === "function" })) {

        // method local variables
        let obj = {
            static: false,
            params: [],
            examples: [],
            returns: []
        }

        block.tags.forEach(({ tag, name, type, optional, description }) => {
            if (tag === "function") {

                Object.assign(obj, {
                    name,
                    description,
                });

            } else if (tag === "param") {

                obj.params.push({
                    name,
                    type,
                    optional,
                    description
                });

            } else if (tag === "returns") {

                obj.returns.type = type;
                obj.returns.description = name + " " + description;

            } else if (tag === "example") {

                obj.examples.push(description);

            } else if (tag === "static") {

                obj.static = true;

            } else {

                console.error("> Unsporrted tag found in function declartion:", tag);

            }
        });

        // add method to template filling
        fillings.methods.push(obj);

    } else {

        console.error("> Code block is neither a class nor a function declration");

    }

});

//console.log(fillings.methods[0])

let rendered = Mustache.render(template, fillings);
//console.log(rendered)
fs.writeFileSync("./class.js.md", rendered);

class.mst:

<div class="mb-0">
    🔗 <a class="source-code" target="_blank"
        href="https://github.com/OpenHausIO/backend/blob/dev/components/devices/class.device.js">{{filename}}</a>
</div>
<hr style="margin: 0 !important" />

## `class` {{className}}
### Description:
{{{description}}}

### Properties:
| Name | Type | Description |
| :---- | :-------- | :----------- |
{{#properties}}
| {{name}} | `{{type}}` | {{description}} |
{{/properties}}

### Events:
| Name | Description |
| :---- | :----------- |
{{#events}}
| {{name}} | {{description}} |
{{/events}}

### Examples:
{{#examples}}
    {{{.}}}
{{/examples}}

### Methods:
{{#methods}}
#### .{{name}}({{args}}); {{#static}} `STATIC` {{/static}}
| Parameter | Type       | Description    |
| :-------- | :--------- |:------------- |
{{#params}}
| {{name}} | `{{type}}` |  {{{description}}} |
{{/params}}

{{description}}

*Returns* `{{returns.type}}` {{{returns.description}}}

##### Examples
{{#examples}}
    {{{.}}}
{{/examples}}

{{/methods}}
{{^methods}}
*none*
{{/methods}}

Generated markdown/output can be directly used in docsify