chore: export structure
This commit is contained in:
parent
94c0c2abb5
commit
85fa802338
165
packages/behavior/context/context.js
Normal file
165
packages/behavior/context/context.js
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
import D from 'debug';
|
||||||
|
import {invokeHookFlat} from 'scwp';
|
||||||
|
|
||||||
|
import {fastApply, objectFromEntries} from '@avocado/core';
|
||||||
|
|
||||||
|
const debug = D('@avocado:behavior:context');
|
||||||
|
|
||||||
|
export class Context {
|
||||||
|
|
||||||
|
constructor(defaults = {}) {
|
||||||
|
this.initialize();
|
||||||
|
for (const key in defaults) {
|
||||||
|
this.add(key, defaults[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static describe(description, fn) {
|
||||||
|
const {type} = description;
|
||||||
|
fn.type = type;
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static globals() {
|
||||||
|
if (!Context._globals) {
|
||||||
|
Context._globals = {};
|
||||||
|
const globalss = invokeHookFlat('behaviorContextGlobals');
|
||||||
|
for (let i = 0; i < globalss.length; i++) {
|
||||||
|
const globals = globalss[i];
|
||||||
|
for (const key in globals) {
|
||||||
|
Context._globals[key] = globals[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Context._globals;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(key, value) {
|
||||||
|
this.map.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
all() {
|
||||||
|
return objectFromEntries(Array.from(this.map.entries()));
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.map = new Map();
|
||||||
|
this.add('context', this);
|
||||||
|
const globals = this.constructor.globals();
|
||||||
|
for (const key in globals) {
|
||||||
|
this.add(key, globals[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static renderSteps(steps) {
|
||||||
|
return steps.reduce((rendered, step) => {
|
||||||
|
switch (step.type) {
|
||||||
|
case 'key':
|
||||||
|
if (rendered) {
|
||||||
|
rendered += '.';
|
||||||
|
}
|
||||||
|
rendered += step.key;
|
||||||
|
break;
|
||||||
|
case 'invoke':
|
||||||
|
rendered += `(${step.args.length > 0 ? '...' : ''})`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return rendered;
|
||||||
|
}, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
static renderStepsUntilNow(steps, step) {
|
||||||
|
const stepsUntilNow = steps.slice(0, steps.indexOf(step));
|
||||||
|
return this.renderSteps(stepsUntilNow);
|
||||||
|
}
|
||||||
|
|
||||||
|
takeStep(value, fn) {
|
||||||
|
if (value instanceof Promise) {
|
||||||
|
return value.then((value) => {
|
||||||
|
return fn(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return fn(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(traversal) {
|
||||||
|
const {steps, value} = traversal;
|
||||||
|
return this.traverseAndDo(steps, (node, previousNode, step, index) => {
|
||||||
|
const isLastStep = index === steps.length - 2;
|
||||||
|
// Traverse if we're not at the final step with a value.
|
||||||
|
if (!isLastStep || !value) {
|
||||||
|
return this.traverseOneStep(steps, previousNode, node, step);
|
||||||
|
}
|
||||||
|
// Try to set the value.
|
||||||
|
switch (step.type) {
|
||||||
|
case 'key':
|
||||||
|
if ('object' === typeof node) {
|
||||||
|
return node[step.key] = value.get(this);
|
||||||
|
}
|
||||||
|
case 'invoke':
|
||||||
|
const rendered = this.constructor.renderStepsUntilNow(steps, step);
|
||||||
|
debug(`invalid assignment to function "${rendered}"`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
traverseAndDo(steps, fn) {
|
||||||
|
const [first, ...rest] = steps;
|
||||||
|
if ('key' !== first.type) {
|
||||||
|
throw new TypeError(`First step in a traversal must be type "key"`);
|
||||||
|
}
|
||||||
|
let previousNode = null;
|
||||||
|
return rest.reduce((node, step, index) => {
|
||||||
|
return this.takeStep(node, (node) => {
|
||||||
|
const result = fn(node, previousNode, step, index);
|
||||||
|
previousNode = node;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}, this.map.get(first.key));
|
||||||
|
}
|
||||||
|
|
||||||
|
traverseOneStep(steps, previousNode, node, step) {
|
||||||
|
if ('undefined' === typeof node) {
|
||||||
|
const rendered = this.constructor.renderStepsUntilNow(steps, step);
|
||||||
|
debug(`"${rendered}" is traversed through but undefined`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (step.type) {
|
||||||
|
case 'key':
|
||||||
|
return node[step.key];
|
||||||
|
case 'invoke':
|
||||||
|
const args = step.args.map((arg) => arg.get(this));
|
||||||
|
// Pass the context itself as the last arg.
|
||||||
|
args.push(this);
|
||||||
|
// Any arg promises will be resolved; the arg values will be passed
|
||||||
|
// transparently to the invocation.
|
||||||
|
let hasPromise = false;
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
if (args[i] instanceof Promise) {
|
||||||
|
hasPromise = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasPromise) {
|
||||||
|
return Promise.all(args).then((args) => {
|
||||||
|
return fastApply(previousNode, node, args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return fastApply(previousNode, node, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,155 +1 @@
|
||||||
import D from 'debug';
|
export {Context} from './context';
|
||||||
import {invokeHookFlat} from 'scwp';
|
|
||||||
|
|
||||||
import {fastApply} from '@avocado/core';
|
|
||||||
|
|
||||||
const debug = D('@avocado:behavior:context');
|
|
||||||
|
|
||||||
export class Context {
|
|
||||||
|
|
||||||
constructor(defaults = {}) {
|
|
||||||
this.initialize();
|
|
||||||
for (const key in defaults) {
|
|
||||||
this.add(key, defaults[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static globals() {
|
|
||||||
if (!Context._globals) {
|
|
||||||
Context._globals = {};
|
|
||||||
const globalss = invokeHookFlat('behaviorContextGlobals');
|
|
||||||
for (let i = 0; i < globalss.length; i++) {
|
|
||||||
const globals = globalss[i];
|
|
||||||
for (const key in globals) {
|
|
||||||
Context._globals[key] = globals[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Context._globals;
|
|
||||||
}
|
|
||||||
|
|
||||||
add(key, value) {
|
|
||||||
this.map.set(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize() {
|
|
||||||
this.map = new Map();
|
|
||||||
this.add('context', this);
|
|
||||||
const globals = this.constructor.globals();
|
|
||||||
for (const key in globals) {
|
|
||||||
this.add(key, globals[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this.initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.map.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static renderSteps(steps) {
|
|
||||||
return steps.reduce((rendered, step) => {
|
|
||||||
switch (step.type) {
|
|
||||||
case 'key':
|
|
||||||
if (rendered) {
|
|
||||||
rendered += '.';
|
|
||||||
}
|
|
||||||
rendered += step.key;
|
|
||||||
break;
|
|
||||||
case 'invoke':
|
|
||||||
rendered += `(${step.args.length > 0 ? '...' : ''})`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return rendered;
|
|
||||||
}, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
static renderStepsUntilNow(steps, step) {
|
|
||||||
const stepsUntilNow = steps.slice(0, steps.indexOf(step));
|
|
||||||
return this.renderSteps(stepsUntilNow);
|
|
||||||
}
|
|
||||||
|
|
||||||
takeStep(value, fn) {
|
|
||||||
if (value instanceof Promise) {
|
|
||||||
return value.then((value) => {
|
|
||||||
return fn(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return fn(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
traverse(traversal) {
|
|
||||||
const {steps, value} = traversal;
|
|
||||||
return this.traverseAndDo(steps, (node, previousNode, step, index) => {
|
|
||||||
const isLastStep = index === steps.length - 2;
|
|
||||||
// Traverse if we're not at the final step with a value.
|
|
||||||
if (!isLastStep || !value) {
|
|
||||||
return this.traverseOneStep(steps, previousNode, node, step);
|
|
||||||
}
|
|
||||||
// Try to set the value.
|
|
||||||
switch (step.type) {
|
|
||||||
case 'key':
|
|
||||||
if ('object' === typeof node) {
|
|
||||||
return node[step.key] = value.get(this);
|
|
||||||
}
|
|
||||||
case 'invoke':
|
|
||||||
const rendered = this.constructor.renderStepsUntilNow(steps, step);
|
|
||||||
debug(`invalid assignment to function "${rendered}"`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
traverseAndDo(steps, fn) {
|
|
||||||
const [first, ...rest] = steps;
|
|
||||||
if ('key' !== first.type) {
|
|
||||||
throw new TypeError(`First step in a traversal must be type "key"`);
|
|
||||||
}
|
|
||||||
let previousNode = null;
|
|
||||||
return rest.reduce((node, step, index) => {
|
|
||||||
return this.takeStep(node, (node) => {
|
|
||||||
const result = fn(node, previousNode, step, index);
|
|
||||||
previousNode = node;
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}, this.map.get(first.key));
|
|
||||||
}
|
|
||||||
|
|
||||||
traverseOneStep(steps, previousNode, node, step) {
|
|
||||||
if ('undefined' === typeof node) {
|
|
||||||
const rendered = this.constructor.renderStepsUntilNow(steps, step);
|
|
||||||
debug(`"${rendered}" is traversed through but undefined`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (step.type) {
|
|
||||||
case 'key':
|
|
||||||
return node[step.key];
|
|
||||||
case 'invoke':
|
|
||||||
const args = step.args.map((arg) => arg.get(this));
|
|
||||||
// Pass the context itself as the last arg.
|
|
||||||
args.push(this);
|
|
||||||
// Any arg promises will be resolved; the arg values will be passed
|
|
||||||
// transparently to the invocation.
|
|
||||||
let hasPromise = false;
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
|
||||||
if (args[i] instanceof Promise) {
|
|
||||||
hasPromise = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasPromise) {
|
|
||||||
return Promise.all(args).then((args) => {
|
|
||||||
return fastApply(previousNode, node, args);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return fastApply(previousNode, node, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user