From d1beffbff58a4bb0e2029e8dfc3cba7158b97eed Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Thu, 5 Mar 2020 15:47:47 +0100 Subject: [PATCH] fix(core): use dedupeMixin @open-wc --- packages/core/index.js | 4 +- packages/core/package.json | 2 + packages/core/src/DelegateMixin.js | 2 +- packages/core/src/DisabledMixin.js | 2 +- .../core/src/DisabledWithTabIndexMixin.js | 2 +- packages/core/src/SlotMixin.js | 3 +- packages/core/src/UpdateStylesMixin.js | 2 +- packages/core/src/dedupeMixin.js | 87 ------------------- packages/core/test/dedupeMixin.test.js | 77 ---------------- 9 files changed, 10 insertions(+), 171 deletions(-) delete mode 100644 packages/core/src/dedupeMixin.js delete mode 100644 packages/core/test/dedupeMixin.test.js diff --git a/packages/core/index.js b/packages/core/index.js index b51dde4a0..cdac6f16b 100644 --- a/packages/core/index.js +++ b/packages/core/index.js @@ -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'; diff --git a/packages/core/package.json b/packages/core/package.json index 2e360ec27..74214b99b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -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" }, diff --git a/packages/core/src/DelegateMixin.js b/packages/core/src/DelegateMixin.js index a4193916f..a83497ecf 100644 --- a/packages/core/src/DelegateMixin.js +++ b/packages/core/src/DelegateMixin.js @@ -1,6 +1,6 @@ /* eslint-disable class-methods-use-this */ -import { dedupeMixin } from './dedupeMixin.js'; +import { dedupeMixin } from '@open-wc/dedupe-mixin'; /** * # DelegateMixin diff --git a/packages/core/src/DisabledMixin.js b/packages/core/src/DisabledMixin.js index b279695b3..3119670f6 100644 --- a/packages/core/src/DisabledMixin.js +++ b/packages/core/src/DisabledMixin.js @@ -1,4 +1,4 @@ -import { dedupeMixin } from './dedupeMixin.js'; +import { dedupeMixin } from '@open-wc/dedupe-mixin'; /** * #DisabledMixin diff --git a/packages/core/src/DisabledWithTabIndexMixin.js b/packages/core/src/DisabledWithTabIndexMixin.js index ac9189fab..e0570bacf 100644 --- a/packages/core/src/DisabledWithTabIndexMixin.js +++ b/packages/core/src/DisabledWithTabIndexMixin.js @@ -1,4 +1,4 @@ -import { dedupeMixin } from './dedupeMixin.js'; +import { dedupeMixin } from '@open-wc/dedupe-mixin'; import { DisabledMixin } from './DisabledMixin.js'; /** diff --git a/packages/core/src/SlotMixin.js b/packages/core/src/SlotMixin.js index 85275d9db..5d1e81f76 100644 --- a/packages/core/src/SlotMixin.js +++ b/packages/core/src/SlotMixin.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. diff --git a/packages/core/src/UpdateStylesMixin.js b/packages/core/src/UpdateStylesMixin.js index 0f5b2d9fe..3ad4f37e3 100644 --- a/packages/core/src/UpdateStylesMixin.js +++ b/packages/core/src/UpdateStylesMixin.js @@ -1,5 +1,5 @@ /* global ShadyCSS */ -import { dedupeMixin } from './dedupeMixin.js'; +import { dedupeMixin } from '@open-wc/dedupe-mixin'; export const UpdateStylesMixin = dedupeMixin( superclass => diff --git a/packages/core/src/dedupeMixin.js b/packages/core/src/dedupeMixin.js deleted file mode 100644 index 4655d0cca..000000000 --- a/packages/core/src/dedupeMixin.js +++ /dev/null @@ -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; - }; -} diff --git a/packages/core/test/dedupeMixin.test.js b/packages/core/test/dedupeMixin.test.js deleted file mode 100644 index eba17deee..000000000 --- a/packages/core/test/dedupeMixin.test.js +++ /dev/null @@ -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'); - // }); -});