diff --git a/src/events/defineEvents.js b/src/events/defineEvents.js index ade7a62..6f83dc8 100644 --- a/src/events/defineEvents.js +++ b/src/events/defineEvents.js @@ -56,11 +56,6 @@ export const Mixin = (Super = HTMLElement) => class WithEvents extends Super { // FIXME these won't apply if we're not using NudeElement somewhere in the inheritance chain static mixins = [PropsMixin(Super)]; - constructor () { - super(); - this.init(); - } - init () { this.constructor.init(); diff --git a/src/form-associated.js b/src/form-associated.js index 1587f61..8220833 100644 --- a/src/form-associated.js +++ b/src/form-associated.js @@ -35,11 +35,6 @@ export function appliesTo (Class) { } export const Mixin = (Super = HTMLElement, { internalsProp = "_internals", configProp = "formAssociated" } = {}) => class FormAssociated extends Super { - constructor () { - super(); - this.init(); - } - init () { this.constructor[init](); diff --git a/src/hooks/with.js b/src/hooks/with.js index ba0bd79..f15122b 100644 --- a/src/hooks/with.js +++ b/src/hooks/with.js @@ -7,15 +7,7 @@ export function appliesTo (Class) { export const Mixin = (Super = HTMLElement) => class WithHooks extends Super { static hooks = new Hooks(super.hooks || {}); - constructor () { - super(); - - this.init?.(); - } - init () { - super.init?.(); - const Self = this.constructor; if (Self.hooks && !(Self.hooks instanceof Hooks)) { diff --git a/src/mixins/apply.js b/src/mixins/apply.js index 0223f3e..55ed32e 100644 --- a/src/mixins/apply.js +++ b/src/mixins/apply.js @@ -1,18 +1,42 @@ import { copyProperties } from "../util/copy-properties.js"; +import { getSuper } from "../util/super.js"; export function applyMixins (Class = this, mixins = Class.mixins) { if (Object.hasOwn(Class, "mixinsActive") || !mixins?.length) { return; } + // Determine applicable mixins first. Mixins without `appliesTo()` are always applicable. + const applicable = mixins.filter(Mixin => Mixin.appliesTo?.(Class) ?? true); + if (!applicable.length) { + return; + } + Class.mixinsActive = []; - for (let Mixin of mixins) { - if (Mixin.appliesTo && !Mixin.appliesTo(Class)) { - // Not applicable to this class - continue; + // Phase 1: create stubs for all prototype methods from all applicable mixins, + // so that they can be used as the base function that mixins can extend. + // In that case, none of the mixins' functions is used as the base function to add side effects to. + for (const Mixin of applicable) { + for (const property of Object.getOwnPropertyNames(Mixin.prototype)) { + if (property === "constructor" || Object.hasOwn(Class.prototype, property)) { + continue; + } + + const descriptor = Object.getOwnPropertyDescriptor(Mixin.prototype, property); + if (typeof descriptor.value !== "function") { + continue; + } + + // Only create a stub if the class doesn't already have its own implementation + Class.prototype[property] = function (...args) { + getSuper(this, property)?.call(this, ...args); + }; } + } + // Phase 2: apply all mixins + for (let Mixin of applicable) { applyMixin(Class, Mixin); } } @@ -24,10 +48,9 @@ export function applyMixin (Class, Mixin, force = false) { return; } - copyProperties(Class, Mixin, {recursive: true, prototypes: true}); + copyProperties(Class, Mixin, { recursive: true, prototypes: true }); if (!alreadyApplied) { Class.mixinsActive.push(Mixin); } } - diff --git a/src/props/defineProps.js b/src/props/defineProps.js index 5fe3f91..5aacf15 100644 --- a/src/props/defineProps.js +++ b/src/props/defineProps.js @@ -4,11 +4,6 @@ import { defineLazyProperties } from "../util/lazy.js"; const { initialized, propsDef } = getSymbols; export const Mixin = (Super = HTMLElement) => class WithProps extends Super { - constructor () { - super(); - this.init(); - } - init () { this.constructor.init(); diff --git a/src/slots/defineSlots.js b/src/slots/defineSlots.js index 8b21009..86c3714 100644 --- a/src/slots/defineSlots.js +++ b/src/slots/defineSlots.js @@ -3,11 +3,6 @@ import { assignToSlot } from "./util.js"; let mutationObserver; export const Mixin = (Super = HTMLElement) => class DefineSlots extends Super { - constructor () { - super(); - this.init(); - } - init () { if (!this.shadowRoot) { return; diff --git a/src/styles/global.js b/src/styles/global.js index f82ccf5..63b9a36 100644 --- a/src/styles/global.js +++ b/src/styles/global.js @@ -10,11 +10,6 @@ export function appliesTo (Class) { } export const Mixin = (Super = HTMLElement) => class GlobalStyles extends Super { - constructor () { - super(); - this.init(); - } - async [render] () { let Self = this.constructor; diff --git a/src/styles/shadow.js b/src/styles/shadow.js index 3606616..6e35e1b 100644 --- a/src/styles/shadow.js +++ b/src/styles/shadow.js @@ -10,11 +10,6 @@ export function appliesTo (Class) { } export const Mixin = (Super = HTMLElement) => class ShadowStyles extends Super { - constructor () { - super(); - this.init(); - } - init () { if (!this.shadowRoot) { return;