open-wc / custom-elements-manifest

Custom Elements Manifest is a file format that describes custom elements in your project.
https://custom-elements-manifest.open-wc.org/
227 stars 37 forks source link

missing attributes extending mixins #147

Open reno1979 opened 2 years ago

reno1979 commented 2 years ago

Checklist

Completing the items above will greatly improve triaging time of your issue.

Expected behavior I would expect attributes to be inherited.

Example code:

Only the attribute from class A is available.

I have created 3 files:

// a.js
/**
 * @tagname my-mixin-a
 */
export class MyElementA extends getClass(HTMLElement) { }

export function getClass(superClass = HTMLElement) {

    return class extends superClass {
        static get observedAttributes() {
            return ['a'];
        }

        set a(val) {
            this._a = val;
        }

        get a() {
            return this._a;
        }
    }
}
// b.js
import { MyElementA } from './a.js';

/**
 * @tag my-mixin-ab
 */
export class MyElementAB extends getClass(MyElementA) { };

export function getClass(superClass = MyElementA) {

    return class extends superClass {
        static get observedAttributes() {
            return ['b'];
        }

        set b(val) {
            this._b = val;
        }

        get b() {
            return this._b;
        }
    }
}
// c.js
import { MyElementAB } from './b.js';

/**
 * @tagname my-mixin-abc
 */
export class MyElementABC extends getClass(MyElementAB) { };

export function getClass(superClass = MyElementAB) {

    return class extends superClass {
        static get observedAttributes() {
            return ['c'];
        }

        set c(val) {
            this._c = val;
        }

        get c() {
            return this._c;
        }
    }
}

The result:

// custom-elements.json
{
  "schemaVersion": "1.0.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "src/a.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
          "name": "MyElementA",
          "mixins": [
            {
              "name": "getClass",
              "module": "src/a.js"
            }
          ],
          "superclass": {
            "name": "HTMLElement"
          },
          "tagName": "my-mixin-a",
          "customElement": true,
          "attributes": [
            {
              "name": "a",
              "inheritedFrom": {
                "name": "getClass",
                "module": "src/c.js"
              }
            }
          ],
          "members": [
            {
              "kind": "field",
              "name": "a",
              "inheritedFrom": {
                "name": "getClass",
                "module": "src/c.js"
              }
            }
          ]
        },
        {
          "kind": "mixin",
          "description": "",
          "name": "getClass",
          "members": [
            {
              "kind": "field",
              "name": "a"
            }
          ],
          "attributes": [
            {
              "name": "a"
            }
          ],
          "parameters": [
            {
              "name": "superClass",
              "default": "HTMLElement"
            }
          ]
        }
      ],
      "exports": [
        {
          "kind": "js",
          "name": "MyElementA",
          "declaration": {
            "name": "MyElementA",
            "module": "src/a.js"
          }
        },
        {
          "kind": "js",
          "name": "getClass",
          "declaration": {
            "name": "getClass",
            "module": "src/a.js"
          }
        }
      ]
    },
    {
      "kind": "javascript-module",
      "path": "src/b.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
          "name": "MyElementAB",
          "mixins": [
            {
              "name": "getClass",
              "module": "src/b.js"
            }
          ],
          "superclass": {
            "name": "MyElementA",
            "module": "/src/a.js"
          },
          "tagName": "my-mixin-ab",
          "customElement": true,
          "attributes": [
            {
              "name": "a",
              "inheritedFrom": {
                "name": "MyElementA",
                "module": "src/a.js"
              }
            }
          ],
          "members": [
            {
              "kind": "field",
              "name": "a",
              "inheritedFrom": {
                "name": "MyElementA",
                "module": "src/a.js"
              }
            }
          ]
        },
        {
          "kind": "mixin",
          "description": "",
          "name": "getClass",
          "members": [
            {
              "kind": "field",
              "name": "b"
            }
          ],
          "attributes": [
            {
              "name": "b"
            }
          ],
          "parameters": [
            {
              "name": "superClass",
              "default": "MyElementA"
            }
          ]
        }
      ],
      "exports": [
        {
          "kind": "js",
          "name": "MyElementAB",
          "declaration": {
            "name": "MyElementAB",
            "module": "src/b.js"
          }
        },
        {
          "kind": "js",
          "name": "getClass",
          "declaration": {
            "name": "getClass",
            "module": "src/b.js"
          }
        }
      ]
    },
    {
      "kind": "javascript-module",
      "path": "src/c.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
          "name": "MyElementABC",
          "mixins": [
            {
              "name": "getClass",
              "module": "src/c.js"
            }
          ],
          "superclass": {
            "name": "MyElementAB",
            "module": "/src/b.js"
          },
          "tagName": "my-mixin-abc",
          "customElement": true,
          "attributes": [
            {
              "name": "a",
              "inheritedFrom": {
                "name": "MyElementA",
                "module": "src/a.js"
              }
            }
          ],
          "members": [
            {
              "kind": "field",
              "name": "a",
              "inheritedFrom": {
                "name": "MyElementA",
                "module": "src/a.js"
              }
            }
          ]
        },
        {
          "kind": "mixin",
          "description": "",
          "name": "getClass",
          "members": [
            {
              "kind": "field",
              "name": "c"
            }
          ],
          "attributes": [
            {
              "name": "c"
            }
          ],
          "parameters": [
            {
              "name": "superClass",
              "default": "MyElementAB"
            }
          ]
        }
      ],
      "exports": [
        {
          "kind": "js",
          "name": "MyElementABC",
          "declaration": {
            "name": "MyElementABC",
            "module": "src/c.js"
          }
        },
        {
          "kind": "js",
          "name": "getClass",
          "declaration": {
            "name": "getClass",
            "module": "src/c.js"
          }
        }
      ]
    }
  ]
}
thepassle commented 2 years ago

Thanks for reporting this issue, I appreciate you taking the time to make a reproduction, could I ask you to try and make the reproduction even smaller?

reno1979 commented 2 years ago

@thepassle No problem, I appreciate the module.

I tried to make it as small as possible, the issue only happened using multiple files. I was not able to reproduce it in the playground.

Would a small repo help?

thepassle commented 2 years ago

Would a small repo help?

Yes that would help, I know it's already pretty minimal, but please try to make the repro as minimal as you possibly can (the least amount of classes/mixins required, maybe only a property instead of properties/and attributes, etc). Sorry to be nitpicky, but bugs in the inheritance stuff get very confusing very quickly.

reno1979 commented 2 years ago

Ok no problem, does this help?

https://github.com/reno1979/custom-element-manifest-test

thepassle commented 2 years ago

Yes that helps, thanks a lot.

Do you use multiple mixins that have the same name in a project? E.g.: in your repo, you declare getClass every time in each file

reno1979 commented 2 years ago

@thepassle correct, a code template is user. So all files are the same.

thepassle commented 2 years ago

Is this code open source by any chance? I'd be interested to see what it looks like

reno1979 commented 2 years ago

@thepassle no sorry I can not publish the code. I wish I could. It is just a vanilla web component library. I'm working on something very basic that works almost the same, it is a way for me to experiment. And that is a personal project, so when published I will let you know.

thepassle commented 2 years ago

I understand. To clarify, I'm interested in seeing why you'd re-declare a mixin with the same name for every file, as opposed to giving the mixin its own module/making it shared somehow.

reno1979 commented 2 years ago

ahh that is because it is a work in progress. The mixin is only a wrapper, the actual class that is wrapped should be able to extend on a different given class.

For example:


// baseA.js
export class BaseClassA extends HTMLElement   {..}

// baseB.js
export class BaseClassB extends HTMLElement   {..}

// element.js
import { BaseClassA } from './baseA.js';
import { BaseClassB } from './baseB.js';

export function getClass(SuperClass){
   return new class extends SuperClass { 
      static get observedAttributes() {
         return [].concat(SuperClass.observedAttributes);
     }
     ... etc
  }
}

class ElementA extends getClass(BaseClassA){};
class ElementB extends getClass(BaseClassB){};

customElements.define('wc-a', ElementA);
customElements.define('wc-b', ElementB);
reno1979 commented 2 years ago

@thepassle

Is there a possible workaround I could do in our code base to make it work?

michaelwarren1106 commented 2 years ago

Bump.

I'm still having this issue today in recent versions/usage of CEM analyzer. Is there a plan of attack for adding properties that come from the inheritance chain added to the class declaration in the manifest?

I'm attempting to use the manifest.json file for automated documentation, and not having mixin properties be available makes my documentation inacccurate