Skip to content
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 58 additions & 6 deletions schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export type Declaration =
| FunctionDeclaration
| MixinDeclaration
| VariableDeclaration
| CustomElement;
| CustomElementDeclaration;

/**
* A reference to an export of a module.
Expand All @@ -149,7 +149,7 @@ export interface Reference {
}

/**
* Description of a custom element class.
* A description of a custom element class.
*
* Custom elements are JavaScript classes, so this extends from
* `ClassDeclaration` and adds custom-element-specific features like
Expand All @@ -168,7 +168,13 @@ export interface Reference {
* tagName, and another `Module` should contain the
* `CustomElement`.
*/
export interface CustomElement extends ClassDeclaration {
export interface CustomElementDeclaration extends ClassDeclaration {}


/**
* The additional fields that a custom element adds to classes and mixins.
*/
export interface CustomElement extends ClassLike {
/**
* An optional tag name that should be specified if this is a
* self-registering element.
Expand All @@ -193,7 +199,7 @@ export interface CustomElement extends ClassDeclaration {
*/
slots?: Slot[];

parts?: CssPart[];
cssParts?: CssPart[];

cssProperties?: CssCustomProperty[];

Expand Down Expand Up @@ -404,9 +410,55 @@ export interface ClassMethod extends FunctionLike {
}

/**
*
* A description of a mixin.
*
* Mixins are functions which generate a new subclass of a given superclass.
* This interfaces describes the class and custom element features that
* are added by the mixin. As such, it extends the CustomElement interface and
* ClassLike interface.
*
* Since mixins are functions, it also extends the FunctionLike interface. This
* means a mixin is callable, and has parameters and a return type.
*
* The return type is often hard or impossible to accurately describe in type
* systems like TypeScript. It requires generics and an `extends` operator
* that TypeScript lacks. Therefore it's recommended that the return type is
* left empty. The most common form of a mixin function takes a single
* argument, so consumers of this interface should assume that the return type
* is the single argument subclassed by this declaration.
*
* A mixin should only have a superclass if it composes another mixin, and the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im not sure I understand.

Given:

export const MixinA = base => class extends MixinB(base){}

Here the MixinB would be "superclass"?

What in the case of:

export const MixinA = base => class extends MixinC(MixinB(base)){}

?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was updated to say "a mixins should not have a superclass". In your examples the other mixins would be included in the mixins field of MixinA.

* superclass should reference that mixin.
*
* @example
*
* This JavaScript mixin declaration:
* ```javascript
* const MyMixin = (base) => class extends base {
* foo() { ... }
* }
* ```
*
* Is described by this JSON:
* ```json
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it still have a "superclass" field that has as value base here? Thats how I currently handle it in @custom-elements-manifest/analyzer, but it might be redundant I guess?

Copy link
Collaborator Author

@justinfagnani justinfagnani Nov 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - mixins (usually) don't have superclasses until they are applied to a specific superclass.

When mixins do have a superclass, it's because of mixin composition:

const MixinA = (base) => class extends base {
  fieldA;
}

const MixinB = (base) class extends MixinA(base) {
  fieldB;
}

class C extends MixinB(S) {
}
const c = new C();
c.fieldA; // exists
c.fieldB; // exists

So here the superclass of MixinB would be MixinA.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a short mention of this in the docs

Copy link
Collaborator

@thepassle thepassle Nov 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a mixin has nested mixins, though, are those nested mixins then documented as being mixins, or superclasses? E.g.:

const MixinA = (base) => class extends base {
  fieldA;
}

const MixinB = (base) => class extends MixinA(base) {
  fieldB;
}

Would the mixin declaration look like this:

        {
          "kind": "mixin",
          "description": "",
          "name": "MixinB",
          "mixins": [
            {
              "name": "MixinA"
            }
          ],
         "parameters": [
             {
               "name": "base"
             }
           ],
          "members": ["//etc"]
        }

If we consider MixinA to be the superclass in that case, we have to change superclass to be superclasses and be an array, because there can be multiple mixins applied.

Intuitively, I feel like I'd prefer to name "nested mixins" just mixins in this case though

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point, I'll change the comments to says composed mixins should go in the mixins field.

* {
* "kind": "mixin",
* "name": "MyMixin",
* "parameters": [
* {
* "name": "base",
* }
* ],
* "members": [
* {
* "kind": "method",
* "name": "foo",
* }
* ]
* }
* ```
*/
export interface MixinDeclaration extends ClassLike, FunctionLike {
export interface MixinDeclaration extends CustomElement, FunctionLike {
kind: 'mixin';
}

Expand Down