UNPKG

element-vir

Version:

Heroic. Reactive. Declarative. Type safe. Web components without compromise.

124 lines (123 loc) 4.27 kB
import { check } from '@augment-vir/assert'; import { filterMap, getOrSet } from '@augment-vir/common'; import { canHoldProperties, hasTagName, isMinimalDefinitionWithInputs, } from './minimal-element-definition.js'; import { tagNameKeys } from './vir-html/tag-name-keys.js'; /** * DOM nodes have a `tagName` but never contribute to a template transform, so keying the cache on * them would create an unbounded entry per node instance. */ function isDomNode(value) { return canHoldProperties(value) && check.isNumber(value.nodeType); } function extractElementKeys(values) { return filterMap(values, (value) => { if (isMinimalDefinitionWithInputs(value)) { return value.definition; } else if (!hasTagName(value) || isDomNode(value)) { return undefined; } else if (value.tagInterpolationKey) { return value.tagInterpolationKey; } else if (check.isFunction(value)) { return value; } else { /** * Any other object that merely carries a tag name gets a key shared by tag name so * that the cache is not defeated by a new object on every render. */ return getOrSet(tagNameKeys, value.tagName, () => { return { tagName: value.tagName, }; }); } }, check.isTruthy); } /** * The transformed templates are written to a map so that we can preserve reference equality between * calls. Without maintaining reference equality between html`` calls, lit-element reconstructs all * of its children on every render. * * This is a WeakMap because we only care about the transformed array value as long as the original * template array key exists. */ const transformedTemplateStrings = new WeakMap(); export function getAlreadyMappedTemplate(templateStringsKey, values) { /** * This runs on every render, so the maps are walked directly instead of through the wrapper * objects that {@link setNestedValues} needs for its failure reasons. */ return extractElementKeys(values).reduce((parent, elementKey) => { return parent?.nested?.get(elementKey); }, transformedTemplateStrings.get(templateStringsKey))?.template; } export function setMappedTemplate(templateStringsKey, values, valueToSet) { const elementKeys = extractElementKeys(values); return setNestedValues({ map: transformedTemplateStrings, keys: [ templateStringsKey, ...elementKeys, ], valueToSet, }); } function getCurrentKeyAndValue(map, keys, index) { const currentKey = keys[index]; if (currentKey == undefined) { return { currentKey: undefined, currentTemplateAndNested: undefined, reason: `key at index ${index} not found`, }; } const currentTemplateAndNested = map.get(currentKey); if (!currentTemplateAndNested) { return { currentKey, currentTemplateAndNested: undefined, reason: `key at index ${index} was not in the map`, }; } return { currentKey, currentTemplateAndNested, reason: 'key and value exists', }; } function setNestedValues({ map, keys, valueToSet, index = 0, }) { const { currentTemplateAndNested, currentKey, reason } = getCurrentKeyAndValue(map, keys, index); if (!currentKey) { return { result: false, reason, }; } const nestedAndTemplate = currentTemplateAndNested ?? { nested: undefined, template: undefined, }; if (!currentTemplateAndNested) { map.set(currentKey, nestedAndTemplate); } if (index === keys.length - 1) { nestedAndTemplate.template = valueToSet; return { result: true, reason: 'set value at end of keys array', }; } const nestedWeakMap = nestedAndTemplate.nested ?? new WeakMap(); if (!nestedAndTemplate.nested) { nestedAndTemplate.nested = nestedWeakMap; } return setNestedValues({ map: nestedWeakMap, keys, valueToSet, index: index + 1, }); }