diff --git a/dist/dde-with-signals.js b/dist/dde-with-signals.js index 115a36b..644454e 100644 --- a/dist/dde-with-signals.js +++ b/dist/dde-with-signals.js @@ -1,6 +1,6 @@ //deka-dom-el library is available via global namespace `dde` (()=> { -// src/signals-lib/signals-common.js +// src/signals-lib/common.js var signals_global = { isSignal(attributes) { return false; @@ -570,8 +570,36 @@ on.attributeChanged = function(listener, options) { }; }; -// src/signals-lib/signals-lib.js +// src/signals-lib/helpers.js var mark = "__dde_signal"; +var SignalDefined = class extends Error { + constructor() { + super(); + const [curr, ...rest] = this.stack.split("\n"); + const curr_file = curr.slice(curr.indexOf("@"), curr.indexOf(".js:") + 4); + this.stack = rest.find((l) => !l.includes(curr_file)); + } +}; +var queueSignalWrite = /* @__PURE__ */ (() => { + let pendingSignals = /* @__PURE__ */ new Set(); + let scheduled = false; + function flushSignals() { + scheduled = false; + for (const signal2 of pendingSignals) { + const M = signal2[mark]; + if (M) M.listeners.forEach((l) => l(M.value)); + } + pendingSignals.clear(); + } + return function(s) { + pendingSignals.add(s); + if (scheduled) return; + scheduled = true; + queueMicrotask(flushSignals); + }; +})(); + +// src/signals-lib/signals-lib.js function isSignal(candidate) { return typeof candidate === "function" && hasOwn(candidate, mark); } @@ -602,12 +630,14 @@ function signal(value, actions) { return out; } signal.action = function(s, name, ...a) { - const M = s[mark], { actions } = M; - if (!actions || !(name in actions)) + const M = s[mark]; + if (!M) return; + const { actions } = M; + if (!actions || !hasOwn(actions, name)) throw new Error(`Action "${name}" not defined. See ${mark}.actions.`); actions[name].apply(M, a); if (M.skip) return delete M.skip; - M.listeners.forEach((l) => l(M.value)); + queueSignalWrite(s); }; signal.on = function on2(s, listener, options = {}) { const { signal: as } = options; @@ -621,7 +651,6 @@ signal.symbols = { onclear: Symbol.for("Signal.onclear") }; signal.clear = function(...signals2) { - console.log("clenaup", signals2); for (const s of signals2) { const M = s[mark]; if (!M) continue; @@ -647,17 +676,15 @@ var storeMemo = /* @__PURE__ */ new WeakMap(); function memo(key, fun, cache) { if (typeof key !== "string") key = JSON.stringify(key); if (!cache) { - const key2 = scope.host(); - if (storeMemo.has(key2)) - cache = storeMemo.get(key2); + const keyStore = scope.host(); + if (storeMemo.has(keyStore)) + cache = storeMemo.get(keyStore); else { cache = {}; - storeMemo.set(key2, cache); + storeMemo.set(keyStore, cache); } } - if (!hasOwn(cache, key)) - cache[key] = fun(); - return cache[key]; + return hasOwn(cache, key) ? cache[key] : cache[key] = fun(); } signal.el = function(s, map) { const mark_start = createElement.mark({ type: "reactive" }, true); @@ -766,7 +793,6 @@ function removeSignalsFromElements(s, listener, ...notes) { }); } var cleanUpRegistry = new FinalizationRegistry(function(s) { - console.log("UNREG"); signal.clear({ [mark]: s }); }); function create(is_readonly, value, actions) { @@ -780,14 +806,6 @@ var protoSigal = Object.assign(/* @__PURE__ */ Object.create(null), { this.skip = true; } }); -var SignalDefined = class extends Error { - constructor() { - super(); - const [curr, ...rest] = this.stack.split("\n"); - const curr_file = curr.slice(curr.indexOf("@"), curr.indexOf(".js:") + 4); - this.stack = rest.find((l) => !l.includes(curr_file)); - } -}; function toSignal(s, value, actions, readonly = false) { const onclear = []; if (typeOf(actions) !== "[object Object]") @@ -828,28 +846,9 @@ function read(s) { if (deps.has(context)) deps.get(context).add(s); return value; } -var queueSignalWrite = /* @__PURE__ */ (() => { - let pendingSignals = /* @__PURE__ */ new Set(); - let scheduled = false; - function flushSignals() { - scheduled = false; - for (const signal2 of pendingSignals) { - const M = signal2[mark]; - if (M) M.listeners.forEach((l) => l(M.value)); - } - pendingSignals.clear(); - } - return function(s) { - pendingSignals.add(s); - if (scheduled) return; - scheduled = true; - queueMicrotask(flushSignals); - }; -})(); function write(s, value, force) { - if (!s[mark]) return; const M = s[mark]; - if (!force && M.value === value) return; + if (!M || !force && M.value === value) return; M.value = value; queueSignalWrite(s); return value; @@ -863,13 +862,13 @@ function removeSignalListener(s, listener, clear_when_empty) { if (!M) return; const { listeners: L } = M; const out = L.delete(listener); - if (clear_when_empty && !L.size) { - signal.clear(s); - if (!deps.has(M)) return out; - const c = deps.get(M); - if (!deps.has(c)) return out; - deps.get(c).forEach((sig) => removeSignalListener(sig, c, true)); - } + if (!out || !clear_when_empty || L.size) return out; + signal.clear(s); + const depList = deps.get(M); + if (!depList) return out; + const depSource = deps.get(depList); + if (!depSource) return out; + for (const sig of depSource) removeSignalListener(sig, depList, true); return out; } diff --git a/dist/dde.js b/dist/dde.js index e535d05..a1cb26a 100644 --- a/dist/dde.js +++ b/dist/dde.js @@ -1,6 +1,6 @@ //deka-dom-el library is available via global namespace `dde` (()=> { -// src/signals-lib/signals-common.js +// src/signals-lib/common.js var signals_global = { isSignal(attributes) { return false; diff --git a/dist/esm-with-signals.js b/dist/esm-with-signals.js index ea9807a..7a5920d 100644 --- a/dist/esm-with-signals.js +++ b/dist/esm-with-signals.js @@ -1,4 +1,4 @@ -// src/signals-lib/signals-common.js +// src/signals-lib/common.js var signals_global = { isSignal(attributes) { return false; @@ -568,8 +568,36 @@ on.attributeChanged = function(listener, options) { }; }; -// src/signals-lib/signals-lib.js +// src/signals-lib/helpers.js var mark = "__dde_signal"; +var SignalDefined = class extends Error { + constructor() { + super(); + const [curr, ...rest] = this.stack.split("\n"); + const curr_file = curr.slice(curr.indexOf("@"), curr.indexOf(".js:") + 4); + this.stack = rest.find((l) => !l.includes(curr_file)); + } +}; +var queueSignalWrite = /* @__PURE__ */ (() => { + let pendingSignals = /* @__PURE__ */ new Set(); + let scheduled = false; + function flushSignals() { + scheduled = false; + for (const signal2 of pendingSignals) { + const M = signal2[mark]; + if (M) M.listeners.forEach((l) => l(M.value)); + } + pendingSignals.clear(); + } + return function(s) { + pendingSignals.add(s); + if (scheduled) return; + scheduled = true; + queueMicrotask(flushSignals); + }; +})(); + +// src/signals-lib/signals-lib.js function isSignal(candidate) { return typeof candidate === "function" && hasOwn(candidate, mark); } @@ -600,12 +628,14 @@ function signal(value, actions) { return out; } signal.action = function(s, name, ...a) { - const M = s[mark], { actions } = M; - if (!actions || !(name in actions)) + const M = s[mark]; + if (!M) return; + const { actions } = M; + if (!actions || !hasOwn(actions, name)) throw new Error(`Action "${name}" not defined. See ${mark}.actions.`); actions[name].apply(M, a); if (M.skip) return delete M.skip; - M.listeners.forEach((l) => l(M.value)); + queueSignalWrite(s); }; signal.on = function on2(s, listener, options = {}) { const { signal: as } = options; @@ -619,7 +649,6 @@ signal.symbols = { onclear: Symbol.for("Signal.onclear") }; signal.clear = function(...signals2) { - console.log("clenaup", signals2); for (const s of signals2) { const M = s[mark]; if (!M) continue; @@ -645,17 +674,15 @@ var storeMemo = /* @__PURE__ */ new WeakMap(); function memo(key, fun, cache) { if (typeof key !== "string") key = JSON.stringify(key); if (!cache) { - const key2 = scope.host(); - if (storeMemo.has(key2)) - cache = storeMemo.get(key2); + const keyStore = scope.host(); + if (storeMemo.has(keyStore)) + cache = storeMemo.get(keyStore); else { cache = {}; - storeMemo.set(key2, cache); + storeMemo.set(keyStore, cache); } } - if (!hasOwn(cache, key)) - cache[key] = fun(); - return cache[key]; + return hasOwn(cache, key) ? cache[key] : cache[key] = fun(); } signal.el = function(s, map) { const mark_start = createElement.mark({ type: "reactive" }, true); @@ -764,7 +791,6 @@ function removeSignalsFromElements(s, listener, ...notes) { }); } var cleanUpRegistry = new FinalizationRegistry(function(s) { - console.log("UNREG"); signal.clear({ [mark]: s }); }); function create(is_readonly, value, actions) { @@ -778,14 +804,6 @@ var protoSigal = Object.assign(/* @__PURE__ */ Object.create(null), { this.skip = true; } }); -var SignalDefined = class extends Error { - constructor() { - super(); - const [curr, ...rest] = this.stack.split("\n"); - const curr_file = curr.slice(curr.indexOf("@"), curr.indexOf(".js:") + 4); - this.stack = rest.find((l) => !l.includes(curr_file)); - } -}; function toSignal(s, value, actions, readonly = false) { const onclear = []; if (typeOf(actions) !== "[object Object]") @@ -826,28 +844,9 @@ function read(s) { if (deps.has(context)) deps.get(context).add(s); return value; } -var queueSignalWrite = /* @__PURE__ */ (() => { - let pendingSignals = /* @__PURE__ */ new Set(); - let scheduled = false; - function flushSignals() { - scheduled = false; - for (const signal2 of pendingSignals) { - const M = signal2[mark]; - if (M) M.listeners.forEach((l) => l(M.value)); - } - pendingSignals.clear(); - } - return function(s) { - pendingSignals.add(s); - if (scheduled) return; - scheduled = true; - queueMicrotask(flushSignals); - }; -})(); function write(s, value, force) { - if (!s[mark]) return; const M = s[mark]; - if (!force && M.value === value) return; + if (!M || !force && M.value === value) return; M.value = value; queueSignalWrite(s); return value; @@ -861,13 +860,13 @@ function removeSignalListener(s, listener, clear_when_empty) { if (!M) return; const { listeners: L } = M; const out = L.delete(listener); - if (clear_when_empty && !L.size) { - signal.clear(s); - if (!deps.has(M)) return out; - const c = deps.get(M); - if (!deps.has(c)) return out; - deps.get(c).forEach((sig) => removeSignalListener(sig, c, true)); - } + if (!out || !clear_when_empty || L.size) return out; + signal.clear(s); + const depList = deps.get(M); + if (!depList) return out; + const depSource = deps.get(depList); + if (!depSource) return out; + for (const sig of depSource) removeSignalListener(sig, depList, true); return out; } diff --git a/dist/esm.js b/dist/esm.js index 0e82012..ed86720 100644 --- a/dist/esm.js +++ b/dist/esm.js @@ -1,4 +1,4 @@ -// src/signals-lib/signals-common.js +// src/signals-lib/common.js var signals_global = { isSignal(attributes) { return false; diff --git a/signals.js b/signals.js index 366dbc8..dc6392d 100644 --- a/signals.js +++ b/signals.js @@ -1,4 +1,4 @@ export { signal, S, isSignal } from "./src/signals-lib/signals-lib.js"; import { signals_config } from "./src/signals-lib/signals-lib.js"; -import { registerReactivity } from "./src/signals-lib/signals-common.js"; +import { registerReactivity } from "./src/signals-lib/common.js"; registerReactivity(signals_config); diff --git a/src/dom.js b/src/dom.js index bea9ef3..fd4e3a1 100644 --- a/src/dom.js +++ b/src/dom.js @@ -1,4 +1,4 @@ -import { signals } from "./signals-lib/signals-common.js"; +import { signals } from "./signals-lib/common.js"; import { enviroment as env } from './dom-common.js'; //TODO: add type, docs ≡ make it public diff --git a/src/events.js b/src/events.js index 7460822..3645507 100644 --- a/src/events.js +++ b/src/events.js @@ -1,4 +1,4 @@ -export { registerReactivity } from './signals-lib/signals-common.js'; +export { registerReactivity } from './signals-lib/common.js'; import { enviroment as env, keyLTE, evc, evd, eva } from './dom-common.js'; export function dispatchEvent(name, options, host){ diff --git a/src/signals-lib/signals-common.js b/src/signals-lib/common.js similarity index 100% rename from src/signals-lib/signals-common.js rename to src/signals-lib/common.js diff --git a/src/signals-lib/helpers.js b/src/signals-lib/helpers.js new file mode 100644 index 0000000..a42f5dc --- /dev/null +++ b/src/signals-lib/helpers.js @@ -0,0 +1,29 @@ +export const mark= "__dde_signal"; + +export class SignalDefined extends Error{ + constructor(){ + super(); + const [ curr, ...rest ]= this.stack.split("\n"); + const curr_file= curr.slice(curr.indexOf("@"), curr.indexOf(".js:")+4); + this.stack= rest.find(l=> !l.includes(curr_file)); + } +} +export const queueSignalWrite= (()=> { + let pendingSignals= new Set(); + let scheduled= false; + + function flushSignals() { + scheduled = false; + for(const signal of pendingSignals){ + const M = signal[mark]; + if(M) M.listeners.forEach(l => l(M.value)); + } + pendingSignals.clear(); + } + return function(s){ + pendingSignals.add(s); + if(scheduled) return; + scheduled = true; + queueMicrotask(flushSignals); + } +})(); diff --git a/src/signals-lib/signals-lib.js b/src/signals-lib/signals-lib.js index 71aa248..fd3d49c 100644 --- a/src/signals-lib/signals-lib.js +++ b/src/signals-lib/signals-lib.js @@ -1,4 +1,5 @@ -export const mark= "__dde_signal"; +import { SignalDefined, queueSignalWrite, mark } from "./helpers.js"; +export { mark }; import { hasOwn } from "../helpers.js"; export function isSignal(candidate){ @@ -23,8 +24,7 @@ export function signal(value, actions){ const out= create(true); function contextReWatch(){ - const deps_old= deps.get(contextReWatch); - const origin= deps_old.shift(); + const [ origin, ...deps_old ]= deps.get(contextReWatch); deps.set(contextReWatch, new Set([ origin ])); stack_watch.push(contextReWatch); @@ -45,12 +45,14 @@ export function signal(value, actions){ } export { signal as S }; signal.action= function(s, name, ...a){ - const M= s[mark], { actions }= M; - if(!actions || !(name in actions)) + const M= s[mark]; + if(!M) return; + const { actions }= M; + if(!actions || !hasOwn(actions, name)) throw new Error(`Action "${name}" not defined. See ${mark}.actions.`); actions[name].apply(M, a); if(M.skip) return (delete M.skip); - M.listeners.forEach(l=> l(M.value)); + queueSignalWrite(s); }; signal.on= function on(s, listener, options= {}){ const { signal: as }= options; @@ -232,14 +234,6 @@ const protoSigal= Object.assign(Object.create(null), { this.skip= true; } }); -class SignalDefined extends Error{ - constructor(){ - super(); - const [ curr, ...rest ]= this.stack.split("\n"); - const curr_file= curr.slice(curr.indexOf("@"), curr.indexOf(".js:")+4); - this.stack= rest.find(l=> !l.includes(curr_file)); - } -} function toSignal(s, value, actions, readonly= false){ const onclear= []; if(typeOf(actions)!=="[object Object]") @@ -277,29 +271,10 @@ function read(s){ if(deps.has(context)) deps.get(context).add(s); return value; } -const queueSignalWrite= (()=> { - let pendingSignals= new Set(); - let scheduled= false; - - function flushSignals() { - scheduled = false; - for(const signal of pendingSignals){ - const M = signal[mark]; - if(M) M.listeners.forEach(l => l(M.value)); - } - pendingSignals.clear(); - } - return function(s){ - pendingSignals.add(s); - if(scheduled) return; - scheduled = true; - queueMicrotask(flushSignals); - } -})(); function write(s, value, force){ const M= s[mark]; if(!M || (!force && M.value===value)) return; - + M.value= value; queueSignalWrite(s); return value; @@ -312,7 +287,7 @@ function addSignalListener(s, listener){ function removeSignalListener(s, listener, clear_when_empty){ const M= s[mark]; if(!M) return; - + const { listeners: L }= M; const out= L.delete(listener); if(!out || !clear_when_empty || L.size) return out; @@ -320,10 +295,10 @@ function removeSignalListener(s, listener, clear_when_empty){ signal.clear(s); const depList= deps.get(M); if(!depList) return out; - + const depSource= deps.get(depList); if(!depSource) return out; - + for(const sig of depSource) removeSignalListener(sig, depList, true); return out; }