UNPKG

uhtml

Version:
77 lines (70 loc) 2.96 kB
'use strict'; const umap = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require('umap')); const {Hole, createCache, unroll} = require('./rabbit.js'); const {create, defineProperties} = Object; // both `html` and `svg` template literal tags are polluted // with a `for(ref[, id])` and a `node` tag too const tag = type => { // both `html` and `svg` tags have their own cache const keyed = umap(new WeakMap); // keyed operations always re-use the same cache and unroll // the template and its interpolations right away const fixed = cache => (template, ...values) => unroll( cache, {type, template, values} ); return defineProperties( // non keyed operations are recognized as instance of Hole // during the "unroll", recursively resolved and updated (template, ...values) => new Hole(type, template, values), { for: { // keyed operations need a reference object, usually the parent node // which is showing keyed results, and optionally a unique id per each // related node, handy with JSON results and mutable list of objects // that usually carry a unique identifier value(ref, id) { const memo = keyed.get(ref) || keyed.set(ref, create(null)); return memo[id] || (memo[id] = fixed(createCache())); } }, node: { // it is possible to create one-off content out of the box via node tag // this might return the single created node, or a fragment with all // nodes present at the root level and, of course, their child nodes value: (template, ...values) => unroll( createCache(), {type, template, values} ).valueOf() } } ); }; // each rendered node gets its own cache const cache = umap(new WeakMap); // rendering means understanding what `html` or `svg` tags returned // and it relates a specific node to its own unique cache. // Each time the content to render changes, the node is cleaned up // and the new new content is appended, and if such content is a Hole // then it's "unrolled" to resolve all its inner nodes. const render = (where, what) => { const hole = typeof what === 'function' ? what() : what; const info = cache.get(where) || cache.set(where, createCache()); const wire = hole instanceof Hole ? unroll(info, hole) : hole; if (wire !== info.wire) { info.wire = wire; where.textContent = ''; // valueOf() simply returns the node itself, but in case it was a "wire" // it will eventually re-append all nodes to its fragment so that such // fragment can be re-appended many times in a meaningful way // (wires are basically persistent fragments facades with special behavior) where.appendChild(wire.valueOf()); } return where; }; const html = tag('html'); const svg = tag('svg'); exports.Hole = Hole; exports.render = render; exports.html = html; exports.svg = svg;