ajo 0.0.21 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs CHANGED
@@ -17,29 +17,22 @@ var __copyProps = (to, from2, except, desc) => {
17
17
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
18
  var ajo_exports = {};
19
19
  __export(ajo_exports, {
20
- For: () => For,
21
20
  Fragment: () => Fragment,
22
- cleanup: () => cleanup,
23
- clx: () => clx,
24
21
  component: () => component,
25
- consume: () => consume,
26
22
  h: () => h,
27
- intercept: () => intercept,
28
- keb: () => keb,
29
- propagate: () => propagate,
30
- provide: () => provide,
31
- refresh: () => refresh,
32
23
  render: () => render,
33
- stx: () => stx
24
+ useCallback: () => useCallback,
25
+ useCatch: () => useCatch,
26
+ useEffect: () => useEffect,
27
+ useHost: () => useHost,
28
+ useLayout: () => useLayout,
29
+ useMemo: () => useMemo,
30
+ useReducer: () => useReducer,
31
+ useRef: () => useRef,
32
+ useState: () => useState
34
33
  });
35
34
  module.exports = __toCommonJS(ajo_exports);
36
- const Fragment = ({ children }) => children, For = ({ is, key, block, each, by, children, ref, ...props }) => h(is ?? "div", {
37
- ...props,
38
- key,
39
- block,
40
- skip: true,
41
- ref: iterate.bind(null, each, by, children, ref)
42
- }), h = (nodeName, props, ...children) => {
35
+ const Fragment = ({ children }) => children, h = (nodeName, props, ...children) => {
43
36
  const { length } = children;
44
37
  children = length == 0 ? null : length == 1 ? children[0] : children;
45
38
  return { children, ...props, nodeName };
@@ -54,14 +47,17 @@ const Fragment = ({ children }) => children, For = ({ is, key, block, each, by,
54
47
  node = node.nextSibling;
55
48
  node ? node.data != h2 && (node.data = h2) : node = document.createTextNode(h2);
56
49
  } else {
57
- const { xmlns = ns, nodeName, key, block, skip, children, ref, ...props } = h2;
50
+ const { xmlns = ns, nodeName, key, ref, memo, children, [FN]: fn, ...props } = h2;
58
51
  while (node && !(node.localName == nodeName && (node.$key ??= key) == key))
59
52
  node = node.nextSibling;
60
53
  node ??= create(xmlns, nodeName, key);
61
- if (block == null || some(node.$deps, node.$deps = block)) {
54
+ if (isObject(ref)) {
55
+ ref.current = node;
56
+ node.$ref = ref;
57
+ }
58
+ if (memo == null || some(node.$memo, node.$memo = memo)) {
62
59
  update(props, node);
63
- skip || render(children, node, xmlns);
64
- isFunction(ref) && ref(node);
60
+ isFunction(fn) ? fn(node) : render(children, node, xmlns);
65
61
  }
66
62
  }
67
63
  node == child ? child = child.nextSibling : before(host, node, child);
@@ -71,55 +67,61 @@ const Fragment = ({ children }) => children, For = ({ is, key, block, each, by,
71
67
  dispose(host.removeChild(child));
72
68
  child = next;
73
69
  }
74
- }, component = (setup) => ({ is, props, key, block, ref, ...params }) => h(is ?? setup.is ?? "div", {
75
- ...setup.props,
70
+ }, component = (fn) => ({ nodeName, as, props, key, ref, memo, ...args }) => h(as ?? fn?.as ?? "c-host", {
71
+ ...fn?.props,
76
72
  ...props,
77
73
  key,
78
- block,
79
- skip: true,
80
- ref: run.bind(null, setup, params, ref)
81
- }), refresh = (host) => {
82
- try {
83
- render((host.$render ??= host.$setup(host))(host.$params), host);
84
- } catch (error) {
85
- propagate(host, error);
74
+ ref,
75
+ memo,
76
+ [FN]: (host) => {
77
+ host.$fn = isFunction(fn) ? fn : noop;
78
+ host.$args = { ...fn?.args, ...args };
79
+ schedule(host);
86
80
  }
87
- }, provide = (host, key, value) => (host.$provisions ??= /* @__PURE__ */ new Map()).set(key, value), consume = (host, key, fallback) => {
88
- for (let map2; host; host = host.parentNode)
89
- if ((map2 = host.$provisions) && map2.has(key))
90
- return map2.get(key);
91
- return fallback;
92
- }, intercept = (host, fn) => isFunction(fn) && (host.$interceptor = fn), propagate = (host, error) => {
93
- for (let fn; host; host = host.parentNode)
94
- if (isFunction(fn = host.$interceptor))
95
- return render(fn(error), host);
96
- throw error;
97
- }, cleanup = (host, fn) => isFunction(fn) && (host.$cleanups ??= /* @__PURE__ */ new Set()).add(fn), clx = (o) => keys(o).filter((k) => o[k]).join(" ") || null, stx = (o) => entries(o).map((t) => t.join(":")).join(";") || null, keb = (o) => keys(o).reduce((r, k) => (r[k.replace(search, replace).toLowerCase()] = o[k], r), {});
98
- const { isArray, from } = Array, { keys, entries } = Object, isFunction = (v) => typeof v == "function", noop = () => {
99
- }, map = (list) => list.reduce(set, /* @__PURE__ */ new Map()), set = (m, v, i) => (m.set(v, i), m), some = (a, b) => isArray(a) && isArray(b) ? a.some((v, i) => v !== b[i]) : a !== b, reduce = (v) => from(v).reduce(assign, {}), assign = (v, { name, value }) => (v[name] = value, v), create = (ns, name, key) => {
81
+ }), useReducer = (fn, init) => {
82
+ const host = useHost(), hooks = useHooks(), [i, stack] = hooks;
83
+ if (i == stack.length)
84
+ stack[i] = [
85
+ isFunction(init) ? init() : init,
86
+ (value) => {
87
+ const prev = stack[i][0], next = isFunction(value) ? value(prev) : value;
88
+ if (is(prev, stack[i][0] = isFunction(fn) ? fn(prev, next) : next))
89
+ return;
90
+ runMutations(host);
91
+ }
92
+ ];
93
+ return stack[hooks[0]++];
94
+ }, useMemo = (fn, deps) => {
95
+ const hooks = useHooks(), [i, stack] = hooks;
96
+ if (i == stack.length || deps == null || some(deps, stack[i][1]))
97
+ stack[i] = [fn(), deps];
98
+ return stack[hooks[0]++][0];
99
+ }, useCatch = (fn) => {
100
+ const host = useHost(), [value, setValue] = useReducer(), hooks = useHooks(), [i, stack] = hooks;
101
+ stack[hooks[0]++] = fn;
102
+ host.$catch ??= (value2) => {
103
+ isFunction(stack[i]) && stack[i](value2);
104
+ setValue(value2);
105
+ };
106
+ return [value, () => setValue()];
107
+ }, useHost = () => current, useState = (init) => useReducer(null, init), useRef = (current2) => useMemo(() => ({ current: current2 }), []), useCallback = (fn, deps) => useMemo(() => fn, deps), useLayout = (fn, deps) => useFx(fn, deps, "$layout"), useEffect = (fn, deps) => useFx(fn, deps, "$effect");
108
+ const microtask = globalThis.queueMicrotask ?? ((fn) => fn()), task = globalThis.requestAnimationFrame ?? microtask, { isArray, from } = Array, { is } = Object, noop = () => {
109
+ }, FN = Symbol(), isObject = (v) => v && typeof v == "object", isFunction = (v) => typeof v == "function", some = (a, b) => isArray(a) && isArray(b) ? a.some((v, i) => !is(v, b[i])) : !is(a, b), reduce = (v) => from(v).reduce(assign, {}), assign = (v, { name, value }) => (v[name] = value, v), create = (ns, name, key) => {
100
110
  const node = ns ? document.createElementNS(ns, name) : document.createElement(name);
101
111
  return node.$key = key, node;
102
- }, proxy = { firstChild: null, insertBefore: (node) => proxy.firstChild ??= node }, handler = {
103
- get(target, key) {
104
- const value = key == "nextSibling" ? null : Reflect.get(target, key);
105
- return isFunction(value) ? value.bind(target) : value;
106
- },
107
- set(target, key, value) {
108
- return Reflect.set(target, key, value);
109
- }
110
- }, search = /([a-z0-9])([A-Z])/g, replace = "$1-$2", normalize = function* (h2, buffer = { t: "" }, root = true) {
111
- let t;
112
+ }, normalize = function* (h2, buffer = { data: "" }, root = true) {
113
+ let data;
112
114
  for (h2 of isArray(h2) ? h2 : [h2]) {
113
115
  if (h2 == null || typeof h2 == "boolean")
114
116
  continue;
115
117
  if (typeof h2.nodeName == "string")
116
- (t = buffer.t) && (buffer.t = "", yield t), yield h2;
118
+ (data = buffer.data) && (buffer.data = "", yield data), yield h2;
117
119
  else if (isFunction(h2.nodeName))
118
120
  yield* normalize(h2.nodeName(h2), buffer, false);
119
121
  else
120
- isArray(h2) ? yield* normalize(h2, buffer, false) : buffer.t += h2;
122
+ isArray(h2) ? yield* normalize(h2, buffer, false) : buffer.data += h2;
121
123
  }
122
- root && (t = buffer.t) && (yield t);
124
+ root && (data = buffer.data) && (yield data);
123
125
  }, update = (props, host) => {
124
126
  const prev = host.$props ??= host.hasAttributes() ? reduce(host.attributes) : {};
125
127
  for (const name in { ...prev, ...host.$props = props }) {
@@ -142,66 +144,111 @@ const { isArray, from } = Array, { keys, entries } = Object, isFunction = (v) =>
142
144
  }
143
145
  } else
144
146
  host.insertBefore(node, child);
145
- }, iterate = (each, by, fn, ref, host) => {
146
- each = isArray(each) ? each : [];
147
- by = isFunction(by) ? by : (v) => v;
148
- fn = isFunction(fn) ? fn : noop;
149
- const map2 = host.$for ??= /* @__PURE__ */ new Map(), del = (node) => {
150
- map2.delete(node.$by);
151
- dispose(node);
152
- }, clr = each !== host.$each, len = (host.$each = each).length, a = from(host.childNodes), b = new Array(len);
153
- clr && map2.clear();
154
- for (let child, index = 0; index < len; index++) {
155
- const item = each[index], key = by(item, index);
156
- child = clr ? a[index] : map2.get(key);
157
- proxy.firstChild = child ? new Proxy(child, handler) : null;
158
- render(fn(item, index), proxy);
159
- child ??= proxy.firstChild;
160
- proxy.firstChild = null;
161
- map2.set(child.$by = key, b[index] = child);
147
+ }, useHooks = () => current.$hooks ??= [0, []], useFx = (fn, deps, key) => {
148
+ const host = useHost(), hooks = useHooks(), [i, stack] = hooks, init = i == stack.length;
149
+ if (init)
150
+ (host[key] ??= /* @__PURE__ */ new Set()).add(stack[i] = [null, fn, deps]);
151
+ else if (deps == null || some(deps, stack[i][2])) {
152
+ stack[i][1] = fn;
153
+ stack[i][2] = deps;
154
+ }
155
+ hooks[0]++;
156
+ }, schedule = (host) => {
157
+ if (host.$idle)
158
+ return;
159
+ if (globalThis.navigator?.scheduling?.isInputPending()) {
160
+ host.$idle = requestIdleCallback(() => {
161
+ host.$idle = null;
162
+ schedule(host);
163
+ });
164
+ return;
162
165
  }
163
- arrange(host, a, b, del);
164
- isFunction(ref) && ref(host);
165
- }, arrange = (host, a, b, dispose2 = noop) => {
166
- const aLen = a.length, bLen = b.length;
167
- let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i;
168
- while (aIndex !== aLen || bIndex !== bLen) {
169
- aValue = a[aIndex], bValue = b[bIndex];
170
- if (aValue === null)
171
- aIndex++;
172
- else if (bLen <= bIndex)
173
- aIndex++, dispose2(host.removeChild(aValue));
174
- else if (aLen <= aIndex)
175
- bIndex++, host.appendChild(bValue);
176
- else if (aValue === bValue)
177
- aIndex++, bIndex++;
178
- else {
179
- aMap ??= map(a), bMap ??= map(b);
180
- if (bMap.get(aValue) == null)
181
- aIndex++, dispose2(host.removeChild(aValue));
182
- else {
183
- host.insertBefore(bValue, aValue), bIndex++;
184
- if ((i = aMap.get(bValue)) != null) {
185
- if (i > aIndex + 1)
186
- aIndex++;
187
- a[i] = null;
166
+ runComponent(host);
167
+ }, runMutations = (host) => {
168
+ if (host.$queued)
169
+ return;
170
+ host.$queued = true;
171
+ microtask(() => {
172
+ host.$queued = false;
173
+ runComponent(host);
174
+ });
175
+ }, runComponent = (host) => {
176
+ if (host.$idle) {
177
+ cancelIdleCallback(host.$idle);
178
+ host.$idle = null;
179
+ }
180
+ current = host;
181
+ if (current.$hooks)
182
+ current.$hooks[0] = 0;
183
+ try {
184
+ host.$h = host.$fn(host.$args);
185
+ } catch (value) {
186
+ propagate(value, host.parentNode);
187
+ } finally {
188
+ current = null;
189
+ layoutsQueue.add(host);
190
+ layoutsId ??= task(runLayouts);
191
+ }
192
+ }, runLayouts = () => {
193
+ layoutsId = null;
194
+ for (const host of layoutsQueue) {
195
+ layoutsQueue.delete(host);
196
+ try {
197
+ render(host.$h, host);
198
+ host.$h = null;
199
+ } catch (value) {
200
+ propagate(value, host);
201
+ } finally {
202
+ runFx(host, "$layout");
203
+ effectsQueue.add(host);
204
+ effectsId ??= task(runEffects);
205
+ }
206
+ }
207
+ }, runEffects = () => {
208
+ effectsId = null;
209
+ for (const host of effectsQueue) {
210
+ effectsQueue.delete(host);
211
+ runFx(host, "$effect");
212
+ }
213
+ }, runFx = (host, key) => {
214
+ if (host[key])
215
+ for (const fx of host[key]) {
216
+ const [cleanup, setup] = fx;
217
+ if (isFunction(setup)) {
218
+ try {
219
+ if (isFunction(cleanup))
220
+ cleanup();
221
+ fx[0] = setup();
222
+ } catch (value) {
223
+ fx[0] = null;
224
+ propagate(value, host.parentNode);
225
+ } finally {
226
+ fx[1] = null;
188
227
  }
189
228
  }
190
229
  }
191
- }
192
- }, run = (setup, params, ref, host) => {
193
- host.$setup ??= isFunction(setup) ? setup : noop;
194
- host.$params = { ...setup.params, ...params };
195
- refresh(host);
196
- isFunction(ref) && ref(host);
197
230
  }, dispose = (host) => {
198
231
  if (host.nodeType != 1)
199
232
  return;
200
233
  for (const child of host.children)
201
234
  dispose(child);
202
- if ("$cleanups" in host)
203
- for (const fn of host.$cleanups) {
204
- host.$cleanups.delete(fn);
205
- fn(host);
235
+ if (host.$ref)
236
+ host.$ref.current = null;
237
+ layoutsQueue.delete(host);
238
+ effectsQueue.delete(host);
239
+ for (const key of ["$layout", "$effect"])
240
+ if (host[key]) {
241
+ for (const fx of host[key])
242
+ try {
243
+ isFunction(fx[0]) && fx[0]();
244
+ } catch {
245
+ }
246
+ host[key].clear();
206
247
  }
248
+ }, propagate = (value, host) => {
249
+ for (let fn; host; host = host.parentNode)
250
+ if (isFunction(fn = host.$catch))
251
+ return void fn(value);
252
+ throw value;
207
253
  };
254
+ let current = null, layoutsQueue = /* @__PURE__ */ new Set(), layoutsId = null, effectsQueue = /* @__PURE__ */ new Set(), effectsId = null;
package/index.js CHANGED
@@ -1,9 +1,6 @@
1
1
  export const
2
- Fragment = ({ children }) => children,
3
2
 
4
- For = ({ is, key, block, each, by, children, ref, ...props }) => h(is ?? 'div', {
5
- ...props, key, block, skip: true, ref: iterate.bind(null, each, by, children, ref)
6
- }),
3
+ Fragment = ({ children }) => children,
7
4
 
8
5
  h = (nodeName, props, ...children) => {
9
6
  const { length } = children
@@ -28,15 +25,19 @@ export const
28
25
 
29
26
  } else {
30
27
 
31
- const { xmlns = ns, nodeName, key, block, skip, children, ref, ...props } = h
28
+ const { xmlns = ns, nodeName, key, ref, memo, children, [FN]: fn, ...props } = h
32
29
 
33
30
  while (node && !(node.localName == nodeName && (node.$key ??= key) == key)) node = node.nextSibling
34
31
  node ??= create(xmlns, nodeName, key)
35
32
 
36
- if (block == null || some(node.$deps, node.$deps = block)) {
33
+ if (isObject(ref)) {
34
+ ref.current = node
35
+ node.$ref = ref
36
+ }
37
+
38
+ if (memo == null || some(node.$memo, node.$memo = memo)) {
37
39
  update(props, node)
38
- skip || render(children, node, xmlns)
39
- isFunction(ref) && ref(node)
40
+ isFunction(fn) ? fn(node) : render(children, node, xmlns)
40
41
  }
41
42
  }
42
43
 
@@ -50,80 +51,103 @@ export const
50
51
  }
51
52
  },
52
53
 
53
- component = setup => ({ is, props, key, block, ref, ...params }) => h(is ?? setup.is ?? 'div', {
54
- ...setup.props, ...props, key, block, skip: true, ref: run.bind(null, setup, params, ref)
55
- }),
54
+ component = fn => ({ nodeName, as, props, key, ref, memo, ...args }) =>
56
55
 
57
- refresh = host => {
58
- try {
59
- render((host.$render ??= host.$setup(host))(host.$params), host)
60
- } catch (error) {
61
- propagate(host, error)
62
- }
63
- },
56
+ h(as ?? fn?.as ?? 'c-host', {
57
+
58
+ ...fn?.props, ...props, key, ref, memo, [FN]: host => {
59
+
60
+ host.$fn = isFunction(fn) ? fn : noop
61
+ host.$args = { ...fn?.args, ...args }
62
+
63
+ schedule(host)
64
+ }
65
+ }),
66
+
67
+ useReducer = (fn, init) => {
68
+
69
+ const host = useHost(), hooks = useHooks(), [i, stack] = hooks
70
+
71
+ if (i == stack.length) stack[i] = [
72
+
73
+ isFunction(init) ? init() : init,
74
+
75
+ value => {
76
+
77
+ const prev = stack[i][0], next = isFunction(value) ? value(prev) : value
64
78
 
65
- provide = (host, key, value) => (host.$provisions ??= new Map).set(key, value),
79
+ if (is(prev, stack[i][0] = isFunction(fn) ? fn(prev, next) : next)) return
66
80
 
67
- consume = (host, key, fallback) => {
68
- for (let map; host; host = host.parentNode) if ((map = host.$provisions) && map.has(key)) return map.get(key)
69
- return fallback
81
+ runMutations(host)
82
+ }
83
+ ]
84
+
85
+ return stack[hooks[0]++]
70
86
  },
71
87
 
72
- intercept = (host, fn) => isFunction(fn) && (host.$interceptor = fn),
88
+ useMemo = (fn, deps) => {
89
+
90
+ const hooks = useHooks(), [i, stack] = hooks
91
+
92
+ if (i == stack.length || deps == null || some(deps, stack[i][1])) stack[i] = [fn(), deps]
73
93
 
74
- propagate = (host, error) => {
75
- for (let fn; host; host = host.parentNode) if (isFunction(fn = host.$interceptor)) return render(fn(error), host)
76
- throw error
94
+ return stack[hooks[0]++][0]
77
95
  },
78
96
 
79
- cleanup = (host, fn) => isFunction(fn) && (host.$cleanups ??= new Set).add(fn),
97
+ useCatch = fn => {
98
+
99
+ const host = useHost(), [value, setValue] = useReducer(), hooks = useHooks(), [i, stack] = hooks
100
+
101
+ stack[hooks[0]++] = fn
102
+
103
+ host.$catch ??= value => {
104
+ isFunction(stack[i]) && stack[i](value)
105
+ setValue(value)
106
+ }
107
+
108
+ return [value, () => setValue()]
109
+ },
80
110
 
81
- clx = o => keys(o).filter(k => o[k]).join(' ') || null,
111
+ useHost = () => current,
112
+ useState = init => useReducer(null, init),
82
113
 
83
- stx = o => entries(o).map(t => t.join(':')).join(';') || null,
114
+ useRef = current => useMemo(() => ({ current }), []),
115
+ useCallback = (fn, deps) => useMemo(() => fn, deps),
84
116
 
85
- keb = o => keys(o).reduce((r, k) => ((r[k.replace(search, replace).toLowerCase()] = o[k]), r), {})
117
+ useLayout = (fn, deps) => useFx(fn, deps, '$layout'),
118
+ useEffect = (fn, deps) => useFx(fn, deps, '$effect')
86
119
 
87
120
  const
88
- { isArray, from } = Array, { keys, entries } = Object,
89
121
 
90
- isFunction = v => typeof v == 'function', noop = () => { },
122
+ microtask = globalThis.queueMicrotask ?? (fn => fn()),
123
+
124
+ task = globalThis.requestAnimationFrame ?? microtask,
125
+
126
+ { isArray, from } = Array, { is } = Object, noop = () => { }, FN = Symbol(),
91
127
 
92
- map = list => list.reduce(set, new Map), set = (m, v, i) => (m.set(v, i), m),
128
+ isObject = v => v && typeof v == 'object', isFunction = v => typeof v == 'function',
93
129
 
94
- some = (a, b) => (isArray(a) && isArray(b)) ? a.some((v, i) => v !== b[i]) : a !== b,
130
+ some = (a, b) => (isArray(a) && isArray(b)) ? a.some((v, i) => !is(v, b[i])) : !is(a, b),
95
131
 
96
132
  reduce = v => from(v).reduce(assign, {}), assign = (v, { name, value }) => ((v[name] = value), v),
97
133
 
98
134
  create = (ns, name, key) => {
99
135
  const node = ns ? document.createElementNS(ns, name) : document.createElement(name)
100
- return ((node.$key = key), node)
136
+ return node.$key = key, node
101
137
  },
102
138
 
103
- proxy = { firstChild: null, insertBefore: node => proxy.firstChild ??= node }, handler = {
104
- get(target, key) {
105
- const value = key == 'nextSibling' ? null : Reflect.get(target, key)
106
- return isFunction(value) ? value.bind(target) : value
107
- },
108
- set(target, key, value) {
109
- return Reflect.set(target, key, value)
110
- }
111
- },
112
-
113
- search = /([a-z0-9])([A-Z])/g, replace = '$1-$2',
114
-
115
- normalize = function* (h, buffer = { t: '' }, root = true) {
139
+ normalize = function* (h, buffer = { data: '' }, root = true) {
116
140
 
117
- let t
141
+ let data
118
142
 
119
143
  for (h of isArray(h) ? h : [h]) {
120
144
  if (h == null || typeof h == 'boolean') continue
121
- if (typeof h.nodeName == 'string') ((t = buffer.t) && (buffer.t = '', yield t)), yield h
145
+ if (typeof h.nodeName == 'string') ((data = buffer.data) && (buffer.data = '', yield data)), yield h
122
146
  else if (isFunction(h.nodeName)) yield* normalize(h.nodeName(h), buffer, false)
123
- else isArray(h) ? yield* normalize(h, buffer, false) : buffer.t += h
147
+ else isArray(h) ? yield* normalize(h, buffer, false) : buffer.data += h
124
148
  }
125
149
 
126
- root && (t = buffer.t) && (yield t)
150
+ root && (data = buffer.data) && (yield data)
127
151
  },
128
152
 
129
153
  update = (props, host) => {
@@ -142,6 +166,7 @@ const
142
166
  },
143
167
 
144
168
  before = (host, node, child) => {
169
+
145
170
  if (node.contains?.(document.activeElement)) {
146
171
 
147
172
  const ref = node.nextSibling
@@ -155,90 +180,145 @@ const
155
180
  } else host.insertBefore(node, child)
156
181
  },
157
182
 
158
- iterate = (each, by, fn, ref, host) => {
183
+ useHooks = () => current.$hooks ??= [0, []],
184
+
185
+ useFx = (fn, deps, key) => {
159
186
 
160
- each = isArray(each) ? each : []
161
- by = isFunction(by) ? by : v => v
162
- fn = isFunction(fn) ? fn : noop
187
+ const host = useHost(), hooks = useHooks(), [i, stack] = hooks, init = i == stack.length
163
188
 
164
- const
165
- map = host.$for ??= new Map,
166
- del = node => {
167
- map.delete(node.$by)
168
- dispose(node)
169
- },
170
- clr = each !== host.$each,
171
- len = (host.$each = each).length,
172
- a = from(host.childNodes),
173
- b = new Array(len)
189
+ if (init) (host[key] ??= new Set).add(stack[i] = [null, fn, deps])
174
190
 
175
- clr && map.clear()
191
+ else if (deps == null || some(deps, stack[i][2])) {
192
+ stack[i][1] = fn
193
+ stack[i][2] = deps
194
+ }
176
195
 
177
- for (let child, index = 0; index < len; index++) {
196
+ hooks[0]++
197
+ },
178
198
 
179
- const item = each[index], key = by(item, index)
199
+ schedule = host => {
180
200
 
181
- child = (clr ? a[index] : map.get(key))
201
+ if (host.$idle) return
182
202
 
183
- proxy.firstChild = child ? new Proxy(child, handler) : null
184
- render(fn(item, index), proxy)
203
+ if (globalThis.navigator?.scheduling?.isInputPending()) {
185
204
 
186
- child ??= proxy.firstChild
187
- proxy.firstChild = null
205
+ host.$idle = requestIdleCallback(() => {
206
+ host.$idle = null
207
+ schedule(host)
208
+ })
188
209
 
189
- map.set(child.$by = key, b[index] = child)
210
+ return
190
211
  }
191
212
 
192
- arrange(host, a, b, del)
193
- isFunction(ref) && ref(host)
213
+ runComponent(host)
214
+ },
215
+
216
+ runMutations = host => {
217
+
218
+ if (host.$queued) return
219
+
220
+ host.$queued = true
221
+ microtask(() => {
222
+ host.$queued = false
223
+ runComponent(host)
224
+ })
194
225
  },
195
226
 
196
- arrange = (host, a, b, dispose = noop) => {
227
+ runComponent = host => {
197
228
 
198
- const aLen = a.length, bLen = b.length
229
+ if (host.$idle) {
230
+ cancelIdleCallback(host.$idle)
231
+ host.$idle = null
232
+ }
199
233
 
200
- let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i
234
+ current = host
201
235
 
202
- while (aIndex !== aLen || bIndex !== bLen) {
236
+ if (current.$hooks) current.$hooks[0] = 0
203
237
 
204
- aValue = a[aIndex], bValue = b[bIndex]
238
+ try {
239
+ host.$h = host.$fn(host.$args)
240
+ } catch (value) {
241
+ propagate(value, host.parentNode)
242
+ } finally {
243
+ current = null
244
+ layoutsQueue.add(host)
245
+ layoutsId ??= task(runLayouts)
246
+ }
247
+ },
205
248
 
206
- if (aValue === null) aIndex++
207
- else if (bLen <= bIndex) aIndex++, dispose(host.removeChild(aValue))
208
- else if (aLen <= aIndex) bIndex++, host.appendChild(bValue)
209
- else if (aValue === bValue) aIndex++, bIndex++
210
- else {
249
+ runLayouts = () => {
211
250
 
212
- aMap ??= map(a), bMap ??= map(b)
251
+ layoutsId = null
213
252
 
214
- if (bMap.get(aValue) == null) aIndex++, dispose(host.removeChild(aValue))
215
- else {
253
+ for (const host of layoutsQueue) {
216
254
 
217
- host.insertBefore(bValue, aValue), bIndex++
255
+ layoutsQueue.delete(host)
218
256
 
219
- if ((i = aMap.get(bValue)) != null) {
220
- if (i > aIndex + 1) aIndex++
221
- a[i] = null
222
- }
223
- }
257
+ try {
258
+ render(host.$h, host)
259
+ host.$h = null
260
+ } catch (value) {
261
+ propagate(value, host)
262
+ } finally {
263
+ runFx(host, '$layout')
264
+ effectsQueue.add(host)
265
+ effectsId ??= task(runEffects)
224
266
  }
225
267
  }
226
268
  },
227
269
 
228
- run = (setup, params, ref, host) => {
270
+ runEffects = () => {
271
+
272
+ effectsId = null
273
+
274
+ for (const host of effectsQueue) {
275
+ effectsQueue.delete(host)
276
+ runFx(host, '$effect')
277
+ }
278
+ },
279
+
280
+ runFx = (host, key) => {
229
281
 
230
- host.$setup ??= isFunction(setup) ? setup : noop
231
- host.$params = { ...setup.params, ...params }
282
+ if (host[key]) for (const fx of host[key]) {
232
283
 
233
- refresh(host)
234
- isFunction(ref) && ref(host)
284
+ const [cleanup, setup] = fx
285
+
286
+ if (isFunction(setup)) {
287
+ try {
288
+ if (isFunction(cleanup)) cleanup()
289
+ fx[0] = setup()
290
+ } catch (value) {
291
+ fx[0] = null
292
+ propagate(value, host.parentNode)
293
+ } finally {
294
+ fx[1] = null
295
+ }
296
+ }
297
+ }
235
298
  },
236
299
 
237
300
  dispose = host => {
301
+
238
302
  if (host.nodeType != 1) return
303
+
239
304
  for (const child of host.children) dispose(child)
240
- if ('$cleanups' in host) for (const fn of host.$cleanups) {
241
- host.$cleanups.delete(fn)
242
- fn(host)
305
+
306
+ if (host.$ref) host.$ref.current = null
307
+
308
+ layoutsQueue.delete(host)
309
+ effectsQueue.delete(host)
310
+
311
+ for (const key of ['$layout', '$effect']) if (host[key]) {
312
+
313
+ for (const fx of host[key]) try { isFunction(fx[0]) && fx[0]() } catch { }
314
+
315
+ host[key].clear()
243
316
  }
317
+ },
318
+
319
+ propagate = (value, host) => {
320
+ for (let fn; host; host = host.parentNode) if (isFunction(fn = host.$catch)) return void fn(value)
321
+ throw value
244
322
  }
323
+
324
+ let current = null, layoutsQueue = new Set, layoutsId = null, effectsQueue = new Set, effectsId = null
package/index.min.js CHANGED
@@ -1 +1 @@
1
- var ajo=(()=>{var x=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var L=(e,n)=>{for(var l in n)x(e,l,{get:n[l],enumerable:!0})},T=(e,n,l,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of E(n))!F.call(e,t)&&t!==l&&x(e,t,{get:()=>n[t],enumerable:!(i=B(n,t))||i.enumerable});return e};var z=e=>T(x({},"__esModule",{value:!0}),e);var re={};L(re,{For:()=>R,Fragment:()=>I,cleanup:()=>Z,clx:()=>q,component:()=>V,consume:()=>P,h:()=>b,intercept:()=>W,keb:()=>G,propagate:()=>A,provide:()=>O,refresh:()=>k,render:()=>g,stx:()=>D});const I=({children:e})=>e,R=({is:e,key:n,block:l,each:i,by:t,children:s,ref:r,...o})=>b(e??"div",{...o,key:n,block:l,skip:!0,ref:le.bind(null,i,t,s,r)}),b=(e,n,...l)=>{const{length:i}=l;return l=i==0?null:i==1?l[0]:l,{children:l,...n,nodeName:e}},g=(e,n,l)=>{let i=n.firstChild;for(e of y(e)){let t=i;if(e instanceof Node)t=e;else if(typeof e=="string"){for(;t&&t.nodeType!=3;)t=t.nextSibling;t?t.data!=e&&(t.data=e):t=document.createTextNode(e)}else{const{xmlns:s=l,nodeName:r,key:o,block:c,skip:f,children:u,ref:a,...d}=e;for(;t&&!(t.localName==r&&(t.$key??=o)==o);)t=t.nextSibling;t??=X(s,r,o),(c==null||K(t.$deps,t.$deps=c))&&(ee(d,t),f||g(u,t,s),p(a)&&a(t))}t==i?i=i.nextSibling:ne(n,t,i)}for(;i;){const t=i.nextSibling;v(n.removeChild(i)),i=t}},V=e=>({is:n,props:l,key:i,block:t,ref:s,...r})=>b(n??e.is??"div",{...e.props,...l,key:i,block:t,skip:!0,ref:te.bind(null,e,r,s)}),k=e=>{try{g((e.$render??=e.$setup(e))(e.$params),e)}catch(n){A(e,n)}},O=(e,n,l)=>(e.$provisions??=new Map).set(n,l),P=(e,n,l)=>{for(let i;e;e=e.parentNode)if((i=e.$provisions)&&i.has(n))return i.get(n);return l},W=(e,n)=>p(n)&&(e.$interceptor=n),A=(e,n)=>{for(let l;e;e=e.parentNode)if(p(l=e.$interceptor))return g(l(n),e);throw n},Z=(e,n)=>p(n)&&(e.$cleanups??=new Set).add(n),q=e=>j(e).filter(n=>e[n]).join(" ")||null,D=e=>H(e).map(n=>n.join(":")).join(";")||null,G=e=>j(e).reduce((n,l)=>(n[l.replace(_,h).toLowerCase()]=e[l],n),{}),{isArray:$,from:M}=Array,{keys:j,entries:H}=Object,p=e=>typeof e=="function",w=()=>{},S=e=>e.reduce(J,new Map),J=(e,n,l)=>(e.set(n,l),e),K=(e,n)=>$(e)&&$(n)?e.some((l,i)=>l!==n[i]):e!==n,Q=e=>M(e).reduce(U,{}),U=(e,{name:n,value:l})=>(e[n]=l,e),X=(e,n,l)=>{const i=e?document.createElementNS(e,n):document.createElement(n);return i.$key=l,i},m={firstChild:null,insertBefore:e=>m.firstChild??=e},Y={get(e,n){const l=n=="nextSibling"?null:Reflect.get(e,n);return p(l)?l.bind(e):l},set(e,n,l){return Reflect.set(e,n,l)}},_=/([a-z0-9])([A-Z])/g,h="$1-$2",y=function*(e,n={t:""},l=!0){let i;for(e of $(e)?e:[e])e==null||typeof e=="boolean"||(typeof e.nodeName=="string"?((i=n.t)&&(n.t="",yield i),yield e):p(e.nodeName)?yield*y(e.nodeName(e),n,!1):$(e)?yield*y(e,n,!1):n.t+=e);l&&(i=n.t)&&(yield i)},ee=(e,n)=>{const l=n.$props??=n.hasAttributes()?Q(n.attributes):{};for(const i in{...l,...n.$props=e}){let t=e[i];t!==l[i]&&(i.startsWith("set:")?n[i.slice(4)]=t:t==null||t===!1?n.removeAttribute(i):n.setAttribute(i,t===!0?"":t))}},ne=(e,n,l)=>{if(n.contains?.(document.activeElement)){const i=n.nextSibling;for(;l&&l!=n;){const t=l.nextSibling;e.insertBefore(l,i),l=t}}else e.insertBefore(n,l)},le=(e,n,l,i,t)=>{e=$(e)?e:[],n=p(n)?n:a=>a,l=p(l)?l:w;const s=t.$for??=new Map,r=a=>{s.delete(a.$by),v(a)},o=e!==t.$each,c=(t.$each=e).length,f=M(t.childNodes),u=new Array(c);o&&s.clear();for(let a,d=0;d<c;d++){const C=e[d],N=n(C,d);a=o?f[d]:s.get(N),m.firstChild=a?new Proxy(a,Y):null,g(l(C,d),m),a??=m.firstChild,m.firstChild=null,s.set(a.$by=N,u[d]=a)}ie(t,f,u,r),p(i)&&i(t)},ie=(e,n,l,i=w)=>{const t=n.length,s=l.length;let r=0,o=0,c,f,u,a,d;for(;r!==t||o!==s;)c=n[r],f=l[o],c===null?r++:s<=o?(r++,i(e.removeChild(c))):t<=r?(o++,e.appendChild(f)):c===f?(r++,o++):(u??=S(n),a??=S(l),a.get(c)==null?(r++,i(e.removeChild(c))):(e.insertBefore(f,c),o++,(d=u.get(f))!=null&&(d>r+1&&r++,n[d]=null)))},te=(e,n,l,i)=>{i.$setup??=p(e)?e:w,i.$params={...e.params,...n},k(i),p(l)&&l(i)},v=e=>{if(e.nodeType==1){for(const n of e.children)v(n);if("$cleanups"in e)for(const n of e.$cleanups)e.$cleanups.delete(n),n(e)}};return z(re);})();
1
+ var ajo=(()=>{var g=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var V=(e,t)=>{for(var l in t)g(e,l,{get:t[l],enumerable:!0})},W=(e,t,l,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of z(t))!P.call(e,n)&&n!==l&&g(e,n,{get:()=>t[n],enumerable:!(r=R(t,n))||r.enumerable});return e};var D=e=>W(g({},"__esModule",{value:!0}),e);var fe={};V(fe,{Fragment:()=>G,component:()=>J,h:()=>v,render:()=>x,useCallback:()=>Y,useCatch:()=>K,useEffect:()=>_,useHost:()=>m,useLayout:()=>Z,useMemo:()=>N,useReducer:()=>b,useRef:()=>X,useState:()=>U});const G=({children:e})=>e,v=(e,t,...l)=>{const{length:r}=l;return l=r==0?null:r==1?l[0]:l,{children:l,...t,nodeName:e}},x=(e,t,l)=>{let r=t.firstChild;for(e of k(e)){let n=r;if(e instanceof Node)n=e;else if(typeof e=="string"){for(;n&&n.nodeType!=3;)n=n.nextSibling;n?n.data!=e&&(n.data=e):n=document.createTextNode(e)}else{const{xmlns:a=l,nodeName:i,key:o,ref:c,memo:C,children:O,[T]:q,...Q}=e;for(;n&&!(n.localName==i&&(n.$key??=o)==o);)n=n.nextSibling;n??=re(a,i,o),te(c)&&(c.current=n,n.$ref=c),(C==null||S(n.$memo,n.$memo=C))&&(ae(Q,n),u(q)?q(n):x(O,n,a))}n==r?r=r.nextSibling:ie(t,n,r)}for(;r;){const n=r.nextSibling;B(t.removeChild(r)),r=n}},J=e=>({nodeName:t,as:l,props:r,key:n,ref:a,memo:i,...o})=>v(l??e?.as??"c-host",{...e?.props,...r,key:n,ref:a,memo:i,[T]:c=>{c.$fn=u(e)?e:ee,c.$args={...e?.args,...o},I(c)}}),b=(e,t)=>{const l=m(),r=y(),[n,a]=r;return n==a.length&&(a[n]=[u(t)?t():t,i=>{const o=a[n][0],c=u(i)?i(o):i;p(o,a[n][0]=u(e)?e(o,c):c)||oe(l)}]),a[r[0]++]},N=(e,t)=>{const l=y(),[r,n]=l;return(r==n.length||t==null||S(t,n[r][1]))&&(n[r]=[e(),t]),n[l[0]++][0]},K=e=>{const t=m(),[l,r]=b(),n=y(),[a,i]=n;return i[n[0]++]=e,t.$catch??=o=>{u(i[a])&&i[a](o),r(o)},[l,()=>r()]},m=()=>f,U=e=>b(null,e),X=e=>N(()=>({current:e}),[]),Y=(e,t)=>N(()=>e,t),Z=(e,t)=>E(e,t,"$layout"),_=(e,t)=>E(e,t,"$effect"),A=globalThis.queueMicrotask??(e=>e()),F=globalThis.requestAnimationFrame??A,{isArray:s,from:h}=Array,{is:p}=Object,ee=()=>{},T=Symbol(),te=e=>e&&typeof e=="object",u=e=>typeof e=="function",S=(e,t)=>s(e)&&s(t)?e.some((l,r)=>!p(l,t[r])):!p(e,t),ne=e=>h(e).reduce(le,{}),le=(e,{name:t,value:l})=>(e[t]=l,e),re=(e,t,l)=>{const r=e?document.createElementNS(e,t):document.createElement(t);return r.$key=l,r},k=function*(e,t={data:""},l=!0){let r;for(e of s(e)?e:[e])e==null||typeof e=="boolean"||(typeof e.nodeName=="string"?((r=t.data)&&(t.data="",yield r),yield e):u(e.nodeName)?yield*k(e.nodeName(e),t,!1):s(e)?yield*k(e,t,!1):t.data+=e);l&&(r=t.data)&&(yield r)},ae=(e,t)=>{const l=t.$props??=t.hasAttributes()?ne(t.attributes):{};for(const r in{...l,...t.$props=e}){let n=e[r];n!==l[r]&&(r.startsWith("set:")?t[r.slice(4)]=n:n==null||n===!1?t.removeAttribute(r):t.setAttribute(r,n===!0?"":n))}},ie=(e,t,l)=>{if(t.contains?.(document.activeElement)){const r=t.nextSibling;for(;l&&l!=t;){const n=l.nextSibling;e.insertBefore(l,r),l=n}}else e.insertBefore(t,l)},y=()=>f.$hooks??=[0,[]],E=(e,t,l)=>{const r=m(),n=y(),[a,i]=n;a==i.length?(r[l]??=new Set).add(i[a]=[null,e,t]):(t==null||S(t,i[a][2]))&&(i[a][1]=e,i[a][2]=t),n[0]++},I=e=>{if(!e.$idle){if(globalThis.navigator?.scheduling?.isInputPending()){e.$idle=requestIdleCallback(()=>{e.$idle=null,I(e)});return}j(e)}},oe=e=>{e.$queued||(e.$queued=!0,A(()=>{e.$queued=!1,j(e)}))},j=e=>{e.$idle&&(cancelIdleCallback(e.$idle),e.$idle=null),f=e,f.$hooks&&(f.$hooks[0]=0);try{e.$h=e.$fn(e.$args)}catch(t){w(t,e.parentNode)}finally{f=null,d.add(e),H??=F(ue)}},ue=()=>{H=null;for(const e of d){d.delete(e);try{x(e.$h,e),e.$h=null}catch(t){w(t,e)}finally{M(e,"$layout"),$.add(e),L??=F(ce)}}},ce=()=>{L=null;for(const e of $)$.delete(e),M(e,"$effect")},M=(e,t)=>{if(e[t])for(const l of e[t]){const[r,n]=l;if(u(n))try{u(r)&&r(),l[0]=n()}catch(a){l[0]=null,w(a,e.parentNode)}finally{l[1]=null}}},B=e=>{if(e.nodeType==1){for(const t of e.children)B(t);e.$ref&&(e.$ref.current=null),d.delete(e),$.delete(e);for(const t of["$layout","$effect"])if(e[t]){for(const l of e[t])try{u(l[0])&&l[0]()}catch{}e[t].clear()}}},w=(e,t)=>{for(let l;t;t=t.parentNode)if(u(l=t.$catch))return void l(e);throw e};let f=null,d=new Set,H=null,$=new Set,L=null;return D(fe);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ajo",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "description": "ajo is a JavaScript view library for building user interfaces",
5
5
  "type": "module",
6
6
  "module": "index.js",
@@ -38,7 +38,7 @@
38
38
  "homepage": "https://github.com/cristianfalcone/ajo#readme",
39
39
  "devDependencies": {
40
40
  "backdom": "^0.0.8",
41
- "esbuild": "^0.15.9",
41
+ "esbuild": "^0.15.10",
42
42
  "uvu": "^0.5.6"
43
43
  }
44
44
  }
package/readme.md CHANGED
@@ -33,49 +33,21 @@ render(<Greet name="World" />, document.body)
33
33
 
34
34
  ```jsx
35
35
  /** @jsx h */
36
- import { h, component, refresh, render } from 'ajo'
36
+ import { h, component, useState, render } from 'ajo'
37
37
 
38
- const Counter = component(host => {
38
+ const Counter = component(() => {
39
39
 
40
- let count = 0
40
+ const [count, setCount] = useState(0)
41
41
 
42
- const increment = () => {
43
- count++
44
- refresh(host)
45
- }
46
-
47
- return () =>
48
- <button set:onclick={increment}>
42
+ return (
43
+ <button set:onclick={() => setCount(count + 1)}>
49
44
  Current: {count}
50
45
  </button>
46
+ )
51
47
  })
52
48
 
53
49
  render(<Counter />, document.body)
54
50
  ```
55
51
 
56
- ## rendering lists
57
-
58
- ```jsx
59
- /** @jsx h */
60
- import { h, render, component, For } from 'ajo'
61
-
62
- const products = [
63
- { title: 'Cabbage', isFruit: false, id: 1 },
64
- { title: 'Garlic', isFruit: false, id: 2 },
65
- { title: 'Apple', isFruit: true, id: 3 },
66
- ];
67
-
68
- const ShoppingList = component(() => {
69
- const listItem = product =>
70
- <li style={stx({ color: product.isFruit ? 'magenta' : 'darkgreen' })}>
71
- {product.title}
72
- </li>
73
-
74
- return ({ products }) => <For each={products} is="ul">{listItem}</For>
75
- })
76
-
77
- render(<ShoppingList producst={products} />, document.body)
78
- ```
79
-
80
52
  ## acknowledgments
81
- ajo takes heavy inspiration from [Incremental DOM](https://github.com/google/incremental-dom) and [Crank.js](https://github.com/bikeshaving/crank)
53
+ ajo takes heavy inspiration from [Incremental DOM](https://github.com/google/incremental-dom) and [React](https://github.com/facebook/react)