ajo 0.0.21 → 0.0.23

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,72 +47,82 @@ 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;
58
- while (node && !(node.localName == nodeName && (node.$key ??= key) == key))
50
+ const { xmlns = ns, nodeName, key, ref, memo, children, [FN]: fn, ...props } = h2;
51
+ while (node && !(node.localName == nodeName && is(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.$deps, node.$deps = 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);
68
64
  }
69
65
  while (child) {
70
66
  const next = child.nextSibling;
71
- dispose(host.removeChild(child));
67
+ dispose(child);
68
+ host.removeChild(child);
72
69
  child = next;
73
70
  }
74
- }, component = (setup) => ({ is, props, key, block, ref, ...params }) => h(is ?? setup.is ?? "div", {
75
- ...setup.props,
71
+ }, component = (fn) => ({ nodeName, as, props, key, ref, memo, ...args }) => h(as ?? fn?.as ?? "c-host", {
72
+ ...fn?.props,
76
73
  ...props,
77
74
  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);
75
+ ref,
76
+ memo,
77
+ [FN]: (host) => {
78
+ host.$fn = isFunction(fn) ? fn : noop;
79
+ host.$args = { ...fn?.args, ...args };
80
+ schedule(host);
86
81
  }
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) => {
82
+ }), useReducer = (fn, init) => {
83
+ const host = useHost(), hooks = useHooks(), [i, stack] = hooks;
84
+ if (i == stack.length)
85
+ stack[i] = [
86
+ isFunction(init) ? init() : init,
87
+ (value) => {
88
+ const prev = stack[i][0], next = isFunction(value) ? value(prev) : value;
89
+ if (is(prev, stack[i][0] = isFunction(fn) ? fn(prev, next) : next))
90
+ return;
91
+ runMutations(host);
92
+ }
93
+ ];
94
+ return stack[hooks[0]++];
95
+ }, useMemo = (fn, deps) => {
96
+ const hooks = useHooks(), [i, stack] = hooks;
97
+ if (i == stack.length || deps == null || some(deps, stack[i][1]))
98
+ stack[i] = [fn(), deps];
99
+ return stack[hooks[0]++][0];
100
+ }, useCatch = (fn) => {
101
+ const host = useHost(), [value, setValue] = useReducer(), hooks = useHooks(), [i, stack] = hooks;
102
+ stack[hooks[0]++] = fn;
103
+ host.$catch ??= (value2) => {
104
+ isFunction(stack[i]) && stack[i](value2);
105
+ setValue(value2);
106
+ };
107
+ return [value, () => setValue()];
108
+ }, 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");
109
+ const { isArray, from } = Array, { is } = Object, noop = () => {
110
+ }, 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), microtask = globalThis.queueMicrotask ?? ((fn) => fn()), task = globalThis.requestAnimationFrame ?? microtask, create = (ns, name, key) => {
100
111
  const node = ns ? document.createElementNS(ns, name) : document.createElement(name);
101
112
  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;
113
+ }, normalize = function* (h2, buffer = { data: "" }, root = true) {
114
+ let data;
112
115
  for (h2 of isArray(h2) ? h2 : [h2]) {
113
116
  if (h2 == null || typeof h2 == "boolean")
114
117
  continue;
115
118
  if (typeof h2.nodeName == "string")
116
- (t = buffer.t) && (buffer.t = "", yield t), yield h2;
119
+ (data = buffer.data) && (buffer.data = "", yield data), yield h2;
117
120
  else if (isFunction(h2.nodeName))
118
121
  yield* normalize(h2.nodeName(h2), buffer, false);
119
122
  else
120
- isArray(h2) ? yield* normalize(h2, buffer, false) : buffer.t += h2;
123
+ isArray(h2) ? yield* normalize(h2, buffer, false) : buffer.data += h2;
121
124
  }
122
- root && (t = buffer.t) && (yield t);
125
+ root && (data = buffer.data) && (yield data);
123
126
  }, update = (props, host) => {
124
127
  const prev = host.$props ??= host.hasAttributes() ? reduce(host.attributes) : {};
125
128
  for (const name in { ...prev, ...host.$props = props }) {
@@ -142,66 +145,129 @@ const { isArray, from } = Array, { keys, entries } = Object, isFunction = (v) =>
142
145
  }
143
146
  } else
144
147
  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);
148
+ }, useHooks = () => current.$hooks ??= [0, []], useFx = (fn, deps, key) => {
149
+ const host = useHost(), hooks = useHooks(), [i, stack] = hooks, init = i == stack.length;
150
+ if (init)
151
+ (host[key] ??= /* @__PURE__ */ new Set()).add(stack[i] = [null, fn, deps]);
152
+ else if (deps == null || some(deps, stack[i][2])) {
153
+ stack[i][1] = fn;
154
+ stack[i][2] = deps;
155
+ }
156
+ hooks[0]++;
157
+ }, schedule = (host) => {
158
+ if (host.$idleId)
159
+ return;
160
+ if (globalThis.navigator?.scheduling?.isInputPending()) {
161
+ if (!host.$idle) {
162
+ host.$idle = true;
163
+ idleCount++;
164
+ }
165
+ host.$idleId = requestIdleCallback(() => {
166
+ host.$idleId = null;
167
+ schedule(host);
168
+ });
169
+ return;
162
170
  }
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;
171
+ runComponent(host);
172
+ }, runMutations = (host) => {
173
+ if (host.$queued)
174
+ return;
175
+ host.$queued = true;
176
+ microtask(() => {
177
+ host.$queued = false;
178
+ runComponent(host);
179
+ });
180
+ }, runComponent = (host) => {
181
+ if (host.$idleId) {
182
+ cancelIdleCallback(host.$idleId);
183
+ host.$idleId = null;
184
+ host.$idle = false;
185
+ idleCount--;
186
+ }
187
+ current = host;
188
+ if (current.$hooks)
189
+ current.$hooks[0] = 0;
190
+ try {
191
+ host.$h = host.$fn(host.$args);
192
+ } catch (value) {
193
+ propagate(value, host.parentNode);
194
+ } finally {
195
+ current = null;
196
+ layoutsQueue.add(host);
197
+ if (host.$idle) {
198
+ host.$idle = false;
199
+ if (--idleCount)
200
+ return;
201
+ }
202
+ layoutsId ??= task(runLayouts);
203
+ }
204
+ }, runLayouts = () => {
205
+ layoutsId = null;
206
+ for (const host of layoutsQueue) {
207
+ layoutsQueue.delete(host);
208
+ try {
209
+ render(host.$h, host);
210
+ host.$h = null;
211
+ } catch (value) {
212
+ propagate(value, host);
213
+ } finally {
214
+ runFx(host, "$layout");
215
+ effectsQueue.add(host);
216
+ effectsId ??= task(runEffects);
217
+ }
218
+ }
219
+ }, runEffects = () => {
220
+ effectsId = null;
221
+ for (const host of effectsQueue) {
222
+ effectsQueue.delete(host);
223
+ runFx(host, "$effect");
224
+ }
225
+ }, runFx = (host, key) => {
226
+ if (host[key])
227
+ for (const fx of host[key]) {
228
+ const [cleanup, setup] = fx;
229
+ if (isFunction(setup)) {
230
+ try {
231
+ if (isFunction(cleanup))
232
+ cleanup();
233
+ fx[0] = setup();
234
+ } catch (value) {
235
+ fx[0] = null;
236
+ propagate(value, host.parentNode);
237
+ } finally {
238
+ fx[1] = null;
188
239
  }
189
240
  }
190
241
  }
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
242
  }, dispose = (host) => {
198
243
  if (host.nodeType != 1)
199
244
  return;
200
245
  for (const child of host.children)
201
246
  dispose(child);
202
- if ("$cleanups" in host)
203
- for (const fn of host.$cleanups) {
204
- host.$cleanups.delete(fn);
205
- fn(host);
247
+ if (host.$ref)
248
+ host.$ref.current = null;
249
+ if (!host.$fn)
250
+ return;
251
+ layoutsQueue.delete(host);
252
+ effectsQueue.delete(host);
253
+ for (const key of ["$layout", "$effect"])
254
+ if (host[key]) {
255
+ for (const fx of host[key]) {
256
+ host[key].delete(fx);
257
+ try {
258
+ const [cleanup] = fx;
259
+ isFunction(cleanup) && cleanup();
260
+ } catch (value) {
261
+ propagate(value, host.parentNode);
262
+ } finally {
263
+ fx[0] = fx[1] = null;
264
+ }
265
+ }
206
266
  }
267
+ }, propagate = (value, host) => {
268
+ for (let fn; host; host = host.parentNode)
269
+ if (isFunction(fn = host.$catch))
270
+ return void fn(value);
271
+ throw value;
207
272
  };
273
+ let idleCount = 0, layoutsQueue = /* @__PURE__ */ new Set(), effectsQueue = /* @__PURE__ */ new Set(), layoutsId = null, effectsId = null, current = 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
- while (node && !(node.localName == nodeName && (node.$key ??= key) == key)) node = node.nextSibling
30
+ while (node && !(node.localName == nodeName && is(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.$deps, node.$deps = 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
 
@@ -45,85 +46,107 @@ export const
45
46
 
46
47
  while (child) {
47
48
  const next = child.nextSibling
48
- dispose(host.removeChild(child))
49
+ dispose(child)
50
+ host.removeChild(child)
49
51
  child = next
50
52
  }
51
53
  },
52
54
 
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
- }),
55
+ component = fn => ({ nodeName, as, props, key, ref, memo, ...args }) =>
56
56
 
57
- refresh = host => {
58
- try {
59
- render((host.$render ??= host.$setup(host))(host.$params), host)
60
- } catch (error) {
61
- propagate(host, error)
62
- }
63
- },
57
+ h(as ?? fn?.as ?? 'c-host', {
58
+
59
+ ...fn?.props, ...props, key, ref, memo, [FN]: host => {
60
+
61
+ host.$fn = isFunction(fn) ? fn : noop
62
+ host.$args = { ...fn?.args, ...args }
63
+
64
+ schedule(host)
65
+ }
66
+ }),
67
+
68
+ useReducer = (fn, init) => {
69
+
70
+ const host = useHost(), hooks = useHooks(), [i, stack] = hooks
71
+
72
+ if (i == stack.length) stack[i] = [
73
+
74
+ isFunction(init) ? init() : init,
75
+
76
+ value => {
77
+
78
+ const prev = stack[i][0], next = isFunction(value) ? value(prev) : value
79
+
80
+ if (is(prev, stack[i][0] = isFunction(fn) ? fn(prev, next) : next)) return
64
81
 
65
- provide = (host, key, value) => (host.$provisions ??= new Map).set(key, value),
82
+ runMutations(host)
83
+ }
84
+ ]
66
85
 
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
86
+ return stack[hooks[0]++]
70
87
  },
71
88
 
72
- intercept = (host, fn) => isFunction(fn) && (host.$interceptor = fn),
89
+ useMemo = (fn, deps) => {
90
+
91
+ const hooks = useHooks(), [i, stack] = hooks
92
+
93
+ if (i == stack.length || deps == null || some(deps, stack[i][1])) stack[i] = [fn(), deps]
73
94
 
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
95
+ return stack[hooks[0]++][0]
77
96
  },
78
97
 
79
- cleanup = (host, fn) => isFunction(fn) && (host.$cleanups ??= new Set).add(fn),
98
+ useCatch = fn => {
99
+
100
+ const host = useHost(), [value, setValue] = useReducer(), hooks = useHooks(), [i, stack] = hooks
101
+
102
+ stack[hooks[0]++] = fn
103
+
104
+ host.$catch ??= value => {
105
+ isFunction(stack[i]) && stack[i](value)
106
+ setValue(value)
107
+ }
108
+
109
+ return [value, () => setValue()]
110
+ },
80
111
 
81
- clx = o => keys(o).filter(k => o[k]).join(' ') || null,
112
+ useHost = () => current,
113
+ useState = init => useReducer(null, init),
82
114
 
83
- stx = o => entries(o).map(t => t.join(':')).join(';') || null,
115
+ useRef = current => useMemo(() => ({ current }), []),
116
+ useCallback = (fn, deps) => useMemo(() => fn, deps),
84
117
 
85
- keb = o => keys(o).reduce((r, k) => ((r[k.replace(search, replace).toLowerCase()] = o[k]), r), {})
118
+ useLayout = (fn, deps) => useFx(fn, deps, '$layout'),
119
+ useEffect = (fn, deps) => useFx(fn, deps, '$effect')
86
120
 
87
121
  const
88
- { isArray, from } = Array, { keys, entries } = Object,
89
122
 
90
- isFunction = v => typeof v == 'function', noop = () => { },
123
+ { isArray, from } = Array, { is } = Object, noop = () => { }, FN = Symbol(),
91
124
 
92
- map = list => list.reduce(set, new Map), set = (m, v, i) => (m.set(v, i), m),
125
+ isObject = v => v && typeof v == 'object', isFunction = v => typeof v == 'function',
93
126
 
94
- some = (a, b) => (isArray(a) && isArray(b)) ? a.some((v, i) => v !== b[i]) : a !== b,
127
+ some = (a, b) => (isArray(a) && isArray(b)) ? a.some((v, i) => !is(v, b[i])) : !is(a, b),
95
128
 
96
129
  reduce = v => from(v).reduce(assign, {}), assign = (v, { name, value }) => ((v[name] = value), v),
97
130
 
131
+ microtask = globalThis.queueMicrotask ?? (fn => fn()), task = globalThis.requestAnimationFrame ?? microtask,
132
+
98
133
  create = (ns, name, key) => {
99
134
  const node = ns ? document.createElementNS(ns, name) : document.createElement(name)
100
- return ((node.$key = key), node)
135
+ return node.$key = key, node
101
136
  },
102
137
 
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) {
138
+ normalize = function* (h, buffer = { data: '' }, root = true) {
116
139
 
117
- let t
140
+ let data
118
141
 
119
142
  for (h of isArray(h) ? h : [h]) {
120
143
  if (h == null || typeof h == 'boolean') continue
121
- if (typeof h.nodeName == 'string') ((t = buffer.t) && (buffer.t = '', yield t)), yield h
144
+ if (typeof h.nodeName == 'string') ((data = buffer.data) && (buffer.data = '', yield data)), yield h
122
145
  else if (isFunction(h.nodeName)) yield* normalize(h.nodeName(h), buffer, false)
123
- else isArray(h) ? yield* normalize(h, buffer, false) : buffer.t += h
146
+ else isArray(h) ? yield* normalize(h, buffer, false) : buffer.data += h
124
147
  }
125
148
 
126
- root && (t = buffer.t) && (yield t)
149
+ root && (data = buffer.data) && (yield data)
127
150
  },
128
151
 
129
152
  update = (props, host) => {
@@ -142,6 +165,7 @@ const
142
165
  },
143
166
 
144
167
  before = (host, node, child) => {
168
+
145
169
  if (node.contains?.(document.activeElement)) {
146
170
 
147
171
  const ref = node.nextSibling
@@ -155,90 +179,176 @@ const
155
179
  } else host.insertBefore(node, child)
156
180
  },
157
181
 
158
- iterate = (each, by, fn, ref, host) => {
182
+ useHooks = () => current.$hooks ??= [0, []],
183
+
184
+ useFx = (fn, deps, key) => {
159
185
 
160
- each = isArray(each) ? each : []
161
- by = isFunction(by) ? by : v => v
162
- fn = isFunction(fn) ? fn : noop
186
+ const host = useHost(), hooks = useHooks(), [i, stack] = hooks, init = i == stack.length
163
187
 
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)
188
+ if (init) (host[key] ??= new Set).add(stack[i] = [null, fn, deps])
189
+
190
+ else if (deps == null || some(deps, stack[i][2])) {
191
+ stack[i][1] = fn
192
+ stack[i][2] = deps
193
+ }
174
194
 
175
- clr && map.clear()
195
+ hooks[0]++
196
+ },
176
197
 
177
- for (let child, index = 0; index < len; index++) {
198
+ schedule = host => {
178
199
 
179
- const item = each[index], key = by(item, index)
200
+ if (host.$idleId) return
180
201
 
181
- child = (clr ? a[index] : map.get(key))
202
+ if (globalThis.navigator?.scheduling?.isInputPending()) {
182
203
 
183
- proxy.firstChild = child ? new Proxy(child, handler) : null
184
- render(fn(item, index), proxy)
204
+ if (!host.$idle) {
205
+ host.$idle = true
206
+ idleCount++
207
+ }
185
208
 
186
- child ??= proxy.firstChild
187
- proxy.firstChild = null
209
+ host.$idleId = requestIdleCallback(() => {
210
+ host.$idleId = null
211
+ schedule(host)
212
+ })
188
213
 
189
- map.set(child.$by = key, b[index] = child)
214
+ return
190
215
  }
191
216
 
192
- arrange(host, a, b, del)
193
- isFunction(ref) && ref(host)
217
+ runComponent(host)
194
218
  },
195
219
 
196
- arrange = (host, a, b, dispose = noop) => {
220
+ runMutations = host => {
197
221
 
198
- const aLen = a.length, bLen = b.length
222
+ if (host.$queued) return
199
223
 
200
- let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i
224
+ host.$queued = true
225
+ microtask(() => {
226
+ host.$queued = false
227
+ runComponent(host)
228
+ })
229
+ },
201
230
 
202
- while (aIndex !== aLen || bIndex !== bLen) {
231
+ runComponent = host => {
203
232
 
204
- aValue = a[aIndex], bValue = b[bIndex]
233
+ if (host.$idleId) {
234
+ cancelIdleCallback(host.$idleId)
235
+ host.$idleId = null
236
+ host.$idle = false
237
+ idleCount--
238
+ }
205
239
 
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 {
240
+ current = host
211
241
 
212
- aMap ??= map(a), bMap ??= map(b)
242
+ if (current.$hooks) current.$hooks[0] = 0
213
243
 
214
- if (bMap.get(aValue) == null) aIndex++, dispose(host.removeChild(aValue))
215
- else {
244
+ try {
245
+ host.$h = host.$fn(host.$args)
246
+ } catch (value) {
247
+ propagate(value, host.parentNode)
248
+ } finally {
249
+ current = null
250
+ layoutsQueue.add(host)
251
+
252
+ if (host.$idle) {
253
+ host.$idle = false
254
+ if (--idleCount) return
255
+ }
216
256
 
217
- host.insertBefore(bValue, aValue), bIndex++
257
+ layoutsId ??= task(runLayouts)
258
+ }
259
+ },
218
260
 
219
- if ((i = aMap.get(bValue)) != null) {
220
- if (i > aIndex + 1) aIndex++
221
- a[i] = null
222
- }
223
- }
261
+ runLayouts = () => {
262
+
263
+ layoutsId = null
264
+
265
+ for (const host of layoutsQueue) {
266
+
267
+ layoutsQueue.delete(host)
268
+
269
+ try {
270
+ render(host.$h, host)
271
+ host.$h = null
272
+ } catch (value) {
273
+ propagate(value, host)
274
+ } finally {
275
+ runFx(host, '$layout')
276
+ effectsQueue.add(host)
277
+ effectsId ??= task(runEffects)
224
278
  }
225
279
  }
226
280
  },
227
281
 
228
- run = (setup, params, ref, host) => {
282
+ runEffects = () => {
283
+
284
+ effectsId = null
285
+
286
+ for (const host of effectsQueue) {
287
+ effectsQueue.delete(host)
288
+ runFx(host, '$effect')
289
+ }
290
+ },
291
+
292
+ runFx = (host, key) => {
229
293
 
230
- host.$setup ??= isFunction(setup) ? setup : noop
231
- host.$params = { ...setup.params, ...params }
294
+ if (host[key]) for (const fx of host[key]) {
232
295
 
233
- refresh(host)
234
- isFunction(ref) && ref(host)
296
+ const [cleanup, setup] = fx
297
+
298
+ if (isFunction(setup)) {
299
+ try {
300
+ if (isFunction(cleanup)) cleanup()
301
+ fx[0] = setup()
302
+ } catch (value) {
303
+ fx[0] = null
304
+ propagate(value, host.parentNode)
305
+ } finally {
306
+ fx[1] = null
307
+ }
308
+ }
309
+ }
235
310
  },
236
311
 
237
312
  dispose = host => {
313
+
238
314
  if (host.nodeType != 1) return
315
+
239
316
  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)
317
+
318
+ if (host.$ref) host.$ref.current = null
319
+
320
+ if (!host.$fn) return
321
+
322
+ layoutsQueue.delete(host)
323
+ effectsQueue.delete(host)
324
+
325
+ for (const key of ['$layout', '$effect']) if (host[key]) {
326
+
327
+ for (const fx of host[key]) {
328
+
329
+ host[key].delete(fx)
330
+
331
+ try {
332
+ const [cleanup] = fx
333
+ isFunction(cleanup) && cleanup()
334
+ } catch (value) {
335
+ propagate(value, host.parentNode)
336
+ } finally {
337
+ fx[0] = fx[1] = null
338
+ }
339
+ }
243
340
  }
341
+ },
342
+
343
+ propagate = (value, host) => {
344
+ for (let fn; host; host = host.parentNode) if (isFunction(fn = host.$catch)) return void fn(value)
345
+ throw value
244
346
  }
347
+
348
+ let
349
+ idleCount = 0,
350
+ layoutsQueue = new Set,
351
+ effectsQueue = new Set,
352
+ layoutsId = null,
353
+ effectsId = null,
354
+ current = 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 k=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var W=(e,n)=>{for(var i in n)k(e,i,{get:n[i],enumerable:!0})},D=(e,n,i,l)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of P(n))!V.call(e,t)&&t!==i&&k(e,t,{get:()=>n[t],enumerable:!(l=z(n,t))||l.enumerable});return e};var G=e=>D(k({},"__esModule",{value:!0}),e);var se={};W(se,{Fragment:()=>J,component:()=>K,h:()=>q,render:()=>N,useCallback:()=>Z,useCatch:()=>U,useEffect:()=>h,useHost:()=>m,useLayout:()=>_,useMemo:()=>I,useReducer:()=>S,useRef:()=>Y,useState:()=>X});const J=({children:e})=>e,q=(e,n,...i)=>{const{length:l}=i;return i=l==0?null:l==1?i[0]:i,{children:i,...n,nodeName:e}},N=(e,n,i)=>{let l=n.firstChild;for(e of x(e)){let t=l;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:r=i,nodeName:a,key:u,ref:f,memo:C,children:Q,[A]:v,...R}=e;for(;t&&!(t.localName==a&&d(t.$key??=u,u));)t=t.nextSibling;t??=re(r,a,u),te(f)&&(f.current=t,t.$ref=f),(C==null||w(t.$deps,t.$deps=C))&&(ae(R,t),o(v)?v(t):N(Q,t,r))}t==l?l=l.nextSibling:ue(n,t,l)}for(;l;){const t=l.nextSibling;H(l),n.removeChild(l),l=t}},K=e=>({nodeName:n,as:i,props:l,key:t,ref:r,memo:a,...u})=>q(i??e?.as??"c-host",{...e?.props,...l,key:t,ref:r,memo:a,[A]:f=>{f.$fn=o(e)?e:ne,f.$args={...e?.args,...u},j(f)}}),S=(e,n)=>{const i=m(),l=p(),[t,r]=l;return t==r.length&&(r[t]=[o(n)?n():n,a=>{const u=r[t][0],f=o(a)?a(u):a;d(u,r[t][0]=o(e)?e(u,f):f)||oe(i)}]),r[l[0]++]},I=(e,n)=>{const i=p(),[l,t]=i;return(l==t.length||n==null||w(n,t[l][1]))&&(t[l]=[e(),n]),t[i[0]++][0]},U=e=>{const n=m(),[i,l]=S(),t=p(),[r,a]=t;return a[t[0]++]=e,n.$catch??=u=>{o(a[r])&&a[r](u),l(u)},[i,()=>l()]},m=()=>c,X=e=>S(null,e),Y=e=>I(()=>({current:e}),[]),Z=(e,n)=>I(()=>e,n),_=(e,n)=>E(e,n,"$layout"),h=(e,n)=>E(e,n,"$effect"),{isArray:s,from:ee}=Array,{is:d}=Object,ne=()=>{},A=Symbol(),te=e=>e&&typeof e=="object",o=e=>typeof e=="function",w=(e,n)=>s(e)&&s(n)?e.some((i,l)=>!d(i,n[l])):!d(e,n),le=e=>ee(e).reduce(ie,{}),ie=(e,{name:n,value:i})=>(e[n]=i,e),F=globalThis.queueMicrotask??(e=>e()),T=globalThis.requestAnimationFrame??F,re=(e,n,i)=>{const l=e?document.createElementNS(e,n):document.createElement(n);return l.$key=i,l},x=function*(e,n={data:""},i=!0){let l;for(e of s(e)?e:[e])e==null||typeof e=="boolean"||(typeof e.nodeName=="string"?((l=n.data)&&(n.data="",yield l),yield e):o(e.nodeName)?yield*x(e.nodeName(e),n,!1):s(e)?yield*x(e,n,!1):n.data+=e);i&&(l=n.data)&&(yield l)},ae=(e,n)=>{const i=n.$props??=n.hasAttributes()?le(n.attributes):{};for(const l in{...i,...n.$props=e}){let t=e[l];t!==i[l]&&(l.startsWith("set:")?n[l.slice(4)]=t:t==null||t===!1?n.removeAttribute(l):n.setAttribute(l,t===!0?"":t))}},ue=(e,n,i)=>{if(n.contains?.(document.activeElement)){const l=n.nextSibling;for(;i&&i!=n;){const t=i.nextSibling;e.insertBefore(i,l),i=t}}else e.insertBefore(n,i)},p=()=>c.$hooks??=[0,[]],E=(e,n,i)=>{const l=m(),t=p(),[r,a]=t;r==a.length?(l[i]??=new Set).add(a[r]=[null,e,n]):(n==null||w(n,a[r][2]))&&(a[r][1]=e,a[r][2]=n),t[0]++},j=e=>{if(!e.$idleId){if(globalThis.navigator?.scheduling?.isInputPending()){e.$idle||(e.$idle=!0,b++),e.$idleId=requestIdleCallback(()=>{e.$idleId=null,j(e)});return}M(e)}},oe=e=>{e.$queued||(e.$queued=!0,F(()=>{e.$queued=!1,M(e)}))},M=e=>{e.$idleId&&(cancelIdleCallback(e.$idleId),e.$idleId=null,e.$idle=!1,b--),c=e,c.$hooks&&(c.$hooks[0]=0);try{e.$h=e.$fn(e.$args)}catch(n){g(n,e.parentNode)}finally{if(c=null,$.add(e),e.$idle&&(e.$idle=!1,--b))return;L??=T(fe)}},fe=()=>{L=null;for(const e of $){$.delete(e);try{N(e.$h,e),e.$h=null}catch(n){g(n,e)}finally{B(e,"$layout"),y.add(e),O??=T(ce)}}},ce=()=>{O=null;for(const e of y)y.delete(e),B(e,"$effect")},B=(e,n)=>{if(e[n])for(const i of e[n]){const[l,t]=i;if(o(t))try{o(l)&&l(),i[0]=t()}catch(r){i[0]=null,g(r,e.parentNode)}finally{i[1]=null}}},H=e=>{if(e.nodeType==1){for(const n of e.children)H(n);if(e.$ref&&(e.$ref.current=null),!!e.$fn){$.delete(e),y.delete(e);for(const n of["$layout","$effect"])if(e[n])for(const i of e[n]){e[n].delete(i);try{const[l]=i;o(l)&&l()}catch(l){g(l,e.parentNode)}finally{i[0]=i[1]=null}}}}},g=(e,n)=>{for(let i;n;n=n.parentNode)if(o(i=n.$catch))return void i(e);throw e};let b=0,$=new Set,y=new Set,L=null,O=null,c=null;return G(se);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ajo",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
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)