stencil-quantum
Version:
Experience the quantum realm of stencil.
527 lines (515 loc) • 17.4 kB
JavaScript
'use strict';
const index = require('./index-059fa27d.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(() => index.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 = index.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 = index.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 = index.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 = index.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 = index.getElement(obj);
provider === null || provider === void 0 ? void 0 : provider.pauseHook(el, true);
}
});
hookComponent(prototype, "connectedCallback", o => {
if (o === obj) {
const el = index.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 = index.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 = index.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 = index.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 = index.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;
exports.Catch = Catch;
exports.Context = Context;
exports.ContextError = ContextError;
exports.Entanglement = Entanglement;
exports.Implement = Implement;
exports.Observe = Observe;
exports.Provide = Provide;
exports.Provider = Provider;
exports.QuantumError = QuantumError;
exports.React = React;
exports.Throw = Throw;
exports.hookComponent = hookComponent;
exports.log = log;
exports.nop = nop;
exports.throwQuantum = throwQuantum;