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.
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(){}
}
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
npm start
Tests
For tests, you can add a new fixture in the
fixtures
folder, and the tests will automatically run it, and compare theoutput.json
(which is the output generated by the analyzer) to thefixture/custom-elements.json
, which is the expected state.Stencil syntax
@Component({tag: 'my-foo'})
decorator to add atagName
to a classDockind
:custom-elements-definition
, and should show up in a modules exports@Prop()
decorator@Prop()
, make sure to add it as an attribute as well@Prop({reflect: true})
, make sure to add it as an attribute as well@Prop({attribute: 'foo'})
, make sure to add it as an attribute as well, with the given attribute name@Prop() isValid: string;
make sure the attr name isis-valid
(camelcase to kebab)@Prop() foo: ComplexType
ignore the attribute; only add attributes if the type is a primitive (num, str, bool, etc)@Event()
decorator@Event() todoCompleted: EventEmitter<Todo>;
, it will fire atodoCompleted
event, add that to events@Event({eventName: 'todoCompleted'})
, it will fire atodoCompleted
event, add that to eventsWriting a plugin
A plugin is a function that returns an object. There are several hooks you can opt in to:
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 🙂Here's a fixture/overview (and ASTExplorer link) of things the plugin should support: