Giter Club home page Giter Club logo

custom-elements-manifest's Introduction

Open-wc Logo

Open Web Component Recommendations

Open Web Components provides a set of defaults, recommendations and tools to help facilitate your web component project. Our recommendations include: developing, linting, testing, building, tooling, demoing, publishing and automating.

Usage

# in a new or existing folder:
npm init @open-wc
# requires node 10 & npm 6 or higher

This will kickstart a menu guiding you through all available actions.

$ npm init @open-wc
npx: installed 30 in 1.762s

        _.,,,,,,,,,._
     .d''           ``b.       Open Web Components Recommendations
   .p'      Open       `q.
 .d'    Web Components  `b.    Start or upgrade your web component project with
 .d'                     `b.   ease. All our recommendations at your fingertips.
 ::   .................   ::
 `p.                     .q'   See more details at https://open-wc.org/init/
  `p.    open-wc.org    .q'
   `b.     @openWc     .d'
     `q..            ..,'      Note: you can exit any time with Ctrl+C or Esc
        '',,,,,,,,,,''


? What would you like to do today? › - Use arrow-keys. Return to submit.
❯   Scaffold a new project
    Upgrade an existing project

Homepage

For more details please visit us at open-wc.org.

Packages

Package Version Description
building-rollup building-rollup Default configuration for working with rollup.
create create Scaffold web components following open-wc recommendations.
demoing-storybook demoing-storybook Storybook configuration following open-wc recommendations.
eslint-config eslint-config Eslint configuration following open-wc recommendations.
es-dev-server es-dev-server Development server for modern web apps.
polyfills-loader polyfills-loader Load web component polyfills using dynamic imports.
scoped-elements scoped-elements Auto define custom elements to scope them and avoid the name collision.
semantic-dom-diff semantic-dom-diff To compare dom and shadow dom trees. Part of open-wc recommendations.
testing testing Testing following open-wc recommendations.
testing-helpers testing-helpers Testing Helpers following open-wc recommendations.
testing-karma testing-karma Testing with Karma following open-wc recommendations.
testing-karma-bs testing-karma-bs Testing with Karma using Browserstack following open-wc recommendations.
testing-wallaby testing-wallaby Testing with wallaby following open-wc recommendations.

Contact

Feel free to reach out to us on 𝕏 (Twitter) or create a github issue for any feedback or questions you might have.

You can also find us on the Lit & Friends slack in the #open-wc channel.

You can join the Lit & Friends slack by visiting https://lit.dev/slack-invite/.

Sponsored by

Chrome's Web Framework & Tools Performance Fund Logo Divriots Logo

Supported by

Browserstack Logo netlify logo

Guide

# bootstrap/setup
npm install

# linting
npm run lint

# local testing
npm run test

# testing via browserstack
npm run test:bs

# run commands only for a specific scope
lerna run <command> --scope @open-wc/<package-name> --stream

custom-elements-manifest's People

Contributors

ajmchambers avatar bennypowers avatar break-stuff avatar dakmor avatar diervo avatar emilienleroy avatar hsablonniere avatar jonathantneal avatar jpzwarte avatar juliancataldo avatar kateutlik avatar klh avatar luwes avatar markacianfrani avatar markozabcic avatar matsuuu avatar matteematt avatar muratcorlu avatar peschee avatar sbolz avatar thepassle avatar tlouisse avatar westbrook avatar wickynilliams avatar xenobytezero avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

custom-elements-manifest's Issues

Tagname not getting populated if the class is a default export

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

https://custom-elements-manifest.netlify.app/?source=CmV4cG9ydCBkZWZhdWx0IGNsYXNzIE15RWxlbWVudCBleHRlbmRzIEhUTUxFbGVtZW50IHsKICBmb28gPSAnaGVsbG8nOwoKICAvKioKICAgKiBAYmFyIHRoaXMgaXMgY3VzdG9tIGluZm9ybWF0aW9uCiAgICovCiAgbWVzc2FnZTsKfQoKY3VzdG9tRWxlbWVudHMuZGVmaW5lKCJteS1lbGVtZW50IiwgTXlFbGVtZW50KTs%3D&library=null

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

Expected behavior
The link-class-to-tagname.js post process should link tagnames if they are not documented.

However theres a bug in the system currently which names the class "default" if it's default exported
e.g.

export default class MyComponent extends HTMLElement {

}

Which therefore breaks the linking process

Duplicate attributes using jsdoc

I'm not sure if this is intentional or not, but given this example:

https://github.com/storybookjs/storybook/blob/31e3ad8b816f8a0686d616309e100a7d0f3bad72/examples/web-components-kitchen-sink/src/components/sb-button.ts

that has the following attributes defined in the comments:

 * @attr {string} label - Label of the button
 * @attr {boolean} primary - primary
 * @attr {string} size - Size of the button, can be "small", "medium" or "large"; default is "medium".
 * @attr {string} background-color - Color of the button's background

The custom elements analyzer spits out:

   "attributes": [
            {
              "type": {
                "text": "string"
              },
              "description": "Label of the button",
              "name": "label"
            },
            {
              "type": {
                "text": "boolean"
              },
              "description": "primary",
              "name": "primary"
            },
            {
              "type": {
                "text": "string"
              },
              "description": "Size of the button, can be \"small\", \"medium\" or \"large\"; default is \"medium\".",
              "name": "size"
            },
            {
              "type": {
                "text": "string"
              },
              "description": "Color of the button's background",
              "name": "background-color"
            },
            {
              "name": "label",
              "fieldName": "label"
            },
            {
              "name": "primary",
              "fieldName": "primary"
            },
            {
              "name": "size",
              "fieldName": "size"
            },
            {
              "name": "background-color",
              "fieldName": "backgroundColor"
            }
          ],

This is probably because it parses the jsdoc but still parses the Lit properties?
I would expect it to merge them like:

...
            {
              "type": {
                "text": "string"
              },
              "description": "Label of the button",
              "name": "label",
              "fieldName": "label"
            },
...

Is this a bug or intentional?

I'm just running the standard npx command with the litelement flag.

The outdir in the config does not seem to work

Hey team 😉

I tried using outdir in my custom-elements-manifest.config.mjs but the file still gets generated at the default location. Using the CLI param works.

export default {
  globs: ['src/**/cc-*.js'],
  // Has no effect for now
  outdir: 'dist',
  plugins: [
  ],
};

Stack trace is printed if you specify unknown CLI options to `analyze`

cem --help works fine

cem analyze --help doesn't:

(node:33622) UnhandledPromiseRejectionWarning: UNKNOWN_OPTION: Unknown option: --help
    at commandLineArgs (/Users/jeroenz/Projects/dna/core/node_modules/command-line-args/dist/index.js:1374:21)
    at getCliConfig (file:///Users/jeroenz/Projects/dna/core/node_modules/@custom-elements-manifest/analyzer/src/utils/cli.js:73:10)

Not just --help, but any property cem doesn't know about, so cem analyze --silent:

(node:33623) UnhandledPromiseRejectionWarning: UNKNOWN_OPTION: Unknown option: --silent
    at commandLineArgs (/Users/jeroenz/Projects/dna/core/node_modules/command-line-args/dist/index.js:1374:21)
    at getCliConfig (file:///Users/jeroenz/Projects/dna/core/node_modules/@custom-elements-manifest/analyzer/src/utils/cli.js:73:10)

[plugin-lit] litelement properties are duplicate if JSDoc description is added

The following code:

/**
 * @prop {boolean} primary - Set button in primary mode
 */
class MyEl extends LitElement {
  static get properties() {
    return {
      primary: {type: Boolean}
    }
  }
}

customElements.define('my-el', MyEl)

Results in:

  {
          "kind": "class",
          "description": "",
          "name": "MyEl",
          "members": [
            {
              "type": {
                "text": "boolean"
              },
              "description": "- Set button in primary mode",
              "name": "primary",
              "kind": "field"
            },
            {
              "kind": "field",
              "name": "primary",
              "privacy": "public"
            }
          ],
          "attributes": [
            {
              "name": "primary",
              "fieldName": "primary"
            }
          ],
          "superclass": {
            "name": "LitElement"
          },
          "tagName": "my-el",
          "customElement": true
        }

The two properties in the CEM should be merged instead

EDIT: This is not limited to lit, but also regular class fields

Found playground issue: Default exported LitElement components crash the analyzer

Add a CLI/config entry to prevent CEM/A from writing to a package.json file

Expected behavior
Running the analyzer will only write to my package.json when I want it to.

CLI/config entry options*
So as not to make this change breaking, we likely want a negation style flag here. Some options for review:

cem analyze --no-package-json
cem analyze --no-linking
cem analyze --create-only
cem analyze --packagejson=false // defaulting to true
... more?
... better?

With my naive though that these kabob case flags would move to camel case in a config file.

Thoughts?

[Feature Request: to-markdown] support `--features` flag

Hi all-

I took a look through the docs and didnt see anything like this, so I figured I'd request it.

I think it would be really useful to support a option/flag in the to-markdown package and the CEM plugin for markdown that lets consumers decide which sections to include in the final MD output, similar to the --features option in the wca analyzer tool so that the final markdown output could be customized according to use case.

My first question is whether or not there's currently a way to do this, as I'm trying to use the generated markdown for technical docs for my design system, but Im not a fan of the file info sections that the markdown lib currently creates. Its making me flip flop about deciding which analyzer/markdown tool to use, this one or wca. I predict that wca may end up deprecated as the push for switching to the new manifest.json format is pushed forward, so i'd rather use this one, but I definitely would like to be able to customize the output a bit more

My second question is how similar in implementations are the two analyzers? is it a case of porting the feature over, or is there more to it because the implementations are drastically different?

I would be down to help if i can, but i'm not that familiar with ASTs and such.

Edit:

Looking through, could it be as simple as how the returned array in makeModuleDoc gets filled.
https://github.com/open-wc/custom-elements-manifest/blob/master/packages/to-markdown/index.js#L103

Thanks!

LitElement resolveInitializer module path mismatch

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

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

Expected behavior
When importing a module from another file to initialize a property, the resulting resolveInitializer field with module path does not match the path of the actual module resulting in the property's default value not getting extracted from the module. Here is a simple repo reproducing the issue. The prop here is initialized to a value from an imported module. In the generated manifest, the property's default field is set to ColorRed instead of the expected '#FF0000'. This is because module in resolveInitializer found here is set to /src/tokens/colors while the module's path in the manifest is equal to src/tokens/colors.ts found here and so the expected default value is not extracted.

TypeScript optional indicator for parameter/properties not reflected in type

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

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

Expected behavior
When using the ? operator in TypeScript to indicate an optional property, I would expect the type to be propType | undefined. In this playground example I would expect the type for foo to be string | undefined instead of string.

Litelement properties initialized with an imported value does not have type or default value defined

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

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

Expected behavior
Properties of a lit component that get initialized from an imported module (here's an example) do not have a type or default value defined in the manifest (in this case it would be string and 3.5rem). This was working correctly with web-component-analyzer.

`TypeError` when dispatching custom event with no type argument

Description

As the reproduction showcases the analyzer will fail if the dispatched custom event has no type argument. There are occasionally cases where no type is required, see this example.

Error thrown location: ERROR
Reproduction Link: REPRODUCTION

Solution

Preferably the analyzer will go one step beyond and analyze the event if it's a CustomEvent. This includes collecting documentation from the class declaration, and checking the super(...) call for a type argument.

Error

image

Doesn't work with refactored original code (same code, but smaller code)

Extend mixin vanilla js, missing attributes

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

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

Expected behavior
When I use a mixin for retrieving a Custom Element class, and extend using a mixin. I expect the attributes from the mixin I'm extending on te become available.

Some code to demonstrate:

class MyElementA extends getClassA(HTMLElement){}
class MyElementAB extends getClassAB(MyElementA){};

function getClassA(superClass = HTMLElement){
    
    return class extends superClass {
        static get observedAttributes() {
            return ['a'];
        }
       
        set a(val) {
            this._a = val;
        }
       
        get a() {
            return this._a;
        }
    }
}

function getClassAB(superClass = getClassA()){
    
    return class extends superClass {
        static get observedAttributes() {
            return ['b'];
        }
        
        set b(val) {
            this._b = val;
        }
       
        get b() {
            return this._b;
        }
    }
}
customElements.define('my-mixin-element-a', MyElementA);
customElements.define('my-mixin-element-ab', MyElementAB);
//customElements.define('my-element-a', getClassA);
//customElements.define('my-element-ab', getClassAB);

The result has no attributes at all:

{
  "schemaVersion": "1.0.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "src/my-element.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
          "name": "MyElementA",
          "mixins": [
            {
              "name": "getClassA",
              "module": "src/my-element.js"
            }
          ],
          "superclass": {
            "name": "HTMLElement"
          },
          "tagName": "my-mixin-element-a",
          "customElement": true
        },
        {
          "kind": "class",
          "description": "",
          "name": "MyElementAB",
          "mixins": [
            {
              "name": "getClassAB",
              "module": "src/my-element.js"
            }
          ],
          "superclass": {
            "name": "MyElementA",
            "module": "src/my-element.js"
          },
          "tagName": "my-mixin-element-ab",
          "customElement": true
        }
      ],
      "exports": [
        {
          "kind": "custom-element-definition",
          "name": "my-mixin-element-a",
          "declaration": {
            "name": "MyElementA",
            "module": "src/my-element.js"
          }
        },
        {
          "kind": "custom-element-definition",
          "name": "my-mixin-element-ab",
          "declaration": {
            "name": "MyElementAB",
            "module": "src/my-element.js"
          }
        }
      ]
    }
  ]
}

When I uncomment the last two lines of the example code, the result changes to this (and has the expected attributes)

{
  "schemaVersion": "1.0.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "src/my-element.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
          "name": "MyElementA",
          "mixins": [
            {
              "name": "getClassA",
              "module": "src/my-element.js"
            }
          ],
          "superclass": {
            "name": "HTMLElement"
          },
          "tagName": "my-mixin-element-a",
          "customElement": true,
          "attributes": [
            {
              "name": "a",
              "inheritedFrom": {
                "name": "getClassA",
                "module": "src/my-element.js"
              }
            }
          ],
          "members": [
            {
              "kind": "field",
              "name": "a",
              "inheritedFrom": {
                "name": "getClassA",
                "module": "src/my-element.js"
              }
            }
          ]
        },
        {
          "kind": "class",
          "description": "",
          "name": "MyElementAB",
          "mixins": [
            {
              "name": "getClassAB",
              "module": "src/my-element.js"
            }
          ],
          "superclass": {
            "name": "MyElementA",
            "module": "src/my-element.js"
          },
          "tagName": "my-mixin-element-ab",
          "customElement": true,
          "attributes": [
            {
              "name": "b",
              "inheritedFrom": {
                "name": "getClassAB",
                "module": "src/my-element.js"
              }
            },
            {
              "name": "a",
              "inheritedFrom": {
                "name": "MyElementA",
                "module": "src/my-element.js"
              }
            }
          ],
          "members": [
            {
              "kind": "field",
              "name": "b",
              "inheritedFrom": {
                "name": "getClassAB",
                "module": "src/my-element.js"
              }
            },
            {
              "kind": "field",
              "name": "a",
              "inheritedFrom": {
                "name": "MyElementA",
                "module": "src/my-element.js"
              }
            }
          ]
        },
        {
          "kind": "mixin",
          "description": "",
          "name": "getClassA",
          "members": [
            {
              "kind": "field",
              "name": "a"
            }
          ],
          "attributes": [
            {
              "name": "a"
            }
          ],
          "parameters": [
            {
              "name": "superClass",
              "default": "HTMLElement"
            }
          ]
        },
        {
          "kind": "mixin",
          "description": "",
          "name": "getClassAB",
          "members": [
            {
              "kind": "field",
              "name": "b"
            }
          ],
          "attributes": [
            {
              "name": "b"
            }
          ],
          "parameters": [
            {
              "name": "superClass",
              "default": "getClassA()"
            }
          ]
        }
      ],
      "exports": [
        {
          "kind": "custom-element-definition",
          "name": "my-mixin-element-a",
          "declaration": {
            "name": "MyElementA",
            "module": "src/my-element.js"
          }
        },
        {
          "kind": "custom-element-definition",
          "name": "my-mixin-element-ab",
          "declaration": {
            "name": "MyElementAB",
            "module": "src/my-element.js"
          }
        },
        {
          "kind": "custom-element-definition",
          "name": "my-element-a",
          "declaration": {
            "name": "getClassA",
            "module": "src/my-element.js"
          }
        },
        {
          "kind": "custom-element-definition",
          "name": "my-element-ab",
          "declaration": {
            "name": "getClassAB",
            "module": "src/my-element.js"
          }
        }
      ]
    }
  ]
}

The above result is only retrieved when all 4 lines are uncommented:

customElements.define('my-mixin-element-a', MyElementA);
customElements.define('my-mixin-element-ab', MyElementAB);
customElements.define('my-element-a', getClassA);
customElements.define('my-element-ab', getClassAB);

Inherited events don't have a type.

I'm not sure it this is intentional or not, but inherited events don't have an inherited type like methods and fields - it would be useful for events to also have the type.

Also, CSS properties aren't inherited - not sure if this is intentional either.

LitElement subclass overridden field incorrect type

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

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

Expected behavior
If a subclass initializes its own value for an inherited field I would expect that field to include the type value that it inherited. Here is a reproduction of the issue. I would expect type for foo under members for MySecondElement to be Greeting instead of string. It is shown correctly under attributes, though the default is incorrect and shows "hello" instead of the expected value "bye" (as seen under members).

Question: How do you generate descriptions?

Got the tool to work with my project that uses LitElement components. I don't see anything that documents how to generate descriptions though. Can someone point me in the right direction here?

LitElement subclass overridden field value not shown

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

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

Expected behavior
If a subclass initializes its own value for an inherited field I would expect that value to be shown as the default in the schema. Here is a reproduction of the issue. I would expect default for foo under MySecondElement to be "bye" instead of "hello."

LitElement subclass overridden field missing privacy

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

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

Expected behavior
If a subclass initializes its own value for an inherited field I would expect that field to include the privacy value that it inherited. Here is a reproduction of the issue. I would expect privacy for foo under MySecondElement to be public just as bar (which is also inherited and doesn't get overridden) has privacy set.

analyser tests failing on windows

Hey looks good, liking the vscode colours you've got going too.

I ran the analyser tests on windows and they were failing due to path issues:

globby wants forward slashes so added an explicitly posix version of the path

const packagePath = path.join(fixturesDir, `${testCase}/package`);
const packagePathPosix = packagePath.split(path.sep).join(path.posix.sep);

//...

const globs = await globby(packagePathPosix);

changed relativeModulePath to use the current separator instead of /:

const relativeModulePath = `.${path.sep}${path.relative(process.cwd(), glob)}`;

Importing the manifest using a local file url instead of an absolute path (absolute import paths not working on windows):

const manifestPathFileURL = pathToFileURL(`${packagePath}/custom-elements-manifest.config.js`).href;
try {
  const config = await import(manifestPathFileURL);
  plugins = [...config.default.plugins];
} catch {}

I've also added a .gitattributes file to the analyser folder to tell git on windows to always check out the fixtures output.json files with LF endings (was originally checking out those files as CLRF then after running tests they changed to LF)

# Auto detect text files and perform LF normalization
* text=auto

# Declare files that will always have LF line endings on checkout.
/fixtures/**/output.json text eol=lf

All tests pass for me on windows and mac after these changes, will make a pull request shortly.

Private methods and properties with `_` prefix

Hey team 😉,

Context

I'm still using the _ prefix convention for private properties and methods. I know # is coming but I don't want to transpile and while I wait for better support, I'm OK with the _ prefix convention.

WCA recognize _ as a private prefix and it also recognize # as a private prefix I think.

If I use those _ prefix with CEM analyzer:

https://custom-elements-manifest.netlify.app/?source=ZXhwb3J0IGNsYXNzIE15Q29tcG9uZW50IGV4dGVuZHMgTGl0RWxlbWVudCB7CgogIHN0YXRpYyBnZXQgcHJvcGVydGllcyAoKSB7CiAgICByZXR1cm4gewogICAgICBfcHJpdmF0ZVByb3A6IHsgdHlwZTogU3RyaW5nIH0sCiAgICAgICNwcml2YXRlRm9vYmFyOiB7IHR5cGU6IFN0cmluZyB9LAogICAgfTsKICB9CgogIF9wcml2YXRlTWV0aG9kKCkgewogICAgICBjb25zb2xlLmxvZygnZm9vJyk7CiAgfQoKICAjcHJpdmF0ZU1ldGhvZCgpIHsKICAgICAgY29uc29sZS5sb2coJ2ZvbycpOwogIH0KfQoKd2luZG93LmN1c3RvbUVsZW1lbnRzLmRlZmluZSgnbXktY29tcG9uZW50JywgTXlDb21wb25lbnQpOw%3D%3D&library=litelement

  • Properties prefixed with _ or # have a "privacy": "public".
  • Methods prefixed with _ or # don't have a privacy field.
  • If I manually add /** @private */ on methods, we get a "privacy": "private".
  • If I manually add /** @private */ on properties, it doesn't change anything.

Examples with /** @private */:

https://custom-elements-manifest.netlify.app/?source=ZXhwb3J0IGNsYXNzIE15Q29tcG9uZW50IGV4dGVuZHMgTGl0RWxlbWVudCB7CgogIHN0YXRpYyBnZXQgcHJvcGVydGllcyAoKSB7CiAgICByZXR1cm4gewogICAgICAvKiogQHByaXZhdGUgKi8KICAgICAgX3ByaXZhdGVQcm9wOiB7IHR5cGU6IFN0cmluZyB9LAogICAgICAvKiogQHByaXZhdGUgKi8KICAgICAgI3ByaXZhdGVGb29iYXI6IHsgdHlwZTogU3RyaW5nIH0sCiAgICB9OwogIH0KCiAgLyoqIEBwcml2YXRlICovCiAgX3ByaXZhdGVNZXRob2QoKSB7CiAgICAgIGNvbnNvbGUubG9nKCdmb28nKTsKICB9CgogIC8qKiBAcHJpdmF0ZSAqLwogICNwcml2YXRlTWV0aG9kKCkgewogICAgICBjb25zb2xlLmxvZygnZm9vJyk7CiAgfQp9Cgp3aW5kb3cuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCdteS1jb21wb25lbnQnLCBNeUNvbXBvbmVudCk7&library=litelement

Expectations

  1. I expected _ and # to be handled as private.
  2. I expected not to have the private stuffs in the JSON output.

What do you think about those?

comment-parser breaking change

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch @custom-elements-manifest/[email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/class-jsdoc.js b/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/class-jsdoc.js
index 8f67cbc..8d564d1 100644
--- a/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/class-jsdoc.js
+++ b/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/class-jsdoc.js
@@ -1,4 +1,4 @@
-import parse from 'comment-parser';
+import { parse } from 'comment-parser';
 import { handleJsDocType, normalizeDescription } from '../../utils/jsdoc.js';
 import { safe } from '../../utils/index.js';
 
@@ -25,7 +25,7 @@ export function classJsDocPlugin() {
            * Checks to see if the item is already in the classDoc, and if so merge and overwrite (JSDoc takes precedence)
            */
           node?.jsDoc?.forEach(jsDoc => {
-            const parsed = parse.parse(jsDoc?.getFullText());
+            const parsed = parse(jsDoc?.getFullText());
             parsed?.forEach(parsedJsDoc => {
 
               /**
diff --git a/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/handlers.js b/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/handlers.js
index 21ca2ca..99d220b 100644
--- a/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/handlers.js
+++ b/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/handlers.js
@@ -1,9 +1,9 @@
 import ts from 'typescript';
-import parse from 'comment-parser';
+import { parse } from 'comment-parser';
 
 import { has, resolveModuleOrPackageSpecifier, safe } from '../../../utils/index.js';
 import { handleJsDocType, normalizeDescription } from '../../../utils/jsdoc.js';
-import { isPrimitive, isWellKnownType } from '../../../utils/ast-helpers.js';
+import { isWellKnownType } from '../../../utils/ast-helpers.js';
 
 /**
  * @example static foo;
@@ -197,7 +197,7 @@ export function handleHeritage(classTemplate, moduleDoc, context, node) {
  */
 export function handleAttrJsDoc(node, doc) {
   node?.jsDoc?.forEach(jsDoc => {
-    const docs = parse.parse(jsDoc?.getFullText())?.find(doc => doc?.tags?.some(({tag}) => tag === 'attr'));
+    const docs = parse(jsDoc?.getFullText())?.find(doc => doc?.tags?.some(({tag}) => tag === 'attr'));
     const attrTag = docs?.tags?.find(({tag}) => tag === 'attr');
 
     if(attrTag?.name) {

This issue body was partially generated by patch-package.

Type inference for custom events TypeScript

I have a feature request for type generation for events.

I would like to write the following code:

/**
* Some extra info
*/
this.dispatchEvent(new CustomEvent<{ bar: string }>('foo', {
	detail: {
		bar: 'baz'
	}
}));

Would it be possible to infer the type of the event foo to the type argument { bar: 'baz' }?

This would save me from duplicating the information to a comment, which can get out of sync with the code. This also allows TypeScript to check if my event detail has the correct type.

What I have to write right now:

/**
* Some extra info
*
* @type {{ bar: string }}
*/
this.dispatchEvent(new CustomEvent('foo', {
	detail: {
		bar: 'baz'
	}
}));

The simular package web-component-analyzer does this (there are other reasons why I am not using that package). See the example of event typing

`--outdir` file path not reflected in `package.json`

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

Bug
I set the output directory for my manifest file to /dist/, but it is not reflected in package.json after running cem analyze script.

Expected behavior
I expect that if I set --outdir to dist in the script, --outdir will be reflected in package.json:

Command:

cem analyze --globs src/foo-*.ts --litelement --outdir dist

package.json:

{
  "name": "foo-button",
  "customElements": "dist/custom-elements.json"
}

Circular reference when converting to JSON schema

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

In writing a custom element to render custom elements manifest in a friendly format, we've encountered an issue when trying to generate the custom element manifest for this component.

I've included a txt file at the end so I could identify the cyclical items in the object that is being passed to JSON.stringify

We're using latest of lit (2.0.0-rc.2) and TypeScript 4.3.4.

Output [COLLECT PHASE]: src/ce-schema-viewer.ts [ANALYZE PHASE]: src/ce-schema-viewer.ts [MODULE LINK PHASE]: src/ce-schema-viewer.ts [PACKAGE LINK PHASE] Waiting for the debugger to disconnect... file:///C:/code/zywave/custom-element-viewer/node_modules/@custom-elements-manifest/analyzer/index.js:78 fs.writeFileSync(`${process.cwd()}${path.sep}custom-elements.json`, `${JSON.stringify(customElementsManifest, null, 2)}\n`); ^

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'SourceFileObject'
| property 'statements' -> object with constructor 'Array'
| index 0 -> object with constructor 'NodeObject'
--- property 'parent' closes the circle
at JSON.stringify ()
at run (file:///C:/code/zywave/custom-element-viewer/node_modules/@custom-elements-manifest/analyzer/index.js:78:83)
at processTicksAndRejections (node:internal/process/task_queues:93:5)
at async file:///C:/code/zywave/custom-element-viewer/node_modules/@custom-elements-manifest/analyzer/index.js:85:5

Expected behavior
I'd expect for the custom element manifest JSON file to generate.

Reproduction
Repro

custom element manifest bug.txt

Feature Request: Pre-fill the code field in the playground on error

It would be a nice user experience if, on error, as we get provided the playground link, it would have the parameters so that the code part is pre-filled with the contents of the file that caused the error.

This would make it easier and less cumbersome to submit bug reports.

It would mean that we need to add the source to the withErrorHandling function, and then parse the text property of the object.

What might make this ticket harder is if all of the use cases can't access the file responsible. But in these cases we could maybe just not generate it?

How to handle private class fields

Currently If you have a class with private class fields, cem outputs those fields.

class Foo {
  #bar = 'bar';
}

Example cem output (from another lit element web component):

            {
              "kind": "field",
              "name": "#initials",
              "privacy": "private",
              "type": {
                "text": "string | undefined"
              },
              "description": "The actual initials shown in the UI. We keep this as a separate variable to determine\nwhether the initials are derived from the displayName or explicitly set via the initials property."
            },

Unlike typescript private keyword fields, private class fields are really private. So trying to access foo.#bar results in a syntax error.

Since you cannot access these private class fields from outside the class, should cem really output these fields?

Consider custom element manifest files of imported packages

my deps

node_modules/base/MyBase.js
node_modules/base/package.json
node_modules/base/custom-elements-manifst.json

👉 node_modules/base/MyBase.js

import { LitElement } from 'lit-element';

export class MyElement extends LitElement {
  static get properties() {
    return {
      awesome: { type: Boolean }
    }
  }

  constructor() {
    super();
    this.awesome = true;
  }
}

👉 node_modules/base/custom-elements-manifst.json

// ...
          "members": [
            {
              "kind": "field",
              "name": "awesome",
              "type": {
                "text": "boolean"
              },
              "default": "true",
              "privacy": "public",
              "attribute": "awesome"
            }
          ],
// ...

https://custom-elements-manifest.netlify.app/?source=CmltcG9ydCB7IExpdEVsZW1lbnQgfSBmcm9tICdsaXQtZWxlbWVudCc7CgpleHBvcnQgY2xhc3MgTXlCYXNlIGV4dGVuZHMgTGl0RWxlbWVudCB7CiAgc3RhdGljIGdldCBwcm9wZXJ0aWVzKCkgewogICAgcmV0dXJuIHsKICAgICAgYXdlc29tZTogeyB0eXBlOiBCb29sZWFuIH0KICAgIH0KICB9CgogIGNvbnN0cnVjdG9yKCkgewogICAgc3VwZXIoKTsKICAgIHRoaXMuYXdlc29tZSA9IHRydWU7CiAgfQp9Cg%3D%3D&library=litelement

my element

class MyEl extends MyBase {
  static get properties() {
    return {
      rainbow: { type: Boolean }
    }
  }

  constructor() {
     super();
     this.rainbow = true;
  }
}

Actual behavior

Generated CEM or docs for MyEl lists only rainbow as a member but not awesome.

// ...
          "members": [
            {
              "kind": "field",
              "name": "rainbow",
              "type": {
                "text": "boolean"
              },
              "default": "true",
              "privacy": "public",
              "attribute": "rainbow"
            }
          ],
// ...

Expected behavior

Generated CEM or docs for MyEl should list rainbow and awesome as a member.

// ...
          "members": [
            {
              "kind": "field",
              "name": "rainbow",
              "type": {
                "text": "boolean"
              },
              "default": "true",
              "privacy": "public",
              "attribute": "rainbow"
            },
            {
              "kind": "field",
              "name": "awesome",
              "type": {
                "text": "boolean"
              },
              "default": "true",
              "privacy": "public",
              "attribute": "awesome",
              "inheritedFrom": {
                "name": "MyBase",
                "module": "base/MyBase.js"
              }
            }
          ],
// ...

JSDoc description for `@prop` with a `- ` prefix

Hey team 😉,

I'm used to write my JSDoc like this:

@prop {String} foobar - My awesome description

This is how it's documented on JSDoc and how I used it with WCA.

As you can see in this:

https://custom-elements-manifest.netlify.app/?source=LyoqCiAqIEhlbGxvCiAqIAogKiBAcHJvcCB7U3RyaW5nfSBmb29iYXIgLSBNeSBhd2Vzb21lIGRlc2NyaXB0aW9uCiAqLwpleHBvcnQgY2xhc3MgTXlDb21wb25lbnQgZXh0ZW5kcyBMaXRFbGVtZW50IHsKCiAgc3RhdGljIGdldCBwcm9wZXJ0aWVzICgpIHsKICAgIHJldHVybiB7CiAgICAgIGZvb2JhcjogeyB0eXBlOiBTdHJpbmcgfSwKICAgIH07CiAgfQp9Cgp3aW5kb3cuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCdteS1jb21wb25lbnQnLCBNeUNvbXBvbmVudCk7&library=litelement

CEM generates a JSON with:

"members": [
  {
    "type": {
      "text": "String"
    },
    "description": "- My awesome description",
    "name": "foobar",
    "kind": "field",
    "privacy": "public"
  }
]

I think I expected it not to take the - .

  • What do you think?
  • Is it something that needs to be fixed?
  • Maybe I'm the one not properly using JSDoc.

TypeScript error: "tagName" does not exist in type 'ClassDeclaration'

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

In the playground (or command line), use this custom element:

class MyElement extends HTMLElement {}
customElements.define("my-element", MyElement)

You'll get this JSON:

click to expand
{
  "schemaVersion": "1.0.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "src/my-element.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
          "name": "MyElement",
          "superclass": {
            "name": "HTMLElement"
          },
          "tagName": "my-element",
          "customElement": true
        }
      ],
      "exports": [
        {
          "kind": "custom-element-definition",
          "name": "my-element",
          "declaration": {
            "name": "MyElement",
            "module": "src/my-element.js"
          }
        }
      ]
    }
  ]
}

If you compare this to the schema, it doesn't match because of tagName on the ClassDeclaration.

You can repro using a simple TypeScript file:

click to expand
import { Package } from 'custom-elements-manifest/schema'

const json: Package = {
  "schemaVersion": "1.0.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "index.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
          "name": "MyElement",
          "superclass": {
            "name": "HTMLElement"
          },
          "tagName": "my-element",
          "customElement": true
        }
      ],
      "exports": [
        {
          "kind": "custom-element-definition",
          "name": "my-element",
          "declaration": {
            "name": "MyElement",
            "module": "index.js"
          }
        }
      ]
    }
  ]
}

Then run:

mkdir test
cd test
npm init --yes
npm i --save [email protected] [email protected]
npx tsc index.ts

You'll see the error:

test.ts:18:11 - error TS2322: Type '{ kind: "class"; description: string; name: string; superclass: { name: string; }; tagName: string; customElement: true; }' is not assignable to type 'Declaration'.
  Object literal may only specify known properties, and '"tagName"' does not exist in type 'ClassDeclaration'.

18           "tagName": "my-element",
             ~~~~~~~~~~~~~~~~~~~~~~~


Found 1 error.

Expected behavior

The output schema should match the one from custom-elements-manifest .

I'm not sure if this is a problem in the analyzer or in the custom-elements-manifest schema itself, but I'm just reporting it.

`#private` fields should not inherit

Checklist

  • Did you run the analyzer with the --dev flag to get more information?
  • Did you create a minimal reproduction in the playground?

https://custom-elements-manifest.netlify.app/?source=CmV4cG9ydCBjbGFzcyBBIGV4dGVuZHMgSFRNTEVsZW1lbnQgewogICNwcml2YXRlID0gJ2hlbGxvJzsKICBwcml2YXRlIHByaXYgPSAnaGVsbG8nOwp9CgpleHBvcnQgY2xhc3MgQiBleHRlbmRzIEEgewp9&library=null

Source

export class A extends HTMLElement {
  #private = 'hello';
  private priv = 'hello';
}

export class B extends A {
}

Expected behavior
#private and priv should not inherit to B, the first because it's a runtime error, the second because it's a TypeScript error.
Actual

{
  "kind": "class",
  "description": "",
  "name": "B",
  "superclass": {
    "name": "A",
    "module": "src/my-element.js"
  },
  "members": [
    {
      "kind": "field",
      "name": "#private",
      "privacy": "private",
      "type": {
        "text": "string"
      },
      "default": "'hello'",
      "inheritedFrom": {
        "name": "A",
        "module": "src/my-element.js"
      }
    },
    {
      "kind": "field",
      "name": "priv",
      "type": {
        "text": "string"
      },
      "privacy": "private",
      "default": "'hello'",
      "inheritedFrom": {
        "name": "A",
        "module": "src/my-element.js"
      }
    }
  ]
}

[to-markdown] Can't find module error in commonjs

Hi all!

I'm trying to use the to-markdown package in nodejs as commonjs. and getting the following stack trace:

$ node ./tasks/build-readmes.js
internal/modules/cjs/loader.js:892
  throw err;
  ^

Error: Cannot find module './lib/fp.js'
Require stack:
- /Users/<ME>/Documents/Repos/<REPO>/node_modules/@custom-elements-manifest/to-markdown/dist/index.cjs
- /Users/<ME>/Documents/Repos/<REPO>/tasks/build-readmes.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:889:15)
    at Function.Module._load (internal/modules/cjs/loader.js:745:27)
    at Module.require (internal/modules/cjs/loader.js:961:19)
    at require (internal/modules/cjs/helpers.js:92:18)
    at Object.<anonymous> (/Users/<ME>/Documents/Repos/<REPO>/node_modules/@custom-elements-manifest/to-markdown/dist/index.cjs:28:28)
    at Module._compile (internal/modules/cjs/loader.js:1072:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1101:10)
    at Module.load (internal/modules/cjs/loader.js:937:32)
    at Function.Module._load (internal/modules/cjs/loader.js:778:12)
    at Module.require (internal/modules/cjs/loader.js:961:19) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/Users/<ME>/Documents/Repos/<REPO>/node_modules/@custom-elements-manifest/to-markdown/dist/index.cjs',
    '/Users/<ME>/Documents/Repos/<REPO>/tasks/build-readmes.js'
  ]
}

I basically just copied the import from the docs and switched it to commonjs/require notation so im importing like:

const { customElementsManifestToMarkdown } = require('@custom-elements-manifest/to-markdown');

and using like:

const manifest = JSON.parse(fs.readFileSync(ceJsonLocation, 'utf-8'));
const markdown = customElementsManifestToMarkdown(manifest);

I checked the exports in package.json and its correctly pointing at dist/index.cjs, but the path in that file to fp.js is pointing at ./lib/fp.js but since the index.cjs is in dist, i think that path should be ../lib/fp.js maybe?

Stencil compatibility

Hey there @thepassle! This tool is looking amazing for CEM. Is there anything I can do to help improve the Stencil compatibility, either within this code or within Stencil?

I can't quite tell if analyzer code is parsing TypeScript or if you're parsing final compiled files. We do have an output target format that doesn't include the lazy loading which splits definitions out into their own files - dist-custom-elements. Would targeting that help?

Support interface extensions?

Now that class fields are implemented in Chrome (and other browsers), combined with the latest Typescript compiler that can compile to this standard, I recently discovered something that caused me to reevaluate how I define vanilla web components.

If I define a custom element like this:

export class MyCustomElement extends HTMLElement{
    myField;
}
customElements.define('my-custom-element', MyCustomElement);

This allowed Typescript consumers of the class to recognize that there is a field/property called myField, with minimal fuss, and also the custom elements manifest analyzer would add it to custom-elements.json. The problem is, during runtime, if the property value of myField is passed to an my-custom-element instance before the custom element class is registered, so that at that point it is an unknown element, the value passed gets lost when the class is registered. That non-assigned field, placed there to get Typescript support, as well as automatic inclusion in custom-elements.json, is causing asynchronous loading to stop working properly.

So the work around I've found, with Typescript, is to define an interface in a separate types.d.ts file (condensed here for simplicty):

export class MyCustomElement extends HTMLElement{
}
customElements.define('my-custom-element', MyCustomElement);
export interface MyCustomElementProps{
  /**
 *  @prop {string} [myField=someDefaultValue]
*/
  myField: string;
}
export interface MyCustomElement extends MyCustomElementProps {}

Now, thanks to that strange last line, I get the compile time / autocomplete help I was getting before (with the simpler class field) from TypeScript, but without the damage to asynchronous loading. But the custom-element analyzer doesn't seem to pick up the associated property/field information from the extended interface.

Not sure if that's been looked at and not something the Typescript AST libraries pick up, so it is really difficult to implement? Or does any plugin pick it up already?

[FAST] Investigate new way of defining custom elements

import { DesignSystem } from "@microsoft/fast-foundation"
import { fastAnchor } from '@microsoft/fast-components';

DesignSystem.getOrCreate().register(
    fastAnchor()
);
import { DesignSystem } from "@microsoft/fast-foundation"
import { allComponents } from '@microsoft/fast-components';

DesignSystem.getOrCreate().register(
    Object.values(allComponents).map(definition => definition())
);
export const fastAnchor = Anchor.compose({
    baseName: "anchor",
    template,
    styles,
    shadowOptions: {
        delegatesFocus: true,
    },
});

DesignSystem.getOrCreate().register(fastAnchor({prefix: "fluent"})) defines the anchor as <fluent-anchor></fluent-anchor>

pluggable module resolution?

Goal: Plug in to analyzer's module resolution so I can resolve across monorepo packages

Relevant Code:

export function resolveModuleOrPackageSpecifier(moduleDoc, context, name) {
const foundImport = context?.imports?.find(_import => _import.name === name);
/* item is imported from another file */
if(foundImport) {
if(foundImport.isBareModuleSpecifier) {
/* import is from 3rd party package */
return { package: foundImport.importPath }
} else {
/* import is imported from a local module */
return { module: new URL(foundImport.importPath, `file:///${moduleDoc.path}`).pathname }
}
} else {
/* item is in current module */
return { module: moduleDoc.path }
}
}

Given input like:

// @apollo-elements/core/index.js
export { ApolloQueryController } from './apollo-query-controller.js';
// @apollo-elements/fast/apollo-query-behavior.js
import { ApolloQueryController } from '@apollo-elements/core/apollo-query-controller';

export class ApolloQueryBehavior extends ApolloQueryController {...}

Analyser will identify '@apollo-elements/core/apollo-query-controller'; as a bare specifier and quit.
The final manifest for @apollo-elements/fast will contain the members of ApolloQueryBehavior, but not the members of its ancestor ApolloQueryController.

I'd like to write a small plugin to hook into the module resolution so that I can point local monorepo imports to their relevant source files.

Alternatives

I think I could write a large plugin to collate manifest members across packages in packageLinkPhase, but it would be nicer to just use the core's code to do that stuff rather than replicate what analyzer does anyways.

Support output path flag for analyzer

custom-elements-manifest/analyzer automatically outputs the schema file custom-elements.json into whichever directory you run the analyzer, but I'd like to be able to specify a path for the file to output into.

For example, I have a package called foo-button. When analyzing files inside /foo-button/src/, I want custom-elements.json to output into /foo-button/dist/ instead of in the current directory /foo-button/:

foo-button/
├── dist/
│   ├── custom-elements.json
├── src/
│   ├── foo-button.ts
│   ├── foo-button-dropdown.ts
│   ├── foo-button-group.ts
│   ├── index.ts
├── package.json

Here's the script I'm using to analyze files in /foo-button/:

cem analyze --globs "./src/foo-*.ts" --litelement

Would like a flag like this to specify output path:

cem analyze --globs "./src/foo-*.ts" --litelement --out "./dist/"

Thanks!

Rework default exported classes

export default class Foo {}

Should be:

{
  "schemaVersion": "1.0.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "src/my-element.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
+         "name": "Foo"
        }
      ],
      "exports": [
        {
          "kind": "js",
+         "name": "default",
          "declaration": {
+           "name": "Foo",
            "module": "src/my-element.js"
          }
        }
      ]
    }
  ]
}

Instead of (currently):

{
  "schemaVersion": "1.0.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "src/my-element.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
+         "name": "default"
        }
      ],
      "exports": [
        {
          "kind": "js",
+         "name": "default",
          "declaration": {
+           "name": "default",
            "module": "src/my-element.js"
          }
        }
      ]
    }
  ]
}

See also: webcomponents/custom-elements-manifest#72

Notes:

Class members with same name as static members may be ignored

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch @custom-elements-manifest/[email protected] for the project I'm working on.

class A {
  static a: A;
  a: A;
}

expected:

"members": [
  {
    "kind": "field",
    "name": "a",
    "type": {
      "text": "A"
    },
    "static": true
  },
  {
    "kind": "field",
    "name": "a",
    "type": {
      "text": "A"
    }
  }
],

actual:

"members": [
  {
    "kind": "field",
    "name": "a",
    "type": {
      "text": "A"
    },
    "static": true
  }
],

Here is the diff that solved my problem:

diff --git a/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createClass.js b/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createClass.js
index 0d60361..8c76e1b 100644
--- a/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createClass.js
+++ b/node_modules/@custom-elements-manifest/analyzer/src/features/analyse-phase/creators/createClass.js
@@ -4,7 +4,7 @@ import { createAttribute, createAttributeFromField } from './createAttribute.js'
 import { createField } from './createClassField.js';
 import { handleHeritage, handleJsDoc, handleAttrJsDoc } from './handlers.js';
 import { hasDefaultModifier } from '../../../utils/exports.js';
-import { isProperty, isDispatchEvent, hasAttrAnnotation, isReturnStatement, isPrimitive } from '../../../utils/ast-helpers.js';
+import { isProperty, isStaticMember, isDispatchEvent, hasAttrAnnotation, isReturnStatement, isPrimitive } from '../../../utils/ast-helpers.js';
 
 
 /**
@@ -26,6 +26,7 @@ export function createClass(node, moduleDoc) {
   };
 
   node?.members?.forEach(member => {
+
     /**
      * Handle attributes
      */
@@ -79,13 +80,16 @@ export function createClass(node, moduleDoc) {
      * Handle fields
      */
     if (isProperty(member)) {
-      if (gettersAndSetters.includes(member?.name?.getText())) {
-        return;
-      } else {
-        gettersAndSetters.push(member?.name?.getText());
+      if (!isStaticMember(member)) {
+        if (gettersAndSetters.includes(member?.name?.getText())) {
+          return;
+        } else {
+          gettersAndSetters.push(member?.name?.getText());
+        }
       }
 
       const field = createField(member);
+
       classTemplate.members.push(field);
 
       /**
diff --git a/node_modules/@custom-elements-manifest/analyzer/src/utils/ast-helpers.js b/node_modules/@custom-elements-manifest/analyzer/src/utils/ast-helpers.js
index df87c86..73cb792 100644
--- a/node_modules/@custom-elements-manifest/analyzer/src/utils/ast-helpers.js
+++ b/node_modules/@custom-elements-manifest/analyzer/src/utils/ast-helpers.js
@@ -85,3 +85,6 @@ export function getElementNameFromDecorator(decorator) {
  * @example @attr({attribute: 'my-el'})
  */
 export const getOptionsObject = decorator => decorator?.expression?.arguments?.find(arg => arg.kind === ts.SyntaxKind.ObjectLiteralExpression);
+
+export const isStaticMember = member =>
+  member.modifiers?.some?.(x => x.kind === ts.SyntaxKind.StaticKeyword);
diff --git a/node_modules/@custom-elements-manifest/analyzer/src/utils/mixins.js b/node_modules/@custom-elements-manifest/analyzer/src/utils/mixins.js
index 2361e7d..d12375a 100644
--- a/node_modules/@custom-elements-manifest/analyzer/src/utils/mixins.js
+++ b/node_modules/@custom-elements-manifest/analyzer/src/utils/mixins.js
@@ -62,15 +62,20 @@ export function extractMixinNodes(node) {
         const classDeclaration = node.body.statements.find(statement => ts.isClassDeclaration(statement));
         const returnStatement = node.body.statements.find(statement => ts.isReturnStatement(statement));
 
+        /** Avoid undefined === undefined */
+        if(!(classDeclaration && returnStatement))
+          return;
+
+        const classDeclarationName = classDeclaration.name?.getText?.();
+        const returnValue =
+            returnStatement.expression?.kind === ts.SyntaxKind.AsExpression ? returnStatement.expression.expression.getText()
+          : returnStatement.expression?.getText();
+
         /**
          * If the classDeclaration inside the function body has the same name as whats being 
          * returned from the function, consider it a mixin
          */
-        if(
-          /** Avoid undefined === undefined */
-          (classDeclaration && returnStatement) &&
-          (classDeclaration?.name?.getText() === returnStatement?.expression?.getText())
-        ) {
+        if (classDeclarationName === returnValue) {
           return {
             mixinFunction: node,
             mixinClass: classDeclaration

This issue body was partially generated by patch-package.

support @ignore tag

https://jsdoc.app/tags-ignore.html

The @ignore tag indicates that a symbol in your code should never appear in the documentation. This tag takes precedence over all others.

Given this source:

const priv = Symbol();
export class MyEl extends HTMLElement {
    f() { return this[priv](); }
    /** @ignore */
    [priv]() { return 'Hi, Pascal!'; }
}

I expect this output:

{
  "schemaVersion": "0.1.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "src/my-element.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
          "name": "MyEl",
          "members": [
            {
              "kind": "method",
              "name": "f"
            }
          ],
          "superclass": {
            "name": "HTMLElement"
          },
          "customElement": true
        }
      ],
      "exports": [
        {
          "kind": "js",
          "name": "MyEl",
          "declaration": {
            "name": "MyEl",
            "module": "src/my-element.js"
          }
        }
      ]
    }
  ]
}

instead actual result is:

{
  "schemaVersion": "0.1.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "src/my-element.js",
      "declarations": [
        {
          "kind": "class",
          "description": "",
          "name": "MyEl",
          "members": [
            {
              "kind": "method",
              "name": "f"
            },
            {
              "kind": "method",
              "name": "[priv]"
            }
          ],
          "superclass": {
            "name": "HTMLElement"
          },
          "customElement": true
        }
      ],
      "exports": [
        {
          "kind": "js",
          "name": "MyEl",
          "declaration": {
            "name": "MyEl",
            "module": "src/my-element.js"
          }
        }
      ]
    }
  ]
}

Issue with JSDoc's @link tag

Typescript typings for plugin authors

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch @custom-elements-manifest/[email protected] for the project I'm working on.

This should work, the only thing that I'm not sure about is the type imports from the custom-elements-manifest package, which didn't appear to work for me, so please check.

Here is the diff that solved my problem:

diff --git a/node_modules/@custom-elements-manifest/analyzer/index.d.ts b/node_modules/@custom-elements-manifest/analyzer/index.d.ts
new file mode 100644
index 0000000..994d363
--- /dev/null
+++ b/node_modules/@custom-elements-manifest/analyzer/index.d.ts
@@ -0,0 +1,103 @@
+import * as TS from 'typescript'
+import { Module, Package } from 'custom-elements-manifest';
+
+/** Plugin execution context. Pass arbitrary data here. */
+export type Context = Record<string, unknown>;
+
+export interface CollectPhaseParams {
+  /**
+   * TypeScript API
+   */
+  ts: TS;
+
+  /**
+   * The current TypeScript AST Node
+   */
+  node: TS.Node;
+
+  /**
+   * Plugin execution context. Pass arbitrary data here.
+   */
+  context: Context;
+}
+
+export interface AnalyzePhaseParams {
+  /**
+   * TypeScript API
+   */
+  ts: TS;
+
+  /**
+   * The current TypeScript AST Node
+   */
+  node: TS.Node;
+
+  /**
+   * The current state of the current module's manifest
+   */
+  moduleDoc: Partial<Module>;
+
+  /**
+   * Plugin execution context. Pass arbitrary data here.
+   */
+  context: Context;
+}
+
+export interface ModuleLinkPhaseParams {
+  /**
+   * The completed manifest, i.e. the output of the analyze phase
+   */
+  moduleDoc: Module;
+
+  /**
+   * Plugin execution context. Pass arbitrary data here.
+   */
+  context: Context;
+}
+
+export interface PackageLinkPhaseParams {
+  /**
+   * The completed manifest, i.e. the output of the analyze phase
+   */
+  customElementsManifest: Package;
+
+  /**
+   * Plugin execution context. Pass arbitrary data here.
+   */
+  context: Context;
+}
+
+/**
+ * A Custom Elements Manifest Analyzer plugin
+ */
+export interface Plugin {
+  /**
+   * @summary Plugin hook that runs in the analyze phase.
+   *
+   * Runs for all modules in a project, before continuing to the `analyzePhase`
+   */
+  collectPhase(params: CollectPhaseParams): void;
+
+  /**
+   * @summary Plugin hook that runs in the analyze phase.
+   *
+   * Runs for each AST node in each module.
+   * You can use this phase to access a module's AST nodes and mutate the manifest.
+   */
+  analyzePhase(params: AnalyzePhaseParams): void;
+
+  /**
+   * @summary Plugin hook that runs in the module-link phase.
+   *
+   * Post-processing hook that runs for each module, after analyzing.
+   * All information about your module should now be available.
+   */
+  moduleLinkPhase(params: ModuleLinkPhaseParams): void;
+
+  /**
+   * @summary Plugin hook that runs in the package-link phase.
+   *
+   * Runs once per package, after modules have been parsed and after per-module post-processing
+   */
+  packageLinkPhase(params: PackageLinkPhaseParams): void;
+}

This issue body was partially generated by patch-package.

Improved TypeScript ergonomics

This issue tracks some nice-to-have enhancements for type detection.

The analyzer does great picking up on explicitly typed members:

function XMixin<B extends Constructor<{ b: B }>(superclass: B) {
  return class XL<A> extends superclass {
    x: X<A> = new X<A>(this.b);
    bool: boolean = true;
  }
}

Here,

  • the mixin parameter will be correctly typed as B
  • the x field will be correctly typed as X<A>
  • the bool field will be typed as boolean

However, it would be nice if for the following code:

function XMixin<B extends Constructor>(superclass: B) {
  return class XL<A> extends superclass {
    x = new X<A>();
    bool = true;
  }
}

Would give:

  • the parameter as B extends Constructor<{ b: B }>
  • the x field as X<A> (even without an explicit typing)
  • the bool field as boolean (even without an explicit typing)

I'm guessing that the TypeScript API has the chops to infer those things, given a SourceFile AST, but I haven't looked into it just yet.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.