open-wc / custom-elements-manifest-deprecated

Custom Elements Manifest is a file format that describes custom elements in your project.
8 stars 4 forks source link

Possible to extract custom typedocs #52

Closed daKmoR closed 3 years ago

daKmoR commented 3 years ago

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

https://github.com/webcomponents/custom-elements-manifest/issues/42#issuecomment-778629278

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?

thepassle commented 3 years ago

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?

daKmoR commented 3 years ago

yes exactly 👍

I'm wondering if we should distinguish in the config between extra jsdocs for properties, classes, ... 🤔

thepassle commented 3 years ago

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.

thepassle commented 3 years ago

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"
      }
    }
  ]
},
thepassle commented 3 years ago

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"
          }
        }
      ]
    }
  ]
}
daKmoR commented 3 years ago

that looks sweeeet 👍

so I can proceed with my plans to take over the wooooorrllddd.... muhahhaha 🤣

thepassle commented 3 years ago

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