open-wc / custom-elements-manifest-deprecated

Custom Elements Manifest is a file format that describes custom elements in your project.
8 stars 4 forks source link

[analyzer] Stencil plugin #54

Closed thepassle closed 3 years ago

thepassle commented 3 years ago

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

Getting started

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

Writing 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 🙂

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(){}
}