UNPKG

@virtualstate/hooks-extended

Version:
109 lines 3.83 kB
import { Hook as Basic } from "@virtualstate/hooks"; import { createFragment, createNode, isSourceReference } from "@virtualstate/fringe"; import { Mutation } from "./mutation.js"; import { Reference } from "./reference.js"; import { Isolated } from "./isolated.js"; export function isTarget(node) { return Mutation.is(node) || Reference.is(node) || Isolated.is(node); } export const HookExtended = Hook; export function Hook(options, child) { return createNode(Basic, { ...options, hook: hook.bind(undefined, options) }, child); async function hook(options, node) { const { targets = [], depth = 0 } = options; if (isTarget(node)) { return [ createFragment({}, node.children), hook.bind(undefined, { ...options, depth: depth + 1, targets: targets.concat([{ target: node, depth }]) }) ]; } else { return [ await run(node, targets), hook.bind(undefined, { ...options, depth: depth + 1 }) ]; } } } async function run(node, targets = []) { if (!targets.length) { return node; } const isolated = targets.filter(isIsolatedTarget); const isolatedDepth = isolated.reduce((depth, descriptor) => Math.min(depth, descriptor.depth), Number.POSITIVE_INFINITY); function isAllowedDepth(descriptor) { if (isolatedDepth === Number.POSITIVE_INFINITY) { return true; } return descriptor.depth >= isolatedDepth; } const mutators = targets .filter(isAllowedDepth) .filter(isMutationTarget); const references = targets .filter(isAllowedDepth) .filter(isReferenceTarget); // Reference before we mutate await reference(node, references); const mutated = await mutate(node, mutators); // If _any_ change is made then we will re-invoke our references // This is a very important point that we may invoke reference fragments multiple times with the same vnode // // The implementation will need to track whether the reference was found before // // Mutators should only be returning a new node instance if they have a change! if (mutated !== node) { await reference(mutated, references); } return mutated; function reference(node, targets) { // Invoke all at once return Promise.all(targets .map(descriptor => descriptor.target) .filter(target => isExpected(node, target.options)) .map(target => target.options.on(node))); } async function mutate(node, targets) { if (!targets.length) { return node; } const currentMutators = targets.slice(); const nextMutator = currentMutators.shift(); if (isExpected(node, nextMutator.target.options)) { const nextValue = await nextMutator.target.options.mutate(node); return mutate(nextValue, currentMutators); } else { return mutate(node, currentMutators); } } function isExpected(node, { is: isValue }) { if (typeof isValue === "function") { return isValue(node); } if (isSourceReference(isValue)) { return node.reference === isValue; } return true; } } function isIsolatedTarget(hooked) { return Isolated.is(hooked.target); } function isReferenceTarget(hooked) { return Reference.is(hooked.target); } function isMutationTarget(hooked) { return Mutation.is(hooked.target); } //# sourceMappingURL=hooks.js.map