fix(core): use dedupeMixin @open-wc
This commit is contained in:
parent
f810a30103
commit
d1beffbff5
9 changed files with 10 additions and 171 deletions
|
|
@ -51,8 +51,10 @@ export { styleMap } from 'lit-html/directives/style-map.js';
|
|||
export { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
||||
export { until } from 'lit-html/directives/until.js';
|
||||
export { render as renderShady } from 'lit-html/lib/shady-render.js';
|
||||
// open-wc
|
||||
export { ScopedElementsMixin, getScopedTagName } from '@open-wc/scoped-elements';
|
||||
export { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||
// ours
|
||||
export { dedupeMixin } from './src/dedupeMixin.js';
|
||||
export { DelegateMixin } from './src/DelegateMixin.js';
|
||||
export { DisabledMixin } from './src/DisabledMixin.js';
|
||||
export { DisabledWithTabIndexMixin } from './src/DisabledWithTabIndexMixin.js';
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
"*.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"@open-wc/dedupe-mixin": "^1.2.1",
|
||||
"@open-wc/scoped-elements": "^0.5.0",
|
||||
"lit-element": "^2.2.1",
|
||||
"lit-html": "^1.0.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { dedupeMixin } from './dedupeMixin.js';
|
||||
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||
|
||||
/**
|
||||
* # DelegateMixin
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { dedupeMixin } from './dedupeMixin.js';
|
||||
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||
|
||||
/**
|
||||
* #DisabledMixin
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { dedupeMixin } from './dedupeMixin.js';
|
||||
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||
import { DisabledMixin } from './DisabledMixin.js';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
import { dedupeMixin } from './dedupeMixin.js';
|
||||
|
||||
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||
/**
|
||||
* # SlotMixin
|
||||
* `SlotMixin`, when attached to the DOM it creates content for defined slots in the Light DOM.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* global ShadyCSS */
|
||||
import { dedupeMixin } from './dedupeMixin.js';
|
||||
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
||||
|
||||
export const UpdateStylesMixin = dedupeMixin(
|
||||
superclass =>
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
const appliedClassMixins = new WeakMap();
|
||||
|
||||
/**
|
||||
* @param {Object} obj
|
||||
* @returns {Array}
|
||||
*/
|
||||
function getPrototypeChain(obj) {
|
||||
const chain = [];
|
||||
let proto = obj;
|
||||
while (proto) {
|
||||
chain.push(proto);
|
||||
proto = Object.getPrototypeOf(proto);
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {function()} mixin
|
||||
* @param {function()} superClass
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function wasApplied(mixin, superClass) {
|
||||
const classes = getPrototypeChain(superClass);
|
||||
return classes.reduce((res, klass) => res || appliedClassMixins.get(klass) === mixin, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* # dedupeMixin
|
||||
*
|
||||
* Imagine you are developing web components and creating ES classes for
|
||||
* Custom Elements. You have two generic mixins (let's say `M1` and `M2`) which
|
||||
* require independently the same even more generic mixin (`BaseMixin`).
|
||||
* `M1` and `M2` can be used independently, that means they have to inherit
|
||||
* from `BaseMixin` also independently. But they can be also used in combination.
|
||||
* Sometimes `M1` and `M2` are used in the same component and can mess up the
|
||||
* inheritance chain if `BaseMixin` is applied twice. In other words, this may happen
|
||||
* to the protoype chain `... -> M2 -> BaseMixin -> M1 -> BaseMixin -> ...`.
|
||||
*
|
||||
* An example of this may be a `LocalizeMixin` used across different components and mixins.
|
||||
* Some mixins may need it and many components need it too and can not rely on other mixins
|
||||
* to have it by default, so must inherit from it independently.
|
||||
*
|
||||
* The more generic the mixin is, the higher the chance of being applied more than once.
|
||||
* As a mixin author you can't control how it is used, and can't always predict it.
|
||||
* So as a safety measure it is always recommended to create deduping mixins.
|
||||
*
|
||||
* @param {function()} mixin
|
||||
* @returns {function()}
|
||||
*
|
||||
* @example
|
||||
* // makes a conventional ES mixin deduping
|
||||
* const BaseMixin = dedupeMixin((superClass) => {
|
||||
* return class extends superClass { ... };
|
||||
* });
|
||||
*
|
||||
* // inherits from BaseMixin
|
||||
* const M1 = dedupeMixin((superClass) => {
|
||||
* return class extends BaseMixin(superClass) { ... };
|
||||
* });
|
||||
*
|
||||
* // inherits from BaseMixin
|
||||
* const M2 = dedupeMixin((superClass) => {
|
||||
* return class extends BaseMixin(superClass) { ... };
|
||||
* });
|
||||
*
|
||||
* // component inherits from M1
|
||||
* // MyCustomElement -> M1 -> BaseMixin -> BaseCustomElement;
|
||||
* class MyCustomElement extends M1(BaseCustomElement) { ... }
|
||||
*
|
||||
* // component inherits from M2
|
||||
* // MyCustomElement -> M2 -> BaseMixin -> BaseCustomElement;
|
||||
* class MyCustomElement extends M2(BaseCustomElement) { ... }
|
||||
*
|
||||
* // component inherits from both M1 and M2
|
||||
* // MyCustomElement -> M2 -> M1 -> BaseMixin -> BaseCustomElement;
|
||||
* class MyCustomElement extends M2(M1(BaseCustomElement)) { ... }
|
||||
*/
|
||||
export function dedupeMixin(mixin) {
|
||||
return superClass => {
|
||||
if (wasApplied(mixin, superClass)) {
|
||||
return superClass;
|
||||
}
|
||||
const mixedClass = mixin(superClass);
|
||||
appliedClassMixins.set(mixedClass, mixin);
|
||||
return mixedClass;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
import { expect } from '@open-wc/testing';
|
||||
|
||||
import { dedupeMixin } from '../src/dedupeMixin.js';
|
||||
|
||||
describe('dedupeMixin', () => {
|
||||
function createMixin(name) {
|
||||
return superClass =>
|
||||
class MixinClass extends superClass {
|
||||
getMixinNames() {
|
||||
const superName = super.getMixinNames ? ` ${super.getMixinNames()}` : '';
|
||||
return `${name}${superName}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createMixins(...names) {
|
||||
return names.map(name => createMixin(name));
|
||||
}
|
||||
|
||||
it('dedupes mixins', () => {
|
||||
const [Mixin1, Mixin2, Mixin3] = createMixins('Mixin1', 'Mixin2', 'Mixin3');
|
||||
const DedupingMixin3 = dedupeMixin(Mixin3);
|
||||
class BaseClass {}
|
||||
class MixedClass1 extends DedupingMixin3(Mixin1(Mixin2(DedupingMixin3(BaseClass)))) {}
|
||||
const MixedClass2 = DedupingMixin3(Mixin1(Mixin2(DedupingMixin3(BaseClass))));
|
||||
const myObj1 = new MixedClass1();
|
||||
const myObj2 = new MixedClass2();
|
||||
expect(myObj1.getMixinNames()).to.equal('Mixin1 Mixin2 Mixin3');
|
||||
expect(myObj2.getMixinNames()).to.equal('Mixin1 Mixin2 Mixin3');
|
||||
});
|
||||
|
||||
it('dedupes mixins only explicitely', () => {
|
||||
const [Mixin1, Mixin2, Mixin3] = createMixins('Mixin1', 'Mixin2', 'Mixin3');
|
||||
const DedupingMixin3 = dedupeMixin(Mixin3);
|
||||
class BaseClass {}
|
||||
class MixedClass1 extends Mixin3(Mixin1(Mixin2(DedupingMixin3(BaseClass)))) {}
|
||||
class MixedClass2 extends DedupingMixin3(Mixin1(Mixin2(Mixin3(BaseClass)))) {}
|
||||
const myObj1 = new MixedClass1();
|
||||
const myObj2 = new MixedClass2();
|
||||
expect(myObj1.getMixinNames()).to.equal('Mixin3 Mixin1 Mixin2 Mixin3');
|
||||
expect(myObj2.getMixinNames()).to.equal('Mixin3 Mixin1 Mixin2 Mixin3');
|
||||
});
|
||||
|
||||
it('dedupes mixins applied on inherited base classes', () => {
|
||||
const [Mixin1, Mixin2, Mixin3] = createMixins('Mixin1', 'Mixin2', 'Mixin3');
|
||||
const DedupingMixin3 = dedupeMixin(Mixin3);
|
||||
class BaseClass {}
|
||||
class BaseMixedClass extends Mixin1(Mixin2(DedupingMixin3(BaseClass))) {}
|
||||
class ExtendedMixedClass extends DedupingMixin3(BaseMixedClass) {}
|
||||
const myObj = new ExtendedMixedClass();
|
||||
expect(myObj.getMixinNames()).to.equal('Mixin1 Mixin2 Mixin3');
|
||||
});
|
||||
|
||||
it('dedupes mixins applied via other mixins', () => {
|
||||
const [Mixin1, Mixin2, Mixin3] = createMixins('Mixin1', 'Mixin2', 'Mixin3');
|
||||
const DedupingMixin1 = dedupeMixin(Mixin1);
|
||||
const DedupingMixin2 = dedupeMixin(Mixin2);
|
||||
const DedupingMixin3 = dedupeMixin(Mixin3);
|
||||
const Mixin123 = superClass => DedupingMixin1(DedupingMixin2(DedupingMixin3(superClass)));
|
||||
const Mixin312 = superClass => DedupingMixin3(DedupingMixin1(DedupingMixin2(superClass)));
|
||||
class BaseClass {}
|
||||
class MixedClass extends Mixin312(Mixin123(BaseClass)) {}
|
||||
const myObj = new MixedClass();
|
||||
expect(myObj.getMixinNames()).to.equal('Mixin1 Mixin2 Mixin3');
|
||||
});
|
||||
|
||||
// // ToDO: check with polymer3 mixin once we are on npm
|
||||
// it('works with mixins deduped by Polymer', () => {
|
||||
// const [Mixin1, Mixin2] = createMixins('Mixin1', 'Mixin2');
|
||||
// const DedMixin1 = dedupeMixin(Mixin1);
|
||||
// const PolMixin2 = dedupingMixin(Mixin2);
|
||||
// class BaseClass {}
|
||||
// class MixedClass extends DedMixin1(PolMixin2(DedMixin1(PolMixin2(BaseClass)))) {}
|
||||
// const myObj = new MixedClass();
|
||||
// expect(myObj.getMixinNames()).to.equal('Mixin1 Mixin2');
|
||||
// });
|
||||
});
|
||||
Loading…
Reference in a new issue