respond-framework
Version:
create as fast you think
233 lines (228 loc) • 8.26 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.extractedEvents = exports.e = exports.default = exports.Namespace = exports.Event = void 0;
var _isNamespace = require("../utils/isNamespace.js");
var _sliceBranch = require("./helpers/sliceBranch.js");
var _mergeArgMeta = require("./helpers/mergeArgMeta.js");
var _kinds = require("./kinds.js");
var _reserved = require("./reserved.js");
var _inferArgName = require("./helpers/inferArgName.js");
const createEvents0 = (deps, events, propEvents) => {
const {
proto,
respond
} = deps;
proto.events = createEvents(respond, events, propEvents);
proto.events.init = respond.eventsByType.init ?? createEvent(respond, {
kind: _kinds.init
}, _kinds.init, proto.events);
extractedEvents.clear();
};
var _default = exports.default = createEvents0;
const createEvents = (respond, events, propEvents, namespace = new Namespace(respond)) => {
const evs = {
edit,
...events
};
const keys = propEvents ? Object.keys({
...evs,
...propEvents
}) : Object.keys(evs);
propEvents ??= {};
const cache = respond.eventsCache;
return keys.reduce((namespace, name) => {
const config = evs[name];
const propConfig = propEvents[name];
const eventOrNamespaceFromAncestor = cache.get(propConfig);
if (eventOrNamespaceFromAncestor) {
namespace[name] = eventOrNamespaceFromAncestor;
} else if ((0, _isNamespace.default)(propConfig ?? config)) {
namespace[name] = createEvents(respond, config, propConfig, new Namespace(respond, (0, _sliceBranch.prepend)(namespace.name, name)));
} else if (propConfig) {
namespace[name] = createEvent(respond.state[_reserved._parent].respond, propConfig, name, namespace); // fresh event passed as prop
} else if (config) {
namespace[name] = createEvent(respond, config, name, namespace);
}
if (config && !cache.has(config)) cache.set(config, namespace[name]); // even if overriden by a prop, point original to fully created event -- facilitates grandparent props by way of original reference in eventsCache.get(config)
return namespace;
}, namespace);
};
const createEvent = (respond, config, name, namespace) => {
const {
branch,
state
} = respond;
const type = (0, _sliceBranch.prepend)(branch, (0, _sliceBranch.prepend)(namespace.name, name));
const event = respond.prevEventsByType[type] ?? new_Event(); // optimization: preserve ref thru hmr + index changes in current replay so events stored in state are the correct references and cycles don't need to be wasted reviving them
if (typeof config === 'function') config = {
...custom,
custom: config
};
event.construct(branch, {
config,
name,
namespace,
respond,
type
});
if (respond.eventsByType[type]) throw new Error(`respond: adjacent namespaces + modules can't share the same name: "${type}"`);
respond.eventsByType[type] = event;
if (config.pattern) {
const pattern = state.basenameFull ? `${state.basenameFull}${config.pattern}` : config.pattern;
respond.eventsByPattern[pattern] = event;
}
if (extractedEvents.has(config)) {
const key = extractedEvents.get(config);
state[key] = event; // assign event originally extracted from state.fooEvent back to its original key -- see extractModuleAspects.js where the inverse occurs and its assigned to state.events by the same name -- the goal is to have it available as state.fooEvent, but created here as part of the standard events creation process as if it existed on state.events.fooEvent
}
return event;
};
const new_Event = () => {
// like `new Event`, except the instance is a function instead of an object (so we can do events.foo()), and we set its prototype manually
const event = (arg, meta) => event.create(arg, meta);
Object.setPrototypeOf(event, Event.prototype);
Object.defineProperty(event, 'name', {
writable: true
});
return event;
};
class Namespace {
constructor(respond, name = '') {
this[_reserved._branch] = respond.branch;
this.name = name;
}
is(namespace) {
return this === namespace;
}
in(...namespaces) {
return namespaces.includes(this);
}
id(respondOrState) {
if (respondOrState === undefined) return this.name;
const branch = respondOrState.respond?.branch ?? respondOrState.branch;
const b = (0, _sliceBranch.stripBranchWithUnknownFallback)(branch, this[_reserved._branch]);
return (0, _sliceBranch.prepend)(b, this.name);
}
}
exports.Namespace = Namespace;
class Event {
construct(branch, props) {
if (props.respond.hmr && this.config) {
Object.keys(this.config).forEach(k => delete this[k]); // dont preserve through HMR, in case deleted (eg a callback like event.submit was deleted and you expect it to not be to run when HMR replays last event)
}
if (props.respond.reuseEvents) {
delete this.done;
delete this.error;
delete this.data;
delete this.module;
}
Object.assign(this, props.config, props);
this[_reserved._branch] = branch;
this.kind ??= this.pattern ? _kinds.navigation : _kinds.submission;
this.sync ??= !!this.debounce;
this.moduleName = props.respond.moduleName;
this.__event = this.type;
}
get done() {
return this._once(_kinds.done);
}
get error() {
return this._once(_kinds.error);
}
get data() {
return this._once(_kinds.data);
}
_once(kind) {
const k = 'on' + kind[0].toUpperCase() + kind.slice(1);
const config = {
...this.config[k],
kind
};
const name = (0, _sliceBranch.prepend)(this.name, kind);
const value = createEvent(this.respond, config, name, this.namespace); // lazy
Object.defineProperty(this, kind, {
value,
configurable: true
}); // override proto getter, i.e. createEvent only once when first used
return value;
}
create(arg, meta) {
arg = (0, _inferArgName.applyArgName)(arg);
return new e(arg, meta, this);
}
dispatch(arg, meta) {
return this.respond.dispatch(this.create(arg, meta));
}
trigger(arg, meta) {
return this.respond.trigger(this.create(arg, meta));
}
prefetch(arg, meta) {
return this.fetch?.call(this.module, this.module, this.create(arg, meta));
}
is(event) {
return this === event;
}
in(...events) {
return events.includes(this);
}
toJSON() {
return {
__event: this.__event
};
}
id(respondOrState) {
const id = this._id ??= (0, _sliceBranch.prepend)(this.namespace.name, this.name);
if (respondOrState === undefined) return id;
const branch = respondOrState.respond?.branch ?? respondOrState.branch;
const b = (0, _sliceBranch.stripBranchWithUnknownFallback)(branch, this[_reserved._branch]);
return (0, _sliceBranch.prepend)(b, id);
}
get module() {
const branch = this[_reserved._branch];
const state = this.respond.branches[branch] ?? this.respond.topState;
if (this.respond.mem.rendered) {
Object.defineProperty(this, 'module', {
value: state,
configurable: true
}); // optimization: override getter once proxified
}
return state;
}
}
exports.Event = Event;
class e {
constructor(arg, meta, event) {
(0, _mergeArgMeta.default)(arg, meta, this);
const payload = event.transform?.call(event.module, event.module, this.arg, this) || {
...this.arg
};
Object.assign(this, payload);
this.event = event;
this.kind = event.kind;
this.payload = payload;
this.__e = true;
if (this.event.pattern) {
this.meta.url = event.respond.fromEvent(this).url;
}
}
dispatch(arg, meta) {
(0, _mergeArgMeta.default)(arg, meta, this); // 2nd chance to supply meta/arg
return this.event.respond.dispatch(this);
}
trigger(arg, meta) {
(0, _mergeArgMeta.default)(arg, meta, this); // 2nd chance to supply meta/arg
return this.event.respond.trigger(this);
}
create(arg, meta) {
(0, _mergeArgMeta.default)(arg, meta, this); // 2nd chance to supply meta/arg
return this.event(this.arg, this.meta);
}
}
exports.e = e;
const extractedEvents = exports.extractedEvents = new Map();
const edit = {
kind: _kinds.default.edit,
sync: true
};