Code Monkey home page Code Monkey logo

custom-elements-manifest's Introduction

custom-elements-manifest

A file format for describing custom elements.

The schema is published as a JSON Schema file, in schema.json. The schema is written in TypeScript (see schema.d.ts) and then compiled to JSON Schema.

Usage

Install:

npm i -D custom-elements-manifest

Require the JSON Schema:

const customElementManifestSchema = require('custom-elements-manifest');

Import the TypeScript types:

import * as schema from 'custom-elements-manifest/schema';

Referencing manifests from npm packages

In order to allow tools to find npm packages with custom element manifests without having to download package tarballs, packages should have a "customElements" field in their package.json that points to the manifest:

{
  "name": "example-package",
  "customElements": "custom-elements.json",
}

Schema Versioning

The schema has a schemaVersion field in the top-level object to facilitate evolution of the schema. The schema follows semver versioning, the current schema version is 2.1.0.

This version may not always match the npm package version, as some changes to the npm package might not have changes to the schema.

Schema Version Date npm Version git Tag
2.1.0 2024-05-06 2.1.0 v2.2.0
2.0.0 2022-09-13 2.0.0 v2.0.0

Example

Given the following source code in directory my-project:

my-project/my-element.js:

/**
 * This is the description of the class
 */
export class MyElement extends HTMLElement {
  static get observedAttributes() {
    return ['disabled'];
  }

  set disabled(val) {
    this.__disabled = val;
  }
  get disabled() {
    return this.__disabled;
  }

  fire() {
    this.dispatchEvent(new Event('disabled-changed'));
  }
}

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

The manifest would look like:

my-project/custom-elements.json:

{
  "schemaVersion": "2.1.0",
  "readme": "README.md",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "my-project/my-element.js",
      "declarations": [
        {
          "kind": "class",
          "customElement": true,
          "name": "MyElement",
          "tagName": "my-element",
          "description": "This is the description of the class",
          "members": [
            {
              "kind": "field",
              "name": "disabled"
            },
            {
              "kind": "method",
              "name": "fire"
            }
          ],
          "events": [
            {
              "name": "disabled-changed",
              "type": {
                "text": "Event"
              }
            }
          ],
          "attributes": [
            {
              "name": "disabled"
            }
          ],
          "superclass": {
            "name": "HTMLElement"
          }
        }
      ],
      "exports": [
        {
          "kind": "js",
          "name": "MyElement",
          "declaration": {
            "name": "MyElement"
          }
        },
        {
          "kind": "custom-element-definition",
          "name": "my-element",
          "declaration": {
            "name": "MyElement"
          }
        }
      ]
    }
  ]
}

Motivation

Many tools need some machine-readable descriptions of custom elements: IDEs, documentation viewers, linters, graphical design tools, etc.

There have been several efforts in this area, including:

This repository is an effort to bring together tool owners to standardize on a common specification for a description format.

Use Cases

Editor Support

Developers using custom elements should be able to get full-featured IDE support including auto-completion, hover-documentation, unknown symbol warnings, etc. These features should be available in HTML files, and in various template syntaxes via template-specific tools.

Documentation and demos

Documentation viewers should be able to display all the relevant information about a custom element, such as its tag name, attributes, properties, definition module, CSS variables and parts, etc.

Using a custom-elements manifest, it would be easy to generate or display demos for your component using tools such as api-viewer-element, or automatically generate Storybook knobs for your components.

Linting

Linters should be able to produce warnings based on custom element defintions, such as warning if unknown elements are used in HTML templates.

Framework Integration

React currently is the only major framework where custom elements require some special handling. React will pass all data to a custom element in the form of HTML attributes, and cannot listen for DOM events coming from Custom Elements without the use of a workaround.

The solution for this is to create a wrapper React component that handles these things. Using a custom elements manifest, creation of these wrapper components could be automated.

Some component libraries like Fast or Shoelace provide specific instructions on how to integrate with certain frameworks. Automating this integration layer could make development easier for both authors of component libraries, but also for consumers of libraries.

Cataloging

A major use-case of custom elements manifests is that they allow us to reliably detect NPM packages that for certain contain custom elements. These packages could be stored, and displayed on a custom elements catalog, effectively a potential reboot of webcomponents.org. This catalog would be able to show rich demos and documentation of the custom elements contained in a package, by importing its components from a CDN like unpkg, and its custom elements manifest.

Testing

Tooling would be able to detect whether or not the public API of a custom element has changed, based on a snapshot of the current custom elements manifest file to decide the impact of an update, and potentially prevent breaking API change in patch versions.

custom-elements-manifest's People

Contributors

bennypowers avatar jogibear9988 avatar jorgecasar avatar justinfagnani avatar lukewarlow avatar mikematusz avatar octref avatar theolavaux avatar thepassle 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  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

custom-elements-manifest's Issues

Starting point

Given that some tools already support reading and writing the custom-elements.json format, should we take that as a starting point?

On the other hand, @octref has suggested starting from the VS Code Custom Data Format. Whether the format should be based on one IDEs format is a great question to answer.

Add a field for "elementVersion"?

I think it would be helpful to specify a version for the custom element. For example, if I add a few major properties/events to an element, I should be able to say that the element is now version 2.0.0

What to do with "demo"?

Demo is an interesting bit of the schema. Right now it's only referenced from CustomElement#demos, but really, anything could have an associated demo, including or especially the package as a whole.

We could add a demos field to all declarations, modules and package. We could also add an extensible metadata field to just about everything and make DemoMetadata well-known object that can appear there.

I do not understand this ?!

Out of my point of view, I am only a ECMAScript Implamentator so I write interpreters and parsers and compilers Runtimes VM's such stuff. The Objects them self are the description of the component? It is Machine and Human Readable.

Sure it's big but that is how it works and that is what it is that's why it is what it is.

image text

Add support for icons

Catalogs and GUI builders would benefit from being able to show a custom icon. This could go in metadata. We should probably look for inspiration from PWA app manifests.

Support non-self-define custom-elements

Why?

I know this is controversial, but I'm not entirely confortable about only self-define custom-element.
There is so much opportunity for tag collision...

I know there are other issues if we don't use self-define components but it will ultimately be resolved by Scoped Registries.

Additionally, I can see a number of use cases where it would make sense to control programmatically the tag name and the discovery of the class in a module. I would definitely need it in my project for example.

Cases

If custom-elements.json has a tagName then it was self define by the module.
If custom-elements.json has no tagName you need to define.

  • option 1: The module expose the class as a named export.
  • option 2: The module expose the class as export default.

Proposal

exportName = default | xxxxxxx in the schema
(xxxxxxx being the exported name of the class)

Consider a new name

"custom-elements.json" is a filename and not necessarily a description of the file's contents or purpose. The file specific name also might not be required to be custom-elements.json. On npm it could be referenced from a package.json field, like the "types" field. In other systems, like databases, the name might not matter or even exist.

A few options to consider:

  • Custom elements manifest
  • Custom elements docs file
  • Package docs file

Considering that the format will be capable of pretty fully describing package contents, being a "package docs file" might make sense. There very well might be other communities interested in shared docs infrastructure, even if that isn't a primary goal right now.

Do we really want to create yet another suite of typing tools?

I would really like to have custom element attribute etc. type checking and I'm also really happy about the work you've put into this already, but..

Isn't TypeScript (and/or another typing tool like Flow) in a better position to support all of this? It already supports analyzing, declaring and checking types when it comes to classes, properties and methods etc. Specific support for custom elements including attributes, events, styles etc. and html parsing would have to be added. But in that case it could be used both in full fledged TypeScript projects during compilation and in JavaScript projects that only use the type checking part of their tools.

I don't want to force TypeScript on users. I'm definitely also open to using other existing tooling like Babel or Flow. But it seems that right now"types": "index.d.ts" is being published by most NPM packages; wouldn't it be great if that could just include all of this and we wouldn't have to install additional analyzing and linting tooling for our IDE's and CI?

The main issue I do recognize with this is that it will probably be difficult and take a lot of time to get support from the TypeScript team for this and/or contribute to it ourselves there. Still, even if we continue to work on solutions like this I think it might be interesting to simultaneously push and work towards what we think is the best final solution as well.

PS: Sorry if this is not the place to discuss this. But it seems like the related discussion is centering around here, so I've taken the liberty to create this issue.

Remove schema.json

Once we publish to npm and host the schema at a stable URL, we should remove schema.json from the repo as it's a generated artifact.

Add a meta tag to indicate custom elements manifest location on webpage

Documented here: #70

As in some situations, the tooling might not have access to a package.json, it would be beneficial to be able to indicate the location of the custom elements manifest in the html page. A meta tag should be enough for this.

This came up in a discussion with @claviska (https://github.com/claviska), when discussing CEM and DevTools support with the shoelace documentation site.

This would allow documentation sites to indicate the location of the CEM through a meta tag, allowing the devtools to find the manifest for an enhanced experience on the docs page.

Multiple defaults for CSS Custom Properties

Consider this case

class MyElement extends HTMLElement {
  constructor() {
    super().attachShadow({ mode: 'open' }).innerHTML = `
      <style>
        :host { color: var(--my-color, red); }
        @media (prefers-color-scheme: dark) {
          :host { color: var(--my-color, blue); }
        }
      </style>
    `;
  }
}

Should I get

"cssProperties": [
  {
    "name": "--my-color",
    "default": "red"
  }
]

Or

"cssProperties": [
  {
    "name": "--my-color",
    "default": "blue"
  }
]

And if I get either, that's not really correct is it?

We should add a schema facility for declaring multiple defaults and their conditions

Add recommended JSDoc tags

Many analysis tools will use JSDoc tags to determine what web component specific features. To keep from fragmenting the ecosystem around different sets of JSDoc, we should make a recommended set of tags.

Tags will need to document the features in the CustomElement schema kind:

  • Tag Name
  • Attributes
  • Events
  • Slots
  • CSS Shadow Parts
  • CSS Custom Properties
  • Demos

Samples

@justinfagnani
are there any sample using this manifest?
I thought maybe lit already has one, but I could not find it.
What's the state of this?
What's the sate of rebooting custom elements catalogue?

Standardize a npm package.json field

We should standardize on a package.json field to use to point to a custom elements manifest. This is important for indexers and catalogs that might need to inspect npm package for custom elements. The npm registry API allows callers to fetch the package.json metadata without fetching and unpacking the package's tarball.

Duplicate attributes when annotating

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

Specification format

We probably at least want a JSON Schema file describing the spec, at least to validate tool output and to help generate file readers.

JSON Schema isn't the most human readable format though, do we want another format to develop the spec in and present in the main README?

Add summary to method and function return types

Parameter and other types have a markdown summary field which our user could populate with a table to represent a property bag.

It would be helpful to include that summary on the return type of functions and methods so that they can document the properties of return types.

This is an easy escape hatch for many typings-related issues and could be a good way to "thread the needle" of flexibility vs API pollution.

Property/Attribute Lifecycle description

@justinfagnani
I've some Webcomponents wich only use the Attrbiutes Values, when they are set at object creation. They do not look for attribute/property changes.
Is this something we should describe also?
So a designer knows, do I need to change they attribute, the property or do I need to recreate the whole element.

Add support for custom data

Salesforce is quite interested in capitalizing on a manifest like this for a range of projects. I believe the proposed state of schema.ts already covers the majority of Salesforce's requirements. The remaining requirements are rather proprietary, and could be addressed with general-purpose mechanism for including custom data in a manifest.

This could be achieved by:

  1. Adding a standard, unrestricted custom key to the top-level schema.
  2. Recommending a strategy for component authors or tool vendors to further subdivide that custom key. Example: an organization adding custom data should do so inside a key containing a relevant domain name they own.
  3. Encouraging tools to either ignore custom keys they don't understand, or to perform generic processing of anything they find there. Type-checkers could potentially ignore all that data. A doc tool could either skip the custom data, or format it as a pretty-printed JSON block or table as supplementary documentation.

Example: Using some hypothetical custom data values taken from Salesforce's own internal requirements, a Salesforce component might include the following in its manifest:

{
  "custom": {
    "salesforce.com": {
      "apiVersion": "53", /* Salesforce platform API version targeted by this component */
      "formFactor": "mobile", /* Component is designed primarily for mobile use */
      "capabilities": [ /* In which product environments is the component capable of being used? */
        "dashboard",
        "communities"
      ]
    }
  }
}

Something like this would minimize the impact on the schema design, give component creators a way to include component metadata in a single place, and increase the chance that a tool created by one group could be used by a different group without choking on unexpected custom data. This would also provide a means of trying out new ideas for schema fields before standardizing on them.

How to handle types

When documenting a field of a custom element, we definitely want to describe it's type. Before TypeScript came along we almost would have certainly would have use JSDoc-style type annotation syntax, but TypeScript's type syntax might be more commonly known by now though.

So we could use that in the descriptor, for example:

{
  "members": {
    "foo": {
      "kind": "property",
      "type": "Array<number|string>"
    }
  }
}

But at the limit you need to support many TS-specific, non-concrete entries like interfaces and type aliases so that you can use them in types. Does this mean we should leave types completely out of the format and up to .d.ts files? That would degrade the usefulness for documentation tools that don't process type declaration files. TS declaration files are also harder to parse than JSON, requiring the whole TypeScript compiler.

Another option is to include much of the type-related information parsed by Microsoft's API Extractor: https://github.com/microsoft/rushstack/tree/master/apps/api-extractor

Since this tool produces JSON, it would be relatively easy for custom-elements.json generators to use it to parse TypeScript, then merge with it's own custom element specific parsed metadata.

Add explainer doc

Beyond goals and motivations as the readme currently has, we need to explain the schema and rationale for some of the decisions.

deprecated flag

It would be useful to flag members as deprecated

"members": [
  {
    "kind": "method",
    "name":  "dismiss",
    "deprecated": "use `close()`"
  }
]

deprecated could be a boolean or a string, and could apply to almost all the types in schema.d.ts. certainly to ClassLike, FunctionLike, and ClassMember.

Consuming Tools could use this to warn the user about this member, like typescript lang server does for @deprecated jsdoc tag

Goals and non-goals

It will be useful to discuss and record specific goals and non-goals.

Two examples:

  • Many view libraries allow you to create components that they roughly call "web components" that aren't actual W3C custom elements. Some of these can be used in HTML-like syntaxes, like Angular and Vue templates. Should it be a goal or non-goal to describe these components too?
  • Custom elements are JavaScript objects, and their descriptions are a superset of JavaScript. Should it be a goal or non-goal of this description format to completely document the JavaScript-side of the component interface?

Using export map instead of custom property in `package.json`

currently, it says

Referencing manifests from npm packages

In order to allow tools to find npm packages with custom element manifests without having to download package tarballs, > packages should have a "customElements" field in their package.json that points to the manifest:

{
  "name": "example-package",
  "customElements": "custom-elements.json",
}

but if you are using an export map

"name": "my-package",
"customElements": "custom-elements.json",
"exports": {
  ".": "./index.js"
}

then the files can not be imported... e.g. this will fail

import cem from 'my-package/custom-elements.json' assert { type: 'json' };

Suggestion

Replace the custom property with an entry in the export map.

Benefits:

  • resolving is depending on an import map or the node resolution system
  • file is always accessible as it's part of the export map
  • not 2 different ways of defining an CEM
- "customElements": "custom-elements.json",
+ "exports": {
+  "customElements": "custom-elements.json",
+  ".": "./index.js"
+ }

Add readonly flag to properties

It would be helpful to mark a class field or property as readonly (i.e. getter only or defined as writable: false)

Screenshot_2020-12-17 Components apollo-client Apollo Elements

{
  "name": "elements",
  "type": "readonly ApolloElementElement[]",
  "readonly": true,
  "attribute": false,
  "summary": "List of all ApolloElements registered to this client."
},

Spread parameters

As far as I could tell there's no schema to identify a spread parameter:

export interface Parameter extends PropertyLike {
/**
* Whether the parameter is optional. Undefined implies non-optional.
*/
optional?: boolean;
}

export interface PropertyLike {
name: string;
/**
* A markdown summary suitable for display in a listing.
*/
summary?: string;
/**
* A markdown description of the field.
*/
description?: string;
type?: Type;
default?: string;
}

This could be useful for class methods or functions that take a spread:

  protected notify(...keys: (keyof this)[]): void {
    this[update](Object.fromEntries(keys.map(x => [x, this[x]])));
  }

Allowed children for slots

Both standard components (e.g. table, tr, td, dl, dd) and web components (e.g. tab-panels, tab-panel) have implicit constraints on what elements are allowed as children, and also what are allowed as parents.

I would propose that a slot description should also include a way to codify this.

For example:

export interface Slot {
  /**
   * The slot name, or the empty string for an unnamed slot.
   */
  name: string;

  /**
   * A markdown summary suitable for display in a listing.
   */
  summary?: string;

  /**
   * A markdown description.
   */
  description?: string;

  /**
  * A set of CSS selectors for allowable children
  */
  allowedChildren?: string[];
}

Add support for typed Custom CSS Properties

Summary

Today, there's no way to provide type-hints to consumers about a CSS custom property (e.g. '<color>', '<length>', etc.) This requires component authors to have very explicit property names (--my-foo-color) or document the property adequately in the description. Unfortunately, neither is suitable for IDE intellisense support.

Chrome has shipped the CSS @property rule and enabled by default as of 85, and the CSS Properties and Values API as of 78.

Proposal

Add a "type" property to the Custom CSS Property schema. Manifest consumers can treat the omission of this value as '*', which I believe is the default behavior of Custom CSS Properties.

We can additionally strongly type the TypeScript declaration schema with all the legal string values

Support for Inheritence/Mixin state

Consider the following code:

class DisabledElement extends LitElement {}
class MyInput extends FieldMixin(DisabledElement) {}

how would we save this nicely to the json?

strawman proposal:

{
  "tags": [
    {
      "name": "my-input",
      "inheritance": "FieldMixin -> DisabledElement -> LitElement",
      "properties": [
        {
          "name": "myInputState",
          "inheritance": "this"
        },
        {
          "name": "value",
          "inheritance": "FieldMixin"
        },
        {
          "name": "disable",
          "inheritance": "DisabledElement"
        }
      ]
    }
  ]
}

as this is probably not that simple to analyze? I thought we could postpone it? but yeah if anyone things that's easily doable by an analyzer and it totally make sense like this we could move forward with it?

Use cases

I listed some initial use cases in the README. If there are additional use cases that should be added, propose them here.

Support non-JS module types

We will hopefully soon have non-JS module types that packages may want to advertise, and that may be the target for references. CSS custom properties may be defined in a CSS module rather than a JS module.

I don't think we should spend too much time defining the schema for non-JS modules now, but we should probably allow for different module types by encoding a kind discriminator.

export interface PackageDoc {
  version: string;

  /**
   * An array of the modules this package contains.
   */
  modules: Array<ModuleDoc>;
}

type ModuleDoc = JSModuleDoc | CSSModuleDoc;

export interface JSModuleDoc {
  kind: 'js';
  path: string;

  /**
   * A markdown summary suitable for display in a listing.
   */
  summary?: string;

  /**
   * A markdown description of the module.
   */
  description?: string;

  exports?: Array<ExportDoc>;
}

No mention of `formAssociated` in schema

I've added

    static get formAssociated() {
        return true
    }

to a custom element and run the script to generate the manifest. There doesn't seem to be any mention in the manifest that the control is associated to a form. This could be useful in a component directory if someone's looking specifically for controls.

I also saw there was no mention of this anywhere in the custom elements schema definition.

Add InterfaceDeclaration

APIs need a way to describe object interfaces in a way that documentation can describe. ClassDeclaration has most of what we need, but includes some fields not suitable for interfaces.

Hint on `Type` for type of type (e.g. 'typescript', 'flow')

Screen Shot 2021-07-28 at 9 14 12

coptic: Any interest in improving the controls? For example, size here has three options, so a radio is a little nicer than having to type in something IMO. If there’s 3+ it could default to a select maybe?

bennyp: This could maybe be automated from the type field in a custom elements manifest, however, we'd have to assume the type text is typescript in order to parse the union, where the manifest scema makes no such assumption

coptic: Ya that’s what I was playing around with. That’s a good point though not assuming TS. FWIW, stencils doc schema and generator is nice in that it does parse any union into a values array.

tl;dr: tools can't know for certain what kind of string the type text is. If we added a hint (per file, or per type), they could parse the type to do things like generate radio buttons for demos

per type:

{
  "schemaVersion": "1.0.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "my-element.js",
      "declarations": [
        {
          "kind": "class",
          "name": "MyElement",
          "members": [
            {
              "kind": "field",
              "name": "type",
              "type": {
                "text": "'number'|'string'|'boolean'",
                "kind": "typescript"
              }
            }
          ]
        }
      ]
    }
  ]
}

per file

{
  "schemaVersion": "1.0.0",
  "readme": "",
  "modules": [
    {
      "kind": "javascript-module",
      "path": "my-element.js",
      "types": "typescript",
      "declarations": [
        {
          "kind": "class",
          "name": "MyElement",
          "members": [
            {
              "kind": "field",
              "name": "type",
              "type": {
                "text": "'number'|'string'|'boolean'",
              }
            }
          ]
        }
      ]
    }
  ]
}

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

I originally opened this issue on the custom-elements-manifest analyzer (open-wc/custom-elements-manifest#43), but I'm told this is a bug in the spec, so reopening here.

In the custom-elements-manifest analyzer playground, 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

I would expect the output schema to match the one from schema.d.ts.

Requirements

What do we need to capture in the description file?

Links to source code

add optional property that links members to source code

use case: docs sites that link element properties, etc to github source pages.

source code references are strings, could either be relative paths from package root or fully qualified URLs to e.g. github

add optional to ClassMember

class fields may be optional in implementations, likewise methods

class Base extends CanErrorElement {
  context?: any;

  onError?(error: Error, context?: any): void;

  thing() {
    try {
      that()
    } catch(e) {
      this.onError?.(e, context);
    }
}

Add async flag to methods and functions

While subsequent tooling could infer this from the return type, @justinfagnani rightly pointed out that there are some semantic differences, notably for error types, and perhaps for early returns too.

API tables would benefit from this flag as well:

Screenshot_2020-12-17 Interfaces ApolloMutation Apollo Elements

{
  "name": "mutate",
  "async": true,
  "summary": "This resolves a single mutation according to the options specified and returns a Promise which is either resolved with the resulting data or rejected with an error.",
  "parameters": [{
    "name": "params",
    "optional": true,
    "type": "Partial<MutationOptions<Data<D>, Variables<D, V>>>",
    "summary": "All named arguments to mutate default to the element's corresponding instance property. So you can call `element.mutate()` without arguments and it will call using `element.mutation`, `element.variables`, etc. You can likewise override instance properties per-call by passing them in, e.g.\n\n```ts\nawait element.mutate({\n  fetchPolicy: 'network-only'\n  variables: {\n    ...element.variables,\n    name: 'overridden',\n  },\n});\n```\n\n| Property | Description | Type |\n| -------- | ----------- | ---- |\n| `awaitRefetchQueries` | See [awaitRefetchQueries](#awaitrefetchqueries) | `ApolloMutation#awaitRefetchQueries` |\n| `context` | See [context](../element/#context) | `Record<string, unknown>` |\n| `errorPolicy` | See [errorPolicy](../element/#errorpolicy) | `ErrorPolicy` |\n| `fetchPolicy` | See [fetchPolicy](#errorpolicy) | `FetchPolicy` |\n| `mutation` | See [mutation](#mutation) | `ApolloMutation#mutation`\n| `optimisticResponse` | See [optimisticResponse](#optimisticresponse) | `ApolloMutation#optimisticResponse`\n| `refetchQueries` | See [refetchQueries](#refetchqueries) | `ApolloMutation#refetchQueries`\n| `update` | See [updater](#updater) | `ApolloMutation#updater`\n| `variables` | See [variables](#variables) | `ApolloMutation#variables`\n"
  }],
  "return": {
    "type": "Promise<FetchResult<Data<D>>>",
    "summary": "A `FetchResult` object containing the result of the mutation and some metadata\n\n| Property | Description | Type |\n| -------- | ----------- | ---- |\n| `data` | The result of a successful execution of the mutation | `Data<D>` |\n| `errors` | included when any errors occurred as a non-empty array | `readonly GraphQLError[]` |\n| `extensions` | Reserved for adding non-standard properties | `ApolloMutation#awaitRefetchQueries` |\n| `context` | See [context](../element/#context) | `Record<string, unknown>` |\n"
  }
}

Provide a field with the actual class name of a default exported class

As per the spec, (https://github.com/webcomponents/custom-elements-manifest/blob/master/schema.d.ts#L85-L96), a default-exported class gets named "default". However in the Schema's Reference -type, used by the "superclass" property for example (https://github.com/webcomponents/custom-elements-manifest/blob/master/schema.d.ts#L149-L153), a "name" field is provided, and is always populated by the actual class name of the Javascript class.

When creating tooling, it would be useful to be able to look at the declarations of the superclass, but this is impossible with the current schema as if the class is default exported, the name is not found anywhere on the class' schema.

Proposed solution

Add a "className" or similiar field to the JavaScriptExport type. This would always be populated with the actual classname of the class, instead of "default" in default exported classes

Create a reference library to easily query custom elements info

We want to avoid duplication of data in the schema, but we also want to allow for easily querying a schema, or related collection of schemas.

Tools will want to answer questions like:

  • What are all the elements in this package?
  • Build an index from tag name to class, or class to tag name
  • What is the entire class hierarchy for class A?
  • Find all references to a symbol

We can make this much easier by building a shared library that given a set of package docs builds a queryable model from them. It should not live in this repository, but the people here would be interested in it.

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.