Improve read code

This commit is contained in:
2026-07-08 22:45:05 +02:00
parent 4b7db2f2e6
commit af34777b1c
+151 -140
View File
@@ -7,8 +7,8 @@ const toNd = n => n?._rt ? n._cnt : (n instanceof Node ? n : txt(n));
export const fragment = p => p.children; export const fragment = p => p.children;
export const val = v => isF(v) ? v() : v; export const val = v => isF(v) ? v() : v;
let aEff = null, aOwn = null, isFlushing = 0, bDepth = 0; let curEffect = null, curOwner = null, flushing = 0, batchDepth = 0;
const eQ = new Set(), MOUNTED = new WeakMap(); const effectQueue = new Set(), MOUNTED = new WeakMap();
const SVG_NS = "http://www.w3.org/2000/svg", XLINK = "http://www.w3.org/1999/xlink"; const SVG_NS = "http://www.w3.org/2000/svg", XLINK = "http://www.w3.org/1999/xlink";
const SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")); const SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(","));
@@ -16,230 +16,241 @@ const DANG_ATTR = new Set(["src", "href", "formaction", "action", "background",
const clr = s => { if (s) { s.forEach(f=>f()); s.clear(); } }; const clr = s => { if (s) { s.forEach(f=>f()); s.clear(); } };
const dispose = e => { const dispose = e => {
if (!e || e._x) return; if (!e || e._dead) return;
e._x = 1; e._dead = 1;
let st = [e], c; let stack = [e], c;
while ((c = st.pop())) { while ((c = stack.pop())) {
clr(c._c); clr(c._cln);
if (c._ch) { c._ch.forEach(x => st.push(x)); c._ch.clear(); } if (c._kids) { c._kids.forEach(x=>stack.push(x)); c._kids.clear(); }
if (c._d) { c._d.forEach(d => d.delete(c)); c._d.clear(); } if (c._deps) { c._deps.forEach(d=>d.delete(c)); c._deps.clear(); }
} }
}; };
export const onUnmount = f => aOwn && (aOwn._c ||= new Set()).add(f); export const onUnmount = f => curOwner && ((curOwner._cln ||= new Set()).add(f));
const untrack = f => { let p = aEff; aEff = null; try { return f() } finally { aEff = p } }; const untrack = f => { let p = curEffect; curEffect = null; try { return f() } finally { curEffect = p } };
const createEffect = (f, isC = 0) => { const createEffect = (fn, isComputed = 0) => {
const e = () => { const eff = () => {
if (e._x) return; if (eff._dead) return;
if (e._d) e._d.forEach(s => s.delete(e)); eff._deps?.forEach(s => s.delete(eff));
clr(e._c); clr(eff._cln);
let pE = aEff, pO = aOwn; let pE = curEffect, pO = curOwner;
aEff = aOwn = e; curEffect = curOwner = eff;
try { return e._res = f(); } try { return eff._res = fn(); }
catch (err) { console.error("[SigPro]", err); } catch (err) { console.error("[SigPro]", err); }
finally { aEff = pE; aOwn = pO; } finally { curEffect = pE; curOwner = pO; }
}; };
e._d = e._c = e._ch = null; eff._deps = eff._cln = eff._kids = null;
e._x = 0; e._iC = isC; eff._dead = 0; eff._comp = isComputed;
e._dp = aEff ? aEff._dp + 1 : 0; eff._depth = curEffect ? curEffect._depth + 1 : 0;
e._m = []; e._p = aOwn; eff._mnt = []; eff._parent = curOwner;
if (aOwn) (aOwn._ch ||= new Set()).add(e); if (curOwner) (curOwner._kids ||= new Set()).add(eff);
return e; return eff;
}; };
const flush = () => { const flush = () => {
if (isFlushing) return; if (flushing) return;
isFlushing = 1; flushing = 1;
let q = [...eQ].sort((a, b) => a._dp - b._dp); let q = [...effectQueue].sort((a,b)=>a._depth - b._depth);
eQ.clear(); effectQueue.clear();
for (let e of q) if (!e._x) e(); for (let e of q) if (!e._dead) e();
isFlushing = 0; flushing = 0;
}; };
export const batch = f => { export const batch = f => {
bDepth++; batchDepth++;
try { return f() } finally { if (!--bDepth && eQ.size && !isFlushing) flush() } try { return f() } finally { if (!--batchDepth && effectQueue.size && !flushing) flush() }
}; };
const trkUpd = (s, trg = 0) => { const track = (signal, trigger = 0) => {
if (!trg && aEff && !aEff._x) { if (!trigger && curEffect && !curEffect._dead) {
s.add(aEff); signal.add(curEffect);
(aEff._d ||= new Set()).add(s); (curEffect._deps ||= new Set()).add(signal);
} else if (trg && s.size) { } else if (trigger && signal.size) {
let q = 0; let q = 0;
for (let e of s) { for (let e of signal) {
if (e === aEff || e._x) continue; if (e === curEffect || e._dead) continue;
if (e._iC) { e._dt = 1; if (e._sb) trkUpd(e._sb, 1); } if (e._comp) { e._stale = 1; e._sub && track(e._sub, 1); }
else { eQ.add(e); q = 1; } else { effectQueue.add(e); q = 1; }
} }
if (q && !isFlushing && !bDepth) queueMicrotask(flush); if (q && !flushing && !batchDepth) queueMicrotask(flush);
} }
}; };
export const $ = (v, k = null) => { export const $ = (val, key = null) => {
let s = new Set(); const subs = new Set();
if (isF(v)) { if (isF(val)) {
let c, cp = () => { let cached, get = () => {
if (cp._dt) { if (get._stale) {
let p = aEff; aEff = cp; let p = curEffect; curEffect = get;
try { let n = v(); if (!Object.is(c, n)) { c = n; trkUpd(s, 1); } } try { let n = val(); if (!Object.is(cached, n)) { cached = n; track(subs, 1); } }
finally { aEff = p; } finally { curEffect = p; }
cp._dt = 0; get._stale = 0;
} }
trkUpd(s); return c; track(subs); return cached;
}; };
cp._iC = cp._dt = 1; cp._sb = s; cp._d = null; cp._x = 0; get._comp = get._stale = 1; get._sub = subs; get._deps = null; get._dead = 0;
return cp; return get;
} }
if (k) try { v = JSON.parse(localStorage.getItem(k)) ?? v } catch (e) { } if (key) try { val = JSON.parse(localStorage.getItem(key)) ?? val } catch {}
return (...a) => { return (...args) => {
if (a.length) { if (args.length) {
let n = isF(a[0]) ? a[0](v) : a[0]; let n = isF(args[0]) ? args[0](val) : args[0];
if (!Object.is(v, n)) { v = n; if (k) localStorage.setItem(k, JSON.stringify(v)); trkUpd(s, 1); } if (!Object.is(val, n)) { val = n; if (key) localStorage.setItem(key, JSON.stringify(val)); track(subs, 1); }
} }
trkUpd(s); return v; track(subs); return val;
}; };
}; };
export const watch = (src, cb) => { export const watch = (src, cb) => {
let e = createEffect(cb ? () => { let v = isA(src) ? src.map(s => s()) : src(); untrack(() => cb(v)) } : src); let eff = createEffect(cb ? () => { let v = isA(src) ? src.map(s=>s()) : src(); untrack(()=>cb(v)); } : src);
e(); return () => dispose(e); eff();
return () => dispose(eff);
}; };
const clnNd = n => { const cleanNode = n => {
if (!n) return; if (!n) return;
clr(n._c); clr(n._cln);
if (n._oE) dispose(n._oE); if (n._ownEff) dispose(n._ownEff);
if (n.childNodes) n.childNodes.forEach(clnNd); n.childNodes && n.childNodes.forEach(cleanNode);
}; };
const valAtt = (k, v) => (v == null || v === false) ? null : const safeAttr = (k, v) =>
v == null || v === false ? null :
(DANG_ATTR.has(k) || k.startsWith("on")) && /^\s*(javascript|data|vbscript):/i.test(String(v)) ? '#' : v; (DANG_ATTR.has(k) || k.startsWith("on")) && /^\s*(javascript|data|vbscript):/i.test(String(v)) ? '#' : v;
export const h = (tag, prp = {}, ch = []) => { export const h = (tag, props = {}, children = []) => {
if (prp instanceof Node || isA(prp) || !isO(prp)) { ch = prp; prp = {}; } if (props instanceof Node || isA(props) || !isO(props)) { children = props; props = {}; }
if (isF(tag)) { if (isF(tag)) {
let e = createEffect(() => e._res = tag(prp, { children: ch, emit: (ev, ...a) => prp[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...a) })); let eff = createEffect(() => eff._res = tag(props, { children, emit: (ev,...a) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...a) }));
e(); eff();
if (e._res == null) return null; if (eff._res == null) return null;
let nd = e._res instanceof Node || (isA(e._res) && e._res.every(n => n instanceof Node)) ? e._res : txt(e._res); let node = eff._res instanceof Node || (isA(eff._res) && eff._res.every(n => n instanceof Node)) ? eff._res : txt(eff._res);
let att = n => { if (isO(n) && !n._rt) { n._m = e._m || []; n._c = e._c || new Set(); n._oE = e; } }; let mark = n => { if (isO(n) && !n._rt) { n._mnt = eff._mnt || []; n._cln = eff._cln || new Set(); n._ownEff = eff; } };
isA(nd) ? nd.forEach(att) : att(nd); isA(node) ? node.forEach(mark) : mark(node);
return nd; return node;
} }
let isS = SVG_TAGS.has(tag), el = isS ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag); let isSVG = SVG_TAGS.has(tag),
el._c = new Set(); el = isSVG ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag);
el._cln = new Set();
for (let k in prp) { for (let k in props) {
let v = prp[k]; let v = props[k];
if (k === "ref") { isF(v) ? v(el) : (v.current = el); continue; } if (k === "ref") { isF(v) ? v(el) : (v.current = el); continue; }
if (isS && k.startsWith("xlink:")) { if (isSVG && k.startsWith("xlink:")) {
let cv = valAtt(k.slice(6), v); let cv = safeAttr(k.slice(6), v);
cv == null ? el.removeAttributeNS(XLINK, k.slice(6)) : el.setAttributeNS(XLINK, k.slice(6), cv); cv == null ? el.removeAttributeNS(XLINK, k.slice(6)) : el.setAttributeNS(XLINK, k.slice(6), cv);
continue; continue;
} }
if (k.startsWith("on")) { if (k.startsWith("on")) {
let ev = k.slice(2).toLowerCase(); el.addEventListener(ev, v); let ev = k.slice(2).toLowerCase(); el.addEventListener(ev, v);
let off = () => el.removeEventListener(ev, v); el._c.add(off); onUnmount(off); let off = () => el.removeEventListener(ev, v); el._cln.add(off); onUnmount(off);
} else if (isF(v)) { } else if (isF(v)) {
let e = createEffect(() => { let eff = createEffect(() => {
let r = valAtt(k, v()); let r = safeAttr(k, v());
if (k === "class") el.className = r || ""; if (k === "class") el.className = r || "";
else if (r == null) el.removeAttribute(k); else if (r == null) el.removeAttribute(k);
else if (k === "style" && typeof r == "string") el.setAttribute("style", r); else if (k === "style" && typeof r == "string") el.setAttribute("style", r);
else if (k in el && !isS) el[k] = r; else if (k in el && !isSVG) el[k] = r;
else el.setAttribute(k, r === true ? "" : r); else el.setAttribute(k, r === true ? "" : r);
}); });
e(); el._c.add(() => dispose(e)); onUnmount(() => dispose(e)); eff(); el._cln.add(() => dispose(eff)); onUnmount(() => dispose(eff));
if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) { if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) {
el.addEventListener(k === "checked" ? "change" : "input", ev => v(ev.target[k])); el.addEventListener(k === "checked" ? "change" : "input", ev => v(ev.target[k]));
} }
} else { } else {
let r = valAtt(k, v); let r = safeAttr(k, v);
if (r != null) { if (r != null) {
if (k === "style" && typeof r == "string") el.setAttribute("style", r); if (k === "style" && typeof r == "string") el.setAttribute("style", r);
else if (k in el && !isS) el[k] = r; else if (k in el && !isSVG) el[k] = r;
else el.setAttribute(k, r === true ? "" : r); else el.setAttribute(k, r === true ? "" : r);
} }
} }
} }
const app = c => { const append = c => {
if (isA(c)) return c.forEach(app); if (isA(c)) return c.forEach(append);
if (isF(c)) { if (isF(c)) {
let anc = txt(""), cur = []; el.appendChild(anc); let anchor = txt(""), cur = []; el.appendChild(anchor);
let e = createEffect(() => { let eff = createEffect(() => {
let r = c(), nxt = (isA(r) ? r : [r]).map(toNd), ref = anc; let res = c(), next = (isA(res) ? res : [res]).map(toNd), ref = anchor;
cur.forEach(n => { n._rt ? n.destroy() : clnNd(n); if (n.parentNode) n.remove(); }); cur.forEach(n => { n._rt ? n.destroy() : cleanNode(n); n.parentNode && n.remove(); });
for (let i = nxt.length - 1; i >= 0; i--) { for (let i = next.length - 1; i >= 0; i--) {
let nd = nxt[i]; let nd = next[i];
if (nd.parentNode !== ref.parentNode) ref.parentNode?.insertBefore(nd, ref); if (nd.parentNode !== ref.parentNode) ref.parentNode?.insertBefore(nd, ref);
if (nd._m) nd._m.forEach(f => f()); ref = nd; nd._mnt && nd._mnt.forEach(f => f());
ref = nd;
} }
cur = nxt; cur = next;
}); });
e(); el._c.add(() => dispose(e)); onUnmount(() => dispose(e)); eff(); el._cln.add(() => dispose(eff)); onUnmount(() => dispose(eff));
} else { } else {
let nd = toNd(c); el.appendChild(nd); let nd = toNd(c); el.appendChild(nd);
if (nd._m) nd._m.forEach(f => f()); nd._mnt && nd._mnt.forEach(f => f());
} }
}; };
app(ch); return el; append(children);
return el;
}; };
export const render = rFn => { export const render = fn => {
let c = new Set(), pO = aOwn, pE = aEff, cnt = doc.createElement("div"); let cln = new Set(), pO = curOwner, pE = curEffect,
cnt = doc.createElement("div");
cnt.style.display = "contents"; cnt.setAttribute("role", "presentation"); cnt.style.display = "contents"; cnt.setAttribute("role", "presentation");
aOwn = { _c: c }; aEff = null; curOwner = { _cln: cln }; curEffect = null;
const pRes = r => { const place = r => {
if (!r) return; if (!r) return;
if (r._rt) { c.add(r.destroy); cnt.appendChild(r._cnt); } if (r._rt) { cln.add(r.destroy); cnt.appendChild(r._cnt); }
else if (isA(r)) r.forEach(pRes); else if (isA(r)) r.forEach(place);
else cnt.appendChild(r instanceof Node ? r : txt(r)); else cnt.appendChild(r instanceof Node ? r : txt(r));
}; };
try { pRes(rFn({ onCleanup: f => c.add(f) })); } finally { aOwn = pO; aEff = pE; } try { place(fn({ onCleanup: f => cln.add(f) })); } finally { curOwner = pO; curEffect = pE; }
return { _rt: 1, _cnt: cnt, destroy: () => { clr(c); clnNd(cnt); cnt.remove(); } }; return { _rt: 1, _cnt: cnt, destroy: () => { clr(cln); cleanNode(cnt); cnt.remove(); } };
}; };
export const when = (c, Y, N = null) => { export const when = (cond, onTrue, onFalse = null) => {
let anc = txt(""), rt = h("div", { style: "display:contents" }, [anc]), v; let anchor = txt(""), root = h("div", { style: "display:contents" }, [anchor]), current;
watch(() => !!val(c), s => { watch(() => !!val(cond), v => {
if (v) { v.destroy(); v = null; } current?.destroy(); current = null;
let ct = s ? Y : N; let template = v ? onTrue : onFalse;
if (ct) { v = render(() => val(ct)); rt.insertBefore(v._cnt, anc); } if (template) { current = render(() => val(template)); root.insertBefore(current._cnt, anchor); }
}); });
onUnmount(() => v?.destroy()); return rt; onUnmount(() => current?.destroy());
return root;
}; };
export const each = (s, fn, kF) => { export const each = (src, mapFn, keyFn) => {
let anc = txt(""), rt = h("div", { style: "display:contents" }, [anc]), cch = new Map(); let anchor = txt(""), root = h("div", { style: "display:contents" }, [anchor]),
watch(() => val(s) || [], it => { cache = new Map();
let nCc = new Map(), nOd = []; watch(() => val(src) || [], items => {
for (let i = 0, l = (it || []).length; i < l; i++) { let newCache = new Map(), order = [];
let t = it[i], k = kF ? (t?.[kF] ?? i) : (t?.id ?? i), v = cch.get(k); for (let i = 0; i < items.length; i++) {
if (!v) v = render(() => fn(t, i)); else cch.delete(k); let item = items[i], k = keyFn ? (item?.[keyFn] ?? i) : (item?.id ?? i), v = cache.get(k);
nCc.set(k, v); nOd.push(v); if (!v) v = render(() => mapFn(item, i)); else cache.delete(k);
newCache.set(k, v); order.push(v);
} }
cch.forEach(v => v.destroy()); cache.forEach(v => v.destroy());
let ref = anc; let ref = anchor;
for (let i = nOd.length - 1; i >= 0; i--) { for (let i = order.length - 1; i >= 0; i--) {
let nd = nOd[i]._cnt; let nd = order[i]._cnt;
if (nd.nextSibling !== ref) rt.insertBefore(nd, ref); ref = nd; if (nd.nextSibling !== ref) root.insertBefore(nd, ref);
ref = nd;
} }
cch = nCc; cache = newCache;
}); });
return rt; return root;
}; };
export const mount = (c, tgt) => { export const mount = (component, target) => {
let t = typeof tgt == "string" ? doc.querySelector(tgt) : tgt; let el = typeof target === "string" ? doc.querySelector(target) : target;
if (!t) return; if (!el) return;
if (MOUNTED.has(t)) MOUNTED.get(t).destroy(); if (MOUNTED.has(el)) MOUNTED.get(el).destroy();
let i = render(isF(c) ? c : () => c); let instance = render(isF(component) ? component : () => component);
t.replaceChildren(i._cnt); MOUNTED.set(t, i); return i; el.replaceChildren(instance._cnt);
MOUNTED.set(el, instance);
return instance;
}; };
const htmlTags = "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video"; const htmlTags = "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video";