@custom-elements-manifest/helpers
Version:
> ⚠️ Very experimental and very unfinished
1,213 lines (1,110 loc) • 27.3 kB
Markdown
# @custom-elements-json/helpers
> ⚠️ Very experimental and very unfinished
Custom-elements.json is a file format that describes custom elements. This format will allow tooling and IDEs to give rich information about the custom elements in a given project. It is, however, very experimental and things are subject to change. Follow the discussion [here](https://github.com/webcomponents/custom-elements-json).
This library aims to ship some helpers to ease working with the custom-elements.json format.
## Usage
```js
// Node
import { CustomElementsJson } from '@custom-elements-json/helpers';
const foo = JSON.parse(fs.readFileSync('./custom-elements.json', 'utf-8'));
const customElementsJson = new CustomElementsJson(json);
// Browser
import { CustomElementsJson } from 'https://unpkg.com/@custom-elements-json/helpers/dist/esm/index.js';
const json = await (await fetch('./custom-elements.json')).json();
const customElementsJson = new CustomElementsJson(json);
```
### Methods
<hr>
#### `getByTagName`
Returns all information for element `<my-element>`.
```js
customElementsJson.getByTagName('my-element');
```
<details>
<summary><code>custom-elements.json</code></summary>
```json
{
"version": "experimental",
"modules": [
{
"path": "./src/MyMixinB.js",
"exports": [
{
"kind": "variable",
"name": "MyMixinB",
"text": "(klass: any) => typeof MyMixinB"
}
]
},
{
"path": "./src/MySuperClass.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "MySuperClass",
"mixins": [
{
"name": "MyMixinB",
"module": "./src/MyMixinB.js"
}
],
"members": [
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\""
}
]
}
]
},
{
"path": "./src/AnotherSuperClass.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
},
"name": "AnotherSuperClass",
"members": [
{
"kind": "field",
"name": "label",
"type": "string",
"default": "\"d\""
},
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\"",
"inheritedFrom": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
}
}
]
}
]
},
{
"path": "./src/my-element.js",
"exports": [
{
"kind": "definition",
"name": "my-element",
"declaration": {
"name": "MyElement",
"module": "./src/my-element.js"
}
},
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "MyElement",
"members": [
{
"kind": "field",
"name": "foo",
"type": "string",
"default": "\"bar\""
}
],
"tagName": "my-element"
}
]
},
{
"path": "./src/another-component.js",
"exports": [
{
"kind": "definition",
"name": "another-component",
"declaration": {
"name": "AnotherComponent",
"module": "./src/another-component.js"
}
},
{
"kind": "class",
"superclass": {
"name": "AnotherSuperClass",
"module": "./src/AnotherSuperClass.js"
},
"name": "AnotherComponent",
"members": [
{
"kind": "field",
"name": "baz",
"type": "string",
"default": "\"bar\""
},
{
"kind": "field",
"name": "label",
"type": "string",
"default": "\"d\"",
"inheritedFrom": {
"name": "AnotherSuperClass",
"module": "./src/AnotherSuperClass.js"
}
},
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\"",
"inheritedFrom": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
}
}
],
"tagName": "another-component"
}
]
}
]
}
```
</details>
<details>
<summary>Result</summary>
```js
{
kind: 'class',
superclass: { name: 'HTMLElement' },
name: 'MyElement',
members: [ { kind: 'field', name: 'foo', type: 'string', default: '"bar"' } ],
tagName: 'my-element'
}
```
</details>
<hr>
#### `getByClassName`
Returns all information for class `MyElement`.
```js
customElementsJson.getByClassName('MyElement');
```
<details>
<summary><code>custom-elements.json</code></summary>
```json
{
"version": "experimental",
"modules": [
{
"path": "./src/MyMixinB.js",
"exports": [
{
"kind": "variable",
"name": "MyMixinB",
"type": "(klass: any) => typeof MyMixinB"
}
]
},
{
"path": "./src/MySuperClass.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "MySuperClass",
"mixins": [
{
"name": "MyMixinB",
"module": "./src/MyMixinB.js"
}
],
"members": [
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\""
}
]
}
]
},
{
"path": "./src/AnotherSuperClass.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
},
"name": "AnotherSuperClass",
"members": [
{
"kind": "field",
"name": "label",
"type": "string",
"default": "\"d\""
},
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\"",
"inheritedFrom": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
}
}
]
}
]
},
{
"path": "./src/my-element.js",
"exports": [
{
"kind": "definition",
"name": "my-element",
"declaration": {
"name": "MyElement",
"module": "./src/my-element.js"
}
},
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "MyElement",
"members": [
{
"kind": "field",
"name": "foo",
"type": "string",
"default": "\"bar\""
}
],
"tagName": "my-element"
}
]
},
{
"path": "./src/another-component.js",
"exports": [
{
"kind": "definition",
"name": "another-component",
"declaration": {
"name": "AnotherComponent",
"module": "./src/another-component.js"
}
},
{
"kind": "class",
"superclass": {
"name": "AnotherSuperClass",
"module": "./src/AnotherSuperClass.js"
},
"name": "AnotherComponent",
"members": [
{
"kind": "field",
"name": "baz",
"type": "string",
"default": "\"bar\""
},
{
"kind": "field",
"name": "label",
"type": "string",
"default": "\"d\"",
"inheritedFrom": {
"name": "AnotherSuperClass",
"module": "./src/AnotherSuperClass.js"
}
},
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\"",
"inheritedFrom": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
}
}
],
"tagName": "another-component"
}
]
}
]
}
```
</details>
<details>
<summary>Result</summary>
```js
{
kind: 'class',
superclass: { name: 'HTMLElement' },
name: 'MyElement',
members: [ { kind: 'field', name: 'foo', type: 'string', default: '"bar"' } ],
tagName: 'my-element'
}
```
</details>
<hr>
#### `getClasses`
Returns all classes in a `custom-elements.json`
```js
customElementsJson.getClasses();
```
<details>
<summary><code>custom-elements.json</code></summary>
```json
{
"version": "experimental",
"modules": [
{
"path": "./src/MyMixinB.js",
"exports": [
{
"kind": "variable",
"name": "MyMixinB",
"type": "(klass: any) => typeof MyMixinB"
}
]
},
{
"path": "./src/MySuperClass.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "MySuperClass",
"mixins": [
{
"name": "MyMixinB",
"module": "./src/MyMixinB.js"
}
],
"members": [
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\""
}
]
}
]
},
{
"path": "./src/AnotherSuperClass.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
},
"name": "AnotherSuperClass",
"members": [
{
"kind": "field",
"name": "label",
"type": "string",
"default": "\"d\""
},
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\"",
"inheritedFrom": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
}
}
]
}
]
},
{
"path": "./src/my-element.js",
"exports": [
{
"kind": "definition",
"name": "my-element",
"declaration": {
"name": "MyElement",
"module": "./src/my-element.js"
}
},
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "MyElement",
"members": [
{
"kind": "field",
"name": "foo",
"type": "string",
"default": "\"bar\""
}
],
"tagName": "my-element"
}
]
},
{
"path": "./src/another-component.js",
"exports": [
{
"kind": "definition",
"name": "another-component",
"declaration": {
"name": "AnotherComponent",
"module": "./src/another-component.js"
}
},
{
"kind": "class",
"superclass": {
"name": "AnotherSuperClass",
"module": "./src/AnotherSuperClass.js"
},
"name": "AnotherComponent",
"members": [
{
"kind": "field",
"name": "baz",
"type": "string",
"default": "\"bar\""
},
{
"kind": "field",
"name": "label",
"type": "string",
"default": "\"d\"",
"inheritedFrom": {
"name": "AnotherSuperClass",
"module": "./src/AnotherSuperClass.js"
}
},
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\"",
"inheritedFrom": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
}
}
],
"tagName": "another-component"
}
]
}
]
}
```
</details>
<details>
<summary>Result</summary>
```js
[
{
kind: 'class',
superclass: { name: 'HTMLElement' },
name: 'MySuperClass',
mixins: [[Object]],
members: [[Object]],
},
{
kind: 'class',
superclass: { name: 'MySuperClass', module: './src/MySuperClass.js' },
name: 'AnotherSuperClass',
members: [[Object], [Object]],
},
{
kind: 'class',
superclass: { name: 'HTMLElement' },
name: 'MyElement',
members: [[Object]],
tagName: 'my-element',
},
{
kind: 'class',
superclass: { name: 'AnotherSuperClass', module: './src/AnotherSuperClass.js' },
name: 'AnotherComponent',
members: [[Object], [Object], [Object]],
tagName: 'another-component',
},
];
```
</details>
<hr>
#### `getMixins`
Returns all mixins in a `custom-elements.json`
```js
customElementsJson.getMixins();
```
<details>
<summary><code>custom-elements.json</code></summary>
```json
{
"version": "experimental",
"modules": [
{
"path": "./src/MyMixinB.js",
"exports": [
{
"kind": "variable",
"name": "MyMixinB",
"type": "(klass: any) => typeof MyMixinB"
}
]
},
{
"path": "./src/MySuperClass.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "MySuperClass",
"mixins": [
{
"name": "MyMixinB",
"module": "./src/MyMixinB.js"
}
],
"members": [
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\""
}
]
}
]
},
{
"path": "./src/AnotherSuperClass.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
},
"name": "AnotherSuperClass",
"members": [
{
"kind": "field",
"name": "label",
"type": "string",
"default": "\"d\""
},
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\"",
"inheritedFrom": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
}
}
]
}
]
},
{
"path": "./src/my-element.js",
"exports": [
{
"kind": "definition",
"name": "my-element",
"declaration": {
"name": "MyElement",
"module": "./src/my-element.js"
}
},
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "MyElement",
"members": [
{
"kind": "field",
"name": "foo",
"type": "string",
"default": "\"bar\""
}
],
"tagName": "my-element"
}
]
},
{
"path": "./src/another-component.js",
"exports": [
{
"kind": "definition",
"name": "another-component",
"declaration": {
"name": "AnotherComponent",
"module": "./src/another-component.js"
}
},
{
"kind": "class",
"superclass": {
"name": "AnotherSuperClass",
"module": "./src/AnotherSuperClass.js"
},
"name": "AnotherComponent",
"members": [
{
"kind": "field",
"name": "baz",
"type": "string",
"default": "\"bar\""
},
{
"kind": "field",
"name": "label",
"type": "string",
"default": "\"d\"",
"inheritedFrom": {
"name": "AnotherSuperClass",
"module": "./src/AnotherSuperClass.js"
}
},
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\"",
"inheritedFrom": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
}
}
],
"tagName": "another-component"
}
]
}
]
}
```
</details>
<details>
<summary>Result</summary>
```js
[
{
name: 'MyMixinB',
module: './src/MyMixinB.js',
kind: 'variable',
type: '(klass: any) => typeof MyMixinB',
},
];
```
</details>
<hr>
#### `getDefinitions`
Returns all custom element definitions in a `custom-elements.json`
```js
customElementsJson.getDefinitions();
```
<details>
<summary><code>custom-elements.json</code></summary>
```json
{
"version": "experimental",
"modules": [
{
"path": "./src/MyMixinB.js",
"exports": [
{
"kind": "variable",
"name": "MyMixinB",
"type": "(klass: any) => typeof MyMixinB"
}
]
},
{
"path": "./src/MySuperClass.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "MySuperClass",
"mixins": [
{
"name": "MyMixinB",
"module": "./src/MyMixinB.js"
}
],
"members": [
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\""
}
]
}
]
},
{
"path": "./src/AnotherSuperClass.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
},
"name": "AnotherSuperClass",
"members": [
{
"kind": "field",
"name": "label",
"type": "string",
"default": "\"d\""
},
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\"",
"inheritedFrom": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
}
}
]
}
]
},
{
"path": "./src/my-element.js",
"exports": [
{
"kind": "definition",
"name": "my-element",
"declaration": {
"name": "MyElement",
"module": "./src/my-element.js"
}
},
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "MyElement",
"members": [
{
"kind": "field",
"name": "foo",
"type": "string",
"default": "\"bar\""
}
],
"tagName": "my-element"
}
]
},
{
"path": "./src/another-component.js",
"exports": [
{
"kind": "definition",
"name": "another-component",
"declaration": {
"name": "AnotherComponent",
"module": "./src/another-component.js"
}
},
{
"kind": "class",
"superclass": {
"name": "AnotherSuperClass",
"module": "./src/AnotherSuperClass.js"
},
"name": "AnotherComponent",
"members": [
{
"kind": "field",
"name": "baz",
"type": "string",
"default": "\"bar\""
},
{
"kind": "field",
"name": "label",
"type": "string",
"default": "\"d\"",
"inheritedFrom": {
"name": "AnotherSuperClass",
"module": "./src/AnotherSuperClass.js"
}
},
{
"kind": "field",
"name": "text",
"type": "string",
"default": "\"b\"",
"inheritedFrom": {
"name": "MySuperClass",
"module": "./src/MySuperClass.js"
}
}
],
"tagName": "another-component"
}
]
}
]
}
```
</details>
<details>
<summary>Result</summary>
```js
[
{
kind: 'definition',
name: 'my-element',
declaration: { name: 'MyElement', module: './src/my-element.js' },
},
{
kind: 'definition',
name: 'another-component',
declaration: { name: 'AnotherComponent', module: './src/another-component.js' },
},
];
```
</details>
<hr>
#### `getInheritanceTree`
Returns inheritance for class `MyComponent`.
```js
customElementsJson.getInheritanceTree('MyComponent');
```
<details>
<summary><code>custom-elements.json</code></summary>
```json
{
"version": "experimental",
"modules": [
{
"path": "./dev/src/custom-element/LitElement.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "UpdatingElement",
"module": "./dev/src/custom-element/UpdatingElement.js"
},
"name": "LitElement"
}
]
},
{
"path": "./dev/src/custom-element/MyComponent.js",
"exports": [
{
"kind": "definition",
"name": "my-component",
"declaration": {
"name": "LitElement",
"module": "./dev/src/custom-element/LitElement.js"
}
},
{
"kind": "class",
"superclass": {
"name": "LitElement",
"module": "./dev/src/custom-element/LitElement.js"
},
"mixins": [
{
"name": "TabindexMixin",
"module": "./dev/src/custom-element/MyComponent.js"
},
{
"name": "LocalizeMixin",
"module": "./dev/src/custom-element/MyComponent.js"
}
],
"name": "MyComponent"
},
{
"kind": "class",
"name": "TabindexMixin"
},
{
"kind": "class",
"name": "LocalizeMixin"
},
{
"kind": "variable",
"name": "LocalizeMixin",
"type": "(klass: any) => typeof LocalizeMixin"
},
{
"kind": "variable",
"name": "TabindexMixin",
"type": "(klass: any) => typeof TabindexMixin"
}
]
},
{
"path": "./dev/src/custom-element/UpdatingElement.js",
"exports": [
{
"kind": "class",
"superclass": {
"name": "HTMLElement"
},
"name": "UpdatingElement"
}
]
}
]
}
```
</details>
<details>
<summary>Result</summary>
```js
[
{
kind: 'class',
superclass: {
name: 'LitElement',
module: './dev/src/custom-element/LitElement.js',
},
mixins: [[Object], [Object]],
name: 'MyComponent',
},
{
name: 'TabindexMixin',
module: './dev/src/custom-element/MyComponent.js',
kind: 'variable',
type: '(klass: any) => typeof TabindexMixin',
},
{
name: 'LocalizeMixin',
module: './dev/src/custom-element/MyComponent.js',
kind: 'variable',
type: '(klass: any) => typeof LocalizeMixin',
},
{
kind: 'class',
superclass: {
name: 'UpdatingElement',
module: './dev/src/custom-element/UpdatingElement.js',
},
name: 'LitElement',
},
{
kind: 'class',
superclass: { name: 'HTMLElement' },
name: 'UpdatingElement',
},
];
```
</details>
<hr>
## Helper functions
Additionally the following helper functions are available. These functions can help you when developing custom tooling for `custom-elements.json`, and make your code more declarative.
Demo:
```js
import * as h from '@custom-elements-json/helpers';
const json = require('./custom-elements.json');
if (h.hasModules(json)) {
json.modules.forEach(_module => {
if (h.hasExports(_module)) {
_module.exports.forEach(_export => {
if (h.isClass(_export) && h.hasAttributes(_export)) {
const attrs = _export.attributes;
// do something with attributes
}
});
}
});
});
```
### Package
- `hasModules(package: PackageDoc) => boolean`
### Module
- `hasExports(module: ModuleDoc) => boolean`
### Export
- `isClass(export: ExportDoc) => boolean`
- `isFunction(export: ExportDoc) => boolean`
- `isVariable(export: ExportDoc) => boolean`
- `isDefinition(export: ExportDoc) => boolean`
### CustomElement
- `hasAttributes(customElement: CustomElementDoc) => boolean`
- `hasEvents(customElement: CustomElementDoc) => boolean`
- `hasSlots(customElement: CustomElementDoc) => boolean`
- `hasMethods(customElement: CustomElementDoc) => boolean`
- `hasFields(customElement: CustomElementDoc) => boolean`
- `hasMixins(customElement: CustomElementDoc) => boolean`
### ClassMember
- `isField(member: ClassMember) => boolean`
- `isMethod(member: ClassMember) => boolean`
## Contributing
Developing:
```bash
npm run tsc:watch
# in a separate terminal
npm run start
```
Testing:
```bash
npm test
```
## Usecases
- [x] Get all information for a given class (this should include the information of any superclasses or mixins)
- [x] Get all information for a given tagName (this should include the information of any superclasses or mixins)
- [x] Get all mixins
- [x] Get all classes
- [x] Get all definitions
- [ ] Get attributes for a given class or tagName
- [ ] Get properties for a given class or tagName
- [ ] Get cssProperties for a given class or tagName
- [ ] Get cssParts for a given class or tagName
- [ ] Get events for a given class or tagName
- [ ] Get slots for a given class or tagName
- more? What sort of things would be helpful? Create an issue if you have any ideas
## Goals
- It'd be nice to have this library be fully isomorphic (runs in node and the browser)