custom-elements-manifest icon indicating copy to clipboard operation
custom-elements-manifest copied to clipboard

missing attributes extending mixins

Open reno1979 opened this issue 3 years ago • 12 comments

Checklist

  • [x] Did you run the analyzer with the --dev flag to get more information?
  • [x] Did you create a minimal reproduction in the playground?
  • used version 0.5.7 of the analyzer

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

Expected behavior I would expect attributes to be inherited.

Example code:

  • class A extends HTMLElement
  • class B extends A
  • class C extends B

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"
          }
        }
      ]
    }
  ]
}

reno1979 avatar Nov 04 '21 10:11 reno1979

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?

thepassle avatar Nov 04 '21 11:11 thepassle

@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?

reno1979 avatar Nov 04 '21 11:11 reno1979

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.

thepassle avatar Nov 04 '21 12:11 thepassle

Ok no problem, does this help?

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

reno1979 avatar Nov 04 '21 13:11 reno1979

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

thepassle avatar Nov 04 '21 14:11 thepassle

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

reno1979 avatar Nov 04 '21 14:11 reno1979

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

thepassle avatar Nov 04 '21 14:11 thepassle

@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.

reno1979 avatar Nov 04 '21 14:11 reno1979

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.

thepassle avatar Nov 04 '21 14:11 thepassle

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 avatar Nov 04 '21 14:11 reno1979

@thepassle

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

reno1979 avatar Dec 15 '21 10:12 reno1979

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

michaelwarren1106 avatar Apr 14 '22 18:04 michaelwarren1106