UNPKG

stencil-quantum

Version:

Experience the quantum realm of stencil.

511 lines (500 loc) 17.2 kB
import { f as forceUpdate, g as getElement } from './index-44d29459.js'; //#region Logging const _log = (...args) => _log.debug && console.log(...args); _log.debug = false; const log = _log; const nop = () => { }; function hookComponent(prototype, key, cb) { const _original = prototype[key] || nop; prototype[key] = async function (...args) { log(key, this); const cb2 = await cb(this); let result = await _original.apply(this, args); if (cb2 instanceof Function) await cb2(this); return result; }; return () => { prototype[key] = _original; }; } //#endregion const $providers = Symbol.for("stencil-quantum-providers"); class Provider { constructor(key, value, debug = false) { this.key = key; this.value = value; this.debug = debug; this.listeners = []; this.hooks = new Map(); this.mutable = false; this.paused = false; this.retrieve = () => { return this.value; }; this.provide = (value) => { if (this.debug) log(`(${String(this.key)}) `, "PROVIDING", value, "to", this.listeners); let oldvalue = this.value; this.value = value; if (!this.paused) { this.listeners .filter(listener => !listener.paused) .forEach(listener => listener.action(value, oldvalue)); } return this; }; this.update = (fn) => { return this.provide(fn(this.retrieve())); }; this.listen = (cb, updateImmediately = true, el) => { if (this.debug) log(`(${String(this.key)}) `, "LISTEN", updateImmediately, this, cb); const listener = { action: cb, unlisten: () => this.unlisten(cb), paused: false }; this.listeners = [...this.listeners, listener]; if (updateImmediately) cb(this.value); return listener; }; this.unlisten = (cb) => { this.listeners = this.listeners.filter(listener => listener.action !== cb); }; this.attach = (el, noHook) => { if (this.debug) log(`(${String(this.key)}) `, "Add Provider", el, this); const providers = Provider.getAttached(el); if (!providers.includes(this)) providers.push(this); if (this.debug) log(`(${String(this.key)}) `, "Total Providers", el, providers); return noHook ? this : this.hook(el); }; this.isHooked = (el) => { return this.hooks.has(el); }; this.getHook = (el) => this.hooks.get(el); this.hook = (el) => { if (this.debug) log(`(${String(this.key)}) `, "Hook Provider", el, this); this.hooks.set(el, this.listen(() => forceUpdate(el))); return this; }; this.pauseHook = (el, paused = true) => { if (this.debug) log(`(${String(this.key)}) `, "Pausing Hook", paused); if (this.isHooked(el)) this.getHook(el).paused = paused; }; this.unhook = (el) => { var _a; if (this.debug) log(`(${String(this.key)}) `, "Unhook Provider", el, this); if (this.isHooked(el)) { (_a = this.hooks.get(el)) === null || _a === void 0 ? void 0 : _a.unlisten(); this.hooks.delete(el); } return this; }; this.destroy = () => { this.listeners = []; this.hooks = new Map(); }; } pause(paused = true) { this.paused = paused; } /** * Creates a predicate, that filters providers matching a key and having no namespace or being * @param key * @param namespace */ static makeFilter(key, namespace) { return (p) => p.key === key || (namespace && typeof key === "string" && p.key === namespace + "__" + key); } static find(el, key, namespace, debug) { var _a, _b, _c, _d; if (debug) log(`(${String(key)}) `, "Searching Provider", key, namespace, el); const providers = Provider.getAttached(el).filter(Provider.makeFilter(key, namespace)); if (providers.length > 1) { throw new QuantumError(`Found multiple "${String(key)}" providers on the same object!`); } else if (providers.length === 1) { return providers[0]; } // No provider found on this el, check parent let parent = ((_c = (_a = el.parentElement) !== null && _a !== void 0 ? _a : (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.host) !== null && _c !== void 0 ? _c : (_d = el.parentNode) === null || _d === void 0 ? void 0 : _d.host); // parentElement or shadowRoot.host if (!parent) { throw new QuantumError(`No provider in hierarchy found that matches "${String(key)}"!`); } return Provider.find(parent, key, namespace, debug).attach(el, true); // Attach reference to this el to make lookup for children shorter } static create(el, key, value, namespace, debug) { if (typeof key === "string") key = (namespace ? namespace + "__" : "") + key; if (debug) log(`(${String(key)}) `, "Create Provider", el, key, value); return new Provider(key, value, debug).attach(el); } static getAttached(el) { if (!(el[$providers] instanceof Array)) { el[$providers] = []; } return el[$providers]; } } const $error = Symbol.for("stencil-quantum-error"); class QuantumError extends Error { constructor(err, target) { super(err instanceof Error ? err.message : err); this.target = target; if (err instanceof Error) Object.assign(this, err); } } function throwQuantum(el, error) { const err = new QuantumError(error, el); console.error(err); try { const provider = Provider.find(el, $error); provider.provide(err); } catch (e) { throw err; } } function Throw(opts) { return function (prototype, propertyName) { let provider; hookComponent(prototype, "componentWillLoad", obj => { const el = getElement(obj); try { provider = Provider.find(el, $error, opts === null || opts === void 0 ? void 0 : opts.namespace); provider.attach(el); provider.hook(el); } catch (err) { throwQuantum(el, err); } }); if (delete prototype[propertyName]) { Object.defineProperty(prototype, propertyName, { get: () => provider === null || provider === void 0 ? void 0 : provider.retrieve(), set: (v) => provider === null || provider === void 0 ? void 0 : provider.provide(v), enumerable: true, configurable: true }); } }; } function ContextError(opts) { return function (prototype, propertyName) { let provider; hookComponent(prototype, "componentWillLoad", obj => { const el = getElement(obj); provider = Provider.create(el, $error, prototype[propertyName], opts === null || opts === void 0 ? void 0 : opts.namespace); try { provider.attach(el); provider.hook(el); } catch (err) { throwQuantum(el, err); } }); if (delete prototype[propertyName]) { Object.defineProperty(prototype, propertyName, { get: () => provider === null || provider === void 0 ? void 0 : provider.retrieve(), set: (v) => provider === null || provider === void 0 ? void 0 : provider.provide(v), enumerable: true, configurable: true }); } }; } function Catch(opts) { return function (prototype, propertyName, propertyDesciptor) { hookComponent(prototype, "componentWillLoad", obj => { const el = getElement(obj); const provider = Provider.create(el, $error, undefined, opts === null || opts === void 0 ? void 0 : opts.namespace); const responseProvider = (opts === null || opts === void 0 ? void 0 : opts.provide) ? Provider.create(el, opts.provide, undefined, opts.namespace) : undefined; try { provider.attach(el); responseProvider === null || responseProvider === void 0 ? void 0 : responseProvider.attach(el); provider.listen(async (v) => { var _a; try { const response = await ((_a = propertyDesciptor.value) === null || _a === void 0 ? void 0 : _a.apply(obj, [v])); responseProvider === null || responseProvider === void 0 ? void 0 : responseProvider.provide(response); } catch (err) { throwQuantum(el, err); } }); } catch (err) { throwQuantum(el, err); } }); return propertyDesciptor; }; } function Context(config, key) { return function (prototype, propertyName) { const opts = config === null || config === void 0 ? void 0 : config.get(key); let defaultValue = opts === null || opts === void 0 ? void 0 : opts.default; delete prototype[propertyName]; hookComponent(prototype, "componentWillLoad", obj => { const el = getElement(obj); let provider; try { provider = Provider.find(el, key, opts === null || opts === void 0 ? void 0 : opts.namespace, opts === null || opts === void 0 ? void 0 : opts.debug); provider.hook(el); } catch (err) { } hookComponent(prototype, "disconnectedCallback", o => { if (o === obj) { const el = getElement(obj); provider === null || provider === void 0 ? void 0 : provider.pauseHook(el, true); } }); hookComponent(prototype, "connectedCallback", o => { if (o === obj) { const el = getElement(obj); provider === null || provider === void 0 ? void 0 : provider.pauseHook(el, false); } }); Object.defineProperty(obj, propertyName, { get: () => { var _a; return (_a = provider === null || provider === void 0 ? void 0 : provider.retrieve()) !== null && _a !== void 0 ? _a : defaultValue; }, set: v => { if ((opts === null || opts === void 0 ? void 0 : opts.mutable) && (provider === null || provider === void 0 ? void 0 : provider.mutable)) { provider.provide(v); } else { defaultValue = v; } }, enumerable: true, configurable: true }); return () => { if (provider) return; try { provider = Provider.find(el, key, opts === null || opts === void 0 ? void 0 : opts.namespace, opts === null || opts === void 0 ? void 0 : opts.debug); provider.hook(el); } catch (err) { throwQuantum(el, err); } }; }); }; } function Observe(config, key) { return function (prototype, propertyName, propertyDesciptor) { const opts = config === null || config === void 0 ? void 0 : config.get(key); const method = propertyDesciptor.value; hookComponent(prototype, "componentWillLoad", obj => { let provider; let listener; const el = getElement(obj); const hookProvider = () => { provider = Provider.find(el, key, opts === null || opts === void 0 ? void 0 : opts.namespace); listener = provider.listen(v => { try { method.apply(obj, [v]); } catch (err) { throwQuantum(el, err); } }); }; hookComponent(prototype, "disconnectedCallback", o => { if (o === obj && listener) listener.paused = true; }); hookComponent(prototype, "connectedCallback", o => { if (o === obj && listener) listener.paused = false; }); try { hookProvider(); } catch (err) { } return () => { if (provider) return; listener === null || listener === void 0 ? void 0 : listener.unlisten(); try { hookProvider(); } catch (err) { throwQuantum(el, err); } }; }); return propertyDesciptor; }; } function Provide(config, key) { return function (prototype, propertyName) { const opts = config === null || config === void 0 ? void 0 : config.get(key); let defaultValue = prototype[propertyName] || (opts === null || opts === void 0 ? void 0 : opts.default); delete prototype[propertyName]; hookComponent(prototype, "componentWillLoad", obj => { const el = getElement(obj); const provider = Provider.create(el, key, defaultValue !== null && defaultValue !== void 0 ? defaultValue : obj[propertyName], opts === null || opts === void 0 ? void 0 : opts.namespace, opts === null || opts === void 0 ? void 0 : opts.debug); provider.mutable = !!(opts === null || opts === void 0 ? void 0 : opts.mutable); try { provider.attach(el); provider.hook(el); } catch (err) { throwQuantum(el, err); } Object.defineProperty(obj, propertyName, { get: () => provider.retrieve(), set: (v) => provider.provide(v), enumerable: true, configurable: true }); hookComponent(prototype, "disconnectedCallback", o => { if (o === obj) provider.pause(); }); hookComponent(prototype, "connectedCallback", o => { if (o === obj) provider.pause(false); }); }); }; } function React(from, fromKey, to, toKey) { return function (prototype, propertyName, propertyDesciptor) { const fromOpts = from.get(fromKey); const toOpts = to.get(toKey); const method = propertyDesciptor.value; hookComponent(prototype, "componentWillLoad", obj => { const el = getElement(obj); let listener; let inputProvider; const resultProvider = Provider.create(el, toOpts.name, undefined, toOpts.namespace); resultProvider.mutable = !!toOpts.mutable; const hookProvider = () => { inputProvider = Provider.find(el, fromOpts.name, fromOpts.namespace); listener = inputProvider.listen(async (v) => { try { const result = await method.apply(obj, [v]); if (resultProvider) resultProvider.provide(result); } catch (err) { throwQuantum(el, err); } }); }; hookComponent(prototype, "disconnectedCallback", o => { if (o === obj && listener) listener.paused = true; }); hookComponent(prototype, "connectedCallback", o => { if (o === obj && listener) listener.paused = false; }); try { hookProvider(); return nop; } catch (err) { return () => { listener === null || listener === void 0 ? void 0 : listener.unlisten(); try { hookProvider(); } catch (err) { throwQuantum(el, err); } }; } }); return propertyDesciptor; }; } function Implement(config, key) { return function (prototype, propertyName) { const opts = config === null || config === void 0 ? void 0 : config.get(key); let defaultValue = prototype[propertyName] || (opts === null || opts === void 0 ? void 0 : opts.default); delete prototype[propertyName]; hookComponent(prototype, "componentWillLoad", obj => { const el = getElement(obj); const provider = Provider.create(el, key, defaultValue !== null && defaultValue !== void 0 ? defaultValue : obj[propertyName], opts === null || opts === void 0 ? void 0 : opts.namespace, opts === null || opts === void 0 ? void 0 : opts.debug); try { provider.attach(el); provider.hook(el); } catch (err) { throwQuantum(el, err); } Object.defineProperty(obj, propertyName, { get: () => provider.retrieve(), set: (v) => provider.provide((...args) => v.apply(obj, args)), enumerable: true, configurable: true }); hookComponent(prototype, "disconnectedCallback", o => { if (o === obj) provider.pause(); }); hookComponent(prototype, "connectedCallback", o => { if (o === obj) provider.pause(false); }); }); }; } // TODO: factory function that adds all keys to the config; class Entanglement { constructor(schema, opts) { this.keys = Object.entries(schema) .map(([key, value]) => { var _a; return ({ name: key, namespace: (_a = opts === null || opts === void 0 ? void 0 : opts.namespace) !== null && _a !== void 0 ? _a : `$${Entanglement.uid++}`, default: value.default, mutable: value.mutable, debug: value.debug, config: this }); }) .reduce((a, b) => (Object.assign(Object.assign({}, a), { [b.name]: b })), {}); } get(key) { return this.keys[key]; } Provide(key) { return Provide(this, key); } Context(key) { return Context(this, key); } Observe(key) { return Observe(this, key); } React(key, target, targetKey) { return React(this, key, target, targetKey); } Implement(key) { return Implement(this, key); } Action(key) { return Context(this, key); } } Entanglement.uid = 0; export { ContextError as C, Entanglement as E, Implement as I, Observe as O, Provider as P, QuantumError as Q, React as R, Throw as T, Catch as a, Context as b, Provide as c, hookComponent as h, log as l, nop as n, throwQuantum as t };