GithubHelp home page GithubHelp logo

open-wc / custom-elements-manifest-deprecated Goto Github PK

View Code? Open in Web Editor NEW
8.0 4.0 4.0 8.21 MB

Custom Elements Manifest is a file format that describes custom elements in your project.

JavaScript 100.00%
custom-elements customelements webcomponents web-components open-wc hacktoberfest

custom-elements-manifest-deprecated's Introduction

custom-elements-manifest-deprecated's People

Contributors

thepassle avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

custom-elements-manifest-deprecated's Issues

[core] finish adding inherited members/attrs/events for a class

Currently it only does fields, finish attrs/events as well

Also make sure to not have duplicates, e.g.: if a method is already present in the class's members, it means the class is overriding it

example:

class Foo extends Super {
  someMethod(){}
}

class Super extends HTMLElement {
  someMethod(){}
}

someMethod should only be in the output classdoc for Foo once

mixins not output correctly

import { LitElement } from '@lion/core';
import { ChoiceGroupMixin, FormGroupMixin } from '@lion/form-core';

/**
 * A wrapper around multiple radios.
 */
export class LionRadioGroup extends ChoiceGroupMixin(FormGroupMixin(LitElement)) {
  connectedCallback() {
    super.connectedCallback();
    this.setAttribute('role', 'radiogroup');
  }

  /**
   * @override FormGroupMixin, during a reset if the current checked value is behind
   * the initial checked value, they both got unchecked
   */
  resetGroup() {
    let initValue;
    this.formElements.forEach(child => {
      if (typeof child.resetGroup === 'function') {
        child.resetGroup();
      } else if (typeof child.reset === 'function') {
        child.reset();
        // If the value was initially checked save this
        if (child.checked) {
          initValue = child.value;
        }
      }
    });
    this.modelValue = initValue;

    this.resetInteractionState();
  }
}
{
      "kind": "javascript-module",
      "path": "./src/LionRadioGroup.js",
      "declarations": [
        {
          "kind": "class",
          "name": "LionRadioGroup",
          "mixins": [
            {
              "name": "ChoiceGroupMixin",
              "package": "@lion/form-core"
            },
            {
              "name": "ChoiceGroupMixin",
              "package": "@lion/form-core"
            }
          ],
          "superclass": {
            "name": "LitElement",
            "package": "@lion/core"
          },
          "members": [
            {
              "kind": "method",
              "name": "resetGroup",
              "privacy": "public"
            }
          ],
          "tagName": "lion-radio-group"
        }
      ],
      "exports": [
        {
          "kind": "js",
          "name": "LionRadioGroup",
          "declaration": {
            "name": "LionRadioGroup",
            "module": "./src/LionRadioGroup.js"
          }
        }
      ]
    }

duplicate attributes

/**
 * Banner component
 *
 * @attr type - Determines the style, can be "info", "warning", or "error". Default is "info"
 * @attr corner - Determines if banner sets position at upper right corner or not.
 *
 * @slot header - Primary message of the banner.
 * @slot content - Secondary message of the banner. Used to provide a description.
 */
export class Banner extends SpectrumElement {
    @property({ reflect: true, type: String })
    public type: 'info' | 'warning' | 'error' = 'info';

    @property({ reflect: true, type: Boolean })
    public corner = false;
}
"attributes": [
            {
              "name": "type",
              "type": {
                "type": ""
              },
              "description": "Determines the style, can be \"info\", \"warning\", or \"error\". Default is \"info\""
            },
            {
              "name": "corner",
              "type": {
                "type": ""
              },
              "description": "Determines if banner sets position at upper right corner or not."
            },
            {
              "name": "type",
              "fieldName": "type"
            },
            {
              "name": "corner",
              "fieldName": "corner"
            }
          ],

`type`s are References

If types are imported they should have a module or package property

import Person from './Person';

const foo: Person = {};

[core] exported anonymous function should have named "default" instead of ""

e.g.:

export default function() {}
export default () => {}

Currently shows up as:

{
  "declarations": [
    {
      "kind": "function",
      "name": ""
    }
  ],
  "exports": [
    {
      "kind": "js",
      "name": "default",
      "declaration": {
        "name": "",
        "module": "./my-element.js"
      }
    }
  ]
}

Expected:

{
  "declarations": [
    {
      "kind": "function",
      "name": "default"
    }
  ],
  "exports": [
    {
      "kind": "js",
      "name": "default",
      "declaration": {
        "name": "default",
        "module": "./my-element.js"
      }
    }
  ]
}

add --dev flag

add --dev flag to output custom-elements.json to the console.

[core] CLI interface

Currently everything is hardcoded.

  • It'd be nice to have the user be able to specify a glob
  • specify the directory to output custom-elements.json
  • more?

[core] Polymer 3 support

I think this only requires a small change to the static properties getter, since it supports a default value, e.g.:

  static get properties() {
    return {
      mode: {
        type: String,
        value: 'auto'
      },

      data: {
        type: Object,
        notify: true,
        value: function() { return {}; }
      }
    }
  }

Note that objects and arrays are a function that return an object or array

improve window.customElements.define

Currently only customElements.define is picked up as a export of kind "custom-elements-definition". We should also pick up window.customElements.define.

It now naively handles window.customElements.define, but it would be nice to implement the helper code lars mentioned

[core] better jsdoc support PROPERTIES

class MyElement extends HTMLElement {
  /**
   * @type {string} - description
   */
  prop = 'foo';

  constructor() {
    super();
    /** @type {number} - description */ // if duplicate description/type, overwrite the description/type above
    this.prop = 'foo';
  }
}
  • Add docs
  • If jsdoc is applied twice, make the jsdoc in the constructor the override
  • Deal with jsdoc type imports ${import('./module.js').SomeType}. Get type SomeType, add reference to ./module.js
  • merge or override with JSDoc declared above the class

duplicate param

  /**
   * @param {e} Event
   * @returns {void}
   */
  myMethod(e) {}
                {
                  "name": "Event",
                  "type": {
                    "type": "e"
                  }
                },
                {
                  "name": "e"
                }

[core] better JSDoc support

  • Support jsdoc description/summary for everything
  • Support jsdoc for class field, or default setting stuff in the constructor
  • Deal with jsdoc type imports, e.g. {import('foo').SomeType}
/**
 * Size of the text field
 * @attr
 * @type {"small"|"large"}
 */
size = "large";

constructor() {
  super();
  /**
   * Size of the text field
   * @attr
   * @type {"small"|"large"}
   */
  this.size = "large";
}
  static get observedAttributes() {
    return [
    /** some jsdoc */
    "placeholder",
    /** more jsdoc */
    "disabled",
    ];
  }
/**
 * Dispatched when the enter key is pressed
 */
this.dispatchEvent(new CustomEvent('enter'));

[core] better jsdoc support EVENTS

class MyElement extends HTMLElement {
  /**
   * Dispatched when the enter key is pressed
   * @fires {Event} some-event - some description for some-event
   * @event {SomeCustomEvent} foo-changed - some description for foo-changed
   */
  this.dispatchEvent(new CustomEvent('foo-changed'));
}
  • Supports @fires
  • Supports @event
  • Add docs
  • If not Event or CustomEvent, add Reference to the event (it should be in imports or in declarations after the analyze phase)
  • Make sure the description after @event or @fires overwrites the description on top
  • Deal with jsdoc type imports ${import('./module.js').SomeEvent}. Get type SomeEvent, add reference to ./module.js
  • merge or override with JSDoc declared above the class

events end up in manifest multiple times

when adding an event, check if it doesnt already exist

  __open(dispatch) {
    if (dispatch) {
      this.dispatchEvent(
        new CustomEvent('opened-changed', {
          detail: true,
        }),
      );
    }
    this.__button.setAttribute('aria-expanded', 'true');
  }

  __close() {
    this.dispatchEvent(
      new CustomEvent('opened-changed', {
        detail: false,
      }),
    );
    this.__button.setAttribute('aria-expanded', 'false');
  }
{
  "name": "opened-changed",
  "type": {
    "type": "CustomEvent"
  }
},
{
  "name": "opened-changed",
  "type": {
    "type": "CustomEvent"
  }
}

[core] Reduce use of `any`

In some places there are quite a lot of any types used, just to get something working. It'd be nice to reduce the amount of any throughout the code.

Contributions are welcome here, if anybody is interested in contributing to this: Dont worry about the entire codebase, whatever cleanup here is welcome ๐Ÿ™‚

handle destructuring in functionlikes

example:

input

class MyElement extends HTMLElement {
  /**
   * @param {Object} opts
   * @param {string} opts.currency
   */
  _onCurrencyChanged({ currency }) {}
}

output:

              "parameters": [
                {
                  "name": "opts",
                  "type": {
                    "type": "Object"
                  }
                },
                {
                  "name": "opts.currency",
                  "type": {
                    "type": "string"
                  }
                },
                {}
              ]

do something with TS `readonly`

currently readonly is not specified in the schema, and will incorrectly be handled by the analyzer:

private foo: readonly string = '';
"type": {
  "type": "readonly string"
},

Question: Does/could the manifest-to-markdown package allow for the creation of custom sections?

For example, if I wanted to separate out certain properties as special ones, maybe even by adding a special jsdoc-like indicator (like @specialProp or something) how might i go about making a custom section in the produced markdown?

The use case I have is that my web components have what I call "Internal Access properties" that give consumers of the components direct access to certain important DOM elements directly rather than them having to crawl the internal shadowRoot with nested querySelectors. It would be awesome if i could specify some tag on those properties and have them get broken out into their own special category section with a name and table columns I could specify add to, etc.

Crash when analyzing chessboard-element

I was testing the analyzer against https://github.com/justinfagnani/chessboard-element and got this error:

/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/dist/ast/handleCustomElementsDefine.js:24
        elementClass = node.parent.arguments[1].text;
                                            ^

TypeError: Cannot read property '1' of undefined
    at Object.handleCustomElementsDefine (/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/dist/ast/handleCustomElementsDefine.js:24:45)
    at visitNode (/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/dist/create.js:287:46)
    at visitNode (/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/node_modules/typescript/lib/typescript.js:27028:24)
    at Object.forEachChild (/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/node_modules/typescript/lib/typescript.js:27255:21)
    at visitNode (/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/dist/create.js:308:30)
    at visitNode (/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/node_modules/typescript/lib/typescript.js:27028:24)
    at Object.forEachChild (/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/node_modules/typescript/lib/typescript.js:27253:24)
    at visitNode (/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/dist/create.js:308:30)
    at visitNode (/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/node_modules/typescript/lib/typescript.js:27028:24)
    at Object.forEachChild (/Users/justinfagnani/Projects/Personal/chessboardjs/node_modules/@custom-elements-manifest/analyzer/node_modules/typescript/lib/typescript.js:27253:24)

analyzer: Globs in configuration file do not override CLI defaults

There are different results between using CLI globbing custom-elements-manifest analyze --globs="src/*.js" and via config file { globs: ["src/*.js"] }.

The values passed into globby differ here, such that when using the CLI, the glob paths are what is defined, plus some node_modules etc. filtering.

 [
  'src/*.js',
  '!node_modules/**/*.*',
  '!bower_components/**/*.*',
  '!**/*.test.{js,ts}',
  '!**/*.suite.{js,ts}',
  '!**/*.config.{js,ts}'
]

The same defined in a configuration file results in the same set of globs, plus the CLI's default value for globbing:

 [
  '**/*.{js,ts}', // <- these are CLI defaults
  '!**/.*.{js,ts}', // <- these are CLI defaults
  'src/*.js',
  '!node_modules/**/*.*',
  '!bower_components/**/*.*',
  '!**/*.test.{js,ts}',
  '!**/*.suite.{js,ts}',
  '!**/*.config.{js,ts}'
]

If globs are specified, I'd expect the same result regardless if they were via CLI or configuration file, and only use the default glob paths if neither specified.

[core] dont re`visit` entire program in events

Currently in handleEvents we go through the entire source program again with a visitor to find a reference to the used event. We should be able to handle this in the link phase instead, since the used event should either be in the imports or in the declarations for a module.

[core] better slot/css part/css prop support

Currently slots, css parts and css properties are only supported through JSDoc.

We could do a check with dom5/parse5 for any innerHTML or html tagged template literals, do a query select for slot, slot[name] and [part], to automatically find slots, parts, and properties.

[core] better jsdoc support ATTRIBUTES

Fixture:

class MyElement extends HTMLElement {
  static get observedAttributes() {
    return [
    /** some jsdoc */ // only description
    "placeholder",
    /**
     * Disabled the component // description goes here, or after the type. 
     * @type {disabled} 
     * @type {disabled} - Disables the component // If this is present, override the description above
     * @property disabled // adds a `"fieldName": "prop1"` to the attr in the output CE.json
     */
    "disabled",
    ];
  }
}
  • make sure it works for static get observedAttributes() {}
  • make sure it works for static observedAttributes = []
  • make sure the @property related fieldName doesnt clash with the support for the @property jsdoc
  • make sure it supports a @type jsdoc
  • make sure a @type's description overwrites the 'top' description
  • add example in docs
  • Deal with jsdoc type imports ${import('./module.js').SomeEvent}. Get type SomeEvent, add reference to ./module.js
  • merge or override with JSDoc declared above the class

Analyzer - Invalid Syntax Kind

Hey @thepassle!

First off thanks heaps for the hard work on this library. I'm having some slight issues trying to extend the manifest in the analyzePhase within my custom plugin. All the syntax kinds are messed up for some reason, classes are returning as variable declarations, enums as class declarations, types as functions and so on. All the code is available here, you can also see the plugin and what not inside the cem directory (nothing special).

Another issue closely related is I'm trying to get "inherited" documentation on a property or method from an interface but it currently doesn't work. Sometimes it's preferable to write an interface and implement it across different components. Documenting solely the single interface saves energy in avoiding to have to document multiple component props/methods over and over.

Normally the recommended way to get documentation is to retrieve it on a symbol via the TypeChecker. Something like...

// EXAMPLE
const comment = checker.getSymbolAtLocation(identifier)?.getDocumentationComment(checker);
return normalizeLineBreaks(ts.displayPartsToString(comment) ?? '');

This library currently doesn't parse files using createProgram (probably performance reasons to avoid type checks?) and instead uses createSourceFile, unfortunately this loses type information which can be used in the analyzePhase by a custom plugin. In my case this could be used to get better type inference and symbol documentation.

Is this something we can resolve in this library or should I fork?

[core] add simple TS types for variables and fields

For fields and variables we can already add the 'simple' TS type by getting the: node.type.getText()

See: https://astexplorer.net/

Fixture:

class MyElement extends HTMLElement {
  foo: string = ''
}

customElements.define('my-element', MyElement);

Click on foo in the left panel of AST explorer, set compiler to typescript, and you'll see a type property in the PropertyDeclaration that you can call .getText() on

image

  • Make sure the TS type overwrites any jsdoc types that a field or variable might have
  • implement for fields
  • implement for variables

analyzer: Plugins are objects, not functions

via the README, Plugins are defined as functions, including the example:

A plugin is a function that returns an object. There are several hooks you can opt in to:

However, the current implementation defines a Plugin as an object with analyzePhase, and other callbacks. While plugins in npm (and the built in lit and stencil plugins) may expose functions, the plugins array must ultimately be an object.

Happy to send PR with doc changes, but want to confirm the intentions here first.

[analyzer] Stencil plugin

It would be really nice to have Stencil support as a plugin with the new plugin API.

Getting started

  • git clone https://github.com/open-wc/custom-elements-manifest.git
  • cd custom-elements-manifest
  • yarn install
  • cd packages/custom-elements-manifest-analyzer
  • yarn tsc:watch
  • in a different terminal: npm start

Tests

For tests, you can add a new fixture in the fixtures folder, and the tests will automatically run it, and compare the output.json (which is the output generated by the analyzer) to the fixture/custom-elements.json, which is the expected state.

Stencil syntax

  • Extract tagname from the @Component({tag: 'my-foo'}) decorator to add a tagName to a classDoc
    • this is also an export of kind: custom-elements-definition, and should show up in a modules exports
  • Exclude lifecycle methods?
  • Support @Prop() decorator
    • if @Prop(), make sure to add it as an attribute as well
    • if @Prop({reflect: true}), make sure to add it as an attribute as well
    • if @Prop({attribute: 'foo'}), make sure to add it as an attribute as well, with the given attribute name
    • if @Prop() isValid: string; make sure the attr name is is-valid (camelcase to kebab)
    • if @Prop() foo: ComplexType ignore the attribute; only add attributes if the type is a primitive (num, str, bool, etc)
  • Support @Event() decorator
    • if @Event() todoCompleted: EventEmitter<Todo>;, it will fire a todoCompleted event, add that to events
    • if @Event({eventName: 'todoCompleted'}), it will fire a todoCompleted event, add that to events
    • In Stencil, Events are added as class fields; so make sure they get removed from a classDoc's members array, but show up as events instead

Writing a plugin

A plugin is a function that returns an object. There are several hooks you can opt in to:

  • analyzePhase: Runs for each module, and gives access to the current Module's moduleDoc so you can mutate it, and gives access to the AST nodes of your source code.
  • moduleLinkPhase: Runs after a module is done analyzing, all information about your module should now be available. You can use this hook to stitch pieces of information together
  • packageLinkPhase: Runs after all modules are done analyzing, and after post-processing. All information should now be available and linked together.

Most (if not all) of the work for the Stencil plugin will happen in the analyzePhase, which is where you'll have access to a modules AST. ASTExplorer is your friend ๐Ÿ™‚

import ts from 'typescript';

export default {
  plugins: [
    function myPlugin() {
      return {
        // Runs for each module
        analyzePhase({node, moduleDoc}){
          // You can use this phase to access a module's AST nodes and mutate the custom-elements-manifest
          switch (node.kind) {
            case ts.SyntaxKind.ClassDeclaration:
              // AST magic goes here, and you can mutate the `moduleDoc`
              break;
          }
        },
        // Runs for each module, after analyzing, all information about your module should now be available
        moduleLinkPhase({node, moduleDoc}){},
        // Runs after modules have been parsed and after post-processing
        packageLinkPhase(customElementsManifest){},
      }
    }
  ]  
}

Here's a fixture/overview (and ASTExplorer link) of things the plugin should support:

import { Component } from '@stencil/core';

// TodoList should have `todo-list` as `tagName` in the output custom-elements.json
// This is also an export of kind: "custom-elements-definition"
@Component({
  tag: 'todo-list'
})
export class TodoList {
  // shows up as attr
  @Prop() color: string;
  // shows up as `is-valid` attr (camelcase to kebab)
  @Prop() isValid: boolean;
  // doesnt show up as attr! (complex type)
  @Prop() controller: MyController;
  // shows up as attr `valid`
  @Prop({ attribute: 'valid' }) isValid: boolean;
  // shows up as attr `message` (probably doesnt even need special handling, but just incase)
  @Prop({ reflect: true }) message = 'Hello';

  // shows up as event `todoCompleted`
  // `todoCompleted` should not be present in the class's members array
  @Event() todoCompleted: EventEmitter<Todo>;
  
  // shows up as event `foo`
  @Event({
    eventName: 'foo',
  }) fooEvent: EventEmitter<Todo>;

  // these should not show up in custom-elements.json
  componentWillLoad(){}
  componentDidLoad(){}
  componentShouldUpdate(){}
  componentWillRender(){}
  componentDidRender(){}
  componentWillUpdate(){}
  componentDidUpdate(){}
}

[core] Support `@prop` jsdoc

Support @prop and @property jsdoc, e.g.:

/**
 * @prop {boolean} bar - description
 * @property {boolean} bar - description
 */
class Foo extends HTMLElement {}
  • implement
  • add to docs

Possible to extract custom typedocs

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

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

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

i could imagine somthing like this.

class MyElement extend HTMLElement {

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

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

could be something like this

export default {
  extractAdditionalJsDocs: ['editvia']
}

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

what do you think?

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.