open-wc/custom-elements-manifest-deprecated

Possible to extract custom typedocs

daKmoR opened this issue ยท 7 comments

Hey, I saw this list of supported jsdoc "tags"...

webcomponents/custom-elements-manifest#42 (comment)

would it be possible to allow extraction of custom tags via a simple config or so ๐Ÿค”

i could imagine somthing like this.

class MyElement extend HTMLElement {

  /**
   * @type string
   * @editvia textarea[rows=2]
   */
  message = '';

  /**
   * @type string
   * @editvia select['variation-a', 'variation-b']
   */
  variation = '';

could be something like this

export default {
  extractAdditionalJsDocs: ['editvia']
}

so in the end for this class I would have this additional values available in the json and then tools could use it

what do you think?

Yep, I think that sounds reasonable

So the output here would be:

            {
              "kind": "field",
              "name": "message",
              "type": {
                "text": "string"
              },
              "default": "''",
              "editvia": "textarea[rows=2]"
            },

right?

yes exactly ๐Ÿ‘

I'm wondering if we should distinguish in the config between extra jsdocs for properties, classes, ... ๐Ÿค”

I'm wondering if we should distinguish in the config between extra jsdocs for properties, classes, ... ๐Ÿค”

One thing I was thinking of was having a plugin system for supporting differing libraries/syntax, e.g.:

export default {
  extractAdditionalJsDocs: 'editvia',
  plugins: [
    lit(), // handles litelement specifics (static get properties etc)
    stencil(), // handles stencil specific syntax/components
  ]
}

Plugins could have access to the AST during the analyze phase to handle library specific syntaxes and things like that.

So by default, the analyzer would then only handle vanilla stuff, but library specific syntax would be handled by plugins. As an example; LitElement really doesnt need that much extra handling, its pretty much only taking care of the static get properties thats Lit specific; everything else is just regular JS stuff (methods, fields, classes, etc)

That could also make it easy to add support for other libraries, like Stencil, Salesforce, whatever. It could also potentially make maintenance a bit easier, since people could just implement their own plugins for libraries that they would want to see support for.

I'm wondering if we should distinguish in the config between extra jsdocs for properties, classes, ... ๐Ÿค”

Oh lol, I think I misread this line and thought you meant something else ๐Ÿ˜… What do you mean with a distinction? It doesnt really matter what the jsdoc tag is on, right?

/**
 * @foo this would just output a `foo` field in the classDoc
 */
class Foo extends HTMLElement {
  /**
   * @bar this would just output a `bar` field on the memberDoc
   */
  someField = 1;
}

As long as it follows jsdoc:
/* @tag {type} name description */

In the earlier example you gave:

  /**
   * @type string
   * @editvia textarea[rows=2]
   */
  message = '';

it should probably output something like:

{
  "kind": "field",
  "name": "message",
  "type": {
    "text": "string"
  },
  "default": "''",
  "editvia": {
    "tag": "editvia",
    "description": "textarea[rows=2]"
  }
},

Or maybe something like:

  /**
   * @type string
   * @editvia textarea[rows=2]
   */
  message = '';

  /**
   * @someothertag {foo} name description
   */
  foo = '';
{
  "kind": "field",
  "name": "message",
  "type": {
    "text": "string"
  },
  "default": "''",
  "customJSDoc": [
    {
      "tag": "editvia",
      "description": "textarea[rows=2]"
    }, 
    {
      "tag": "someothertag",
      "name": "name",
      "description": "description",
      "type": {
        "text": "foo"
      }
    }
  ]
},

Actually, with plugins this is pretty straightforward to implement:

import ts from 'typescript';

export default {
  plugins: [
    function myPlugin() {
      return {
        // Runs for each module
        analyzePhase({node, moduleDoc}){
          switch (node.kind) {
            case ts.SyntaxKind.ClassDeclaration:
              const className = node.name.getText();

              node.members?.forEach(member => {
                const memberName = member.name.getText();

                member.jsDoc?.forEach(jsDoc => {
                  jsDoc.tags?.forEach(tag => {
                    if(tag.tagName.getText() === 'editvia') {
                      const description = tag.comment;

                      const classDeclaration = moduleDoc.declarations.find(declaration => declaration.name === className);
                      const messageField = classDeclaration.members.find(member => member.name === memberName);
                      
                      messageField.editvia = description
                    }
                  });
                });
              });
     
              break;
          }
        },
        // Runs for each module, after analyzing, all information about your module should now be available
        moduleLinkPhase({moduleDoc}){
          // console.log(moduleDoc);
        },
        // Runs after all modules have been parsed, and after post processing
        packageLinkPhase(customElementsManifest){
          // console.log(customElementsManifest);
        },
      }
    }
  ]
}

output:

{
  "schemaVersion": "0.1.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "./fixtures/-TEST/package/my-element.js",
      "declarations": [
        {
          "kind": "class",
          "name": "MyElement",
          "superclass": {
            "name": "HTMLElement"
          },
          "members": [
            {
              "kind": "field",
              "name": "message",
              "type": {
                "text": ""
              },
              "privacy": "public",
              "default": "''",
+             "editvia": "textarea[rows=2]"
            }
          ]
        }
      ],
      "exports": [
        {
          "kind": "js",
          "name": "MyElement",
          "declaration": {
            "name": "MyElement",
            "module": "./fixtures/-TEST/package/my-element.js"
          }
        }
      ]
    }
  ]
}

that looks sweeeet ๐Ÿ‘

so I can proceed with my plans to take over the wooooorrllddd.... muhahhaha ๐Ÿคฃ

Plugin system has been merged and can be used to achieve extracting custom JSDoc and other functionalities, will close this