ajo 0.0.20 → 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,27 +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, each, by, children, ref, ...props }) => h(is ?? "div", {
37
- ...props,
38
- skip: true,
39
- ref: (host) => iterate(host, each, by, children, ref)
40
- }), h = (nodeName, props, ...children) => {
35
+ const Fragment = ({ children }) => children, h = (nodeName, props, ...children) => {
41
36
  const { length } = children;
42
37
  children = length == 0 ? null : length == 1 ? children[0] : children;
43
38
  return { children, ...props, nodeName };
@@ -52,70 +47,81 @@ const Fragment = ({ children }) => children, For = ({ is, each, by, children, re
52
47
  node = node.nextSibling;
53
48
  node ? node.data != h2 && (node.data = h2) : node = document.createTextNode(h2);
54
49
  } else {
55
- const { xmlns = ns, nodeName, key, block, skip, children, ref, ...props } = h2;
50
+ const { xmlns = ns, nodeName, key, ref, memo, children, [FN]: fn, ...props } = h2;
56
51
  while (node && !(node.localName == nodeName && (node.$key ??= key) == key))
57
52
  node = node.nextSibling;
58
53
  node ??= create(xmlns, nodeName, key);
59
- 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)) {
60
59
  update(props, node);
61
- skip || render(children, node, xmlns);
62
- isFunction(ref) && ref(node);
60
+ isFunction(fn) ? fn(node) : render(children, node, xmlns);
63
61
  }
64
62
  }
65
63
  node == child ? child = child.nextSibling : before(host, node, child);
66
64
  }
67
65
  while (child) {
68
66
  const next = child.nextSibling;
69
- host.removeChild(child);
67
+ dispose(host.removeChild(child));
70
68
  child = next;
71
69
  }
72
- }, component = (setup) => ({ is, props, ref, ...params }) => h(is ?? setup.is ?? "div", {
73
- ...setup.props,
70
+ }, component = (fn) => ({ nodeName, as, props, key, ref, memo, ...args }) => h(as ?? fn?.as ?? "c-host", {
71
+ ...fn?.props,
74
72
  ...props,
75
- skip: true,
76
- ref: (host) => run(host, setup, params, ref)
77
- }), refresh = (host) => {
78
- try {
79
- render((host.$render ??= host.$setup(host))(host.$params), host);
80
- } catch (error) {
81
- propagate(host, error);
73
+ key,
74
+ ref,
75
+ memo,
76
+ [FN]: (host) => {
77
+ host.$fn = isFunction(fn) ? fn : noop;
78
+ host.$args = { ...fn?.args, ...args };
79
+ schedule(host);
82
80
  }
83
- }, provide = (host, key, value) => (host.$provisions ??= /* @__PURE__ */ new Map()).set(key, value), consume = (host, key, fallback) => {
84
- for (let map2; host; host = host.parentNode)
85
- if ((map2 = host.$provisions) && map2.has(key))
86
- return map2.get(key);
87
- return fallback;
88
- }, intercept = (host, fn) => isFunction(fn) && (host.$interceptor = fn), propagate = (host, error) => {
89
- for (let fn; host; host = host.parentNode)
90
- if (isFunction(fn = host.$interceptor))
91
- return render(fn(error), host);
92
- throw error;
93
- }, 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), {});
94
- const { isArray, from } = Array, { keys, entries } = Object, isFunction = (v) => typeof v == "function", noop = () => {
95
- }, 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) => {
96
110
  const node = ns ? document.createElementNS(ns, name) : document.createElement(name);
97
111
  return node.$key = key, node;
98
- }, proxy = { firstChild: null, insertBefore: (node) => proxy.firstChild ??= node }, handler = {
99
- get(target, key) {
100
- const value = key == "nextSibling" ? null : Reflect.get(target, key);
101
- return isFunction(value) ? value.bind(target) : value;
102
- },
103
- set(target, key, value) {
104
- return Reflect.set(target, key, value);
105
- }
106
- }, search = /([a-z0-9])([A-Z])/g, replace = "$1-$2", normalize = function* (h2, buffer = { t: "" }, root = true) {
107
- let t;
112
+ }, normalize = function* (h2, buffer = { data: "" }, root = true) {
113
+ let data;
108
114
  for (h2 of isArray(h2) ? h2 : [h2]) {
109
115
  if (h2 == null || typeof h2 == "boolean")
110
116
  continue;
111
117
  if (typeof h2.nodeName == "string")
112
- (t = buffer.t) && (buffer.t = "", yield t), yield h2;
118
+ (data = buffer.data) && (buffer.data = "", yield data), yield h2;
113
119
  else if (isFunction(h2.nodeName))
114
120
  yield* normalize(h2.nodeName(h2), buffer, false);
115
121
  else
116
- isArray(h2) ? yield* normalize(h2, buffer, false) : buffer.t += h2;
122
+ isArray(h2) ? yield* normalize(h2, buffer, false) : buffer.data += h2;
117
123
  }
118
- root && (t = buffer.t) && (yield t);
124
+ root && (data = buffer.data) && (yield data);
119
125
  }, update = (props, host) => {
120
126
  const prev = host.$props ??= host.hasAttributes() ? reduce(host.attributes) : {};
121
127
  for (const name in { ...prev, ...host.$props = props }) {
@@ -138,66 +144,111 @@ const { isArray, from } = Array, { keys, entries } = Object, isFunction = (v) =>
138
144
  }
139
145
  } else
140
146
  host.insertBefore(node, child);
141
- }, iterate = (host, each, by, fn, ref) => {
142
- each = isArray(each) ? each : [];
143
- by = isFunction(by) ? by : (v) => v;
144
- fn = isFunction(fn) ? fn : noop;
145
- const map2 = host.$for ??= /* @__PURE__ */ new Map(), del = (node) => map2.delete(node.$by), clr = each !== host.$each, len = (host.$each = each).length, a = from(host.childNodes), b = new Array(len);
146
- clr && map2.clear();
147
- for (let child, index = 0; index < len; index++) {
148
- const item = each[index], key = by(item, index);
149
- child = clr ? a[index] : map2.get(key);
150
- proxy.firstChild = child ? new Proxy(child, handler) : null;
151
- render(fn(item, index), proxy);
152
- child ??= proxy.firstChild;
153
- proxy.firstChild = null;
154
- 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;
155
154
  }
156
- arrange(host, a, b, del);
157
- isFunction(ref) && ref(host);
158
- }, arrange = (host, a, b, dispose2 = noop) => {
159
- const aLen = a.length, bLen = b.length;
160
- let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i;
161
- while (aIndex !== aLen || bIndex !== bLen) {
162
- aValue = a[aIndex], bValue = b[bIndex];
163
- if (aValue === null)
164
- aIndex++;
165
- else if (bLen <= bIndex)
166
- aIndex++, dispose2(host.removeChild(aValue));
167
- else if (aLen <= aIndex)
168
- bIndex++, host.appendChild(bValue);
169
- else if (aValue === bValue)
170
- aIndex++, bIndex++;
171
- else {
172
- aMap ??= map(a), bMap ??= map(b);
173
- if (bMap.get(aValue) == null)
174
- aIndex++, dispose2(host.removeChild(aValue));
175
- else {
176
- host.insertBefore(bValue, aValue), bIndex++;
177
- if ((i = aMap.get(bValue)) != null) {
178
- if (i > aIndex + 1)
179
- aIndex++;
180
- a[i] = null;
181
- }
182
- }
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;
165
+ }
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);
183
205
  }
184
206
  }
185
- }, run = (host, setup, params, ref) => {
186
- host.$setup ??= (host.addEventListener("DOMNodeRemovedFromDocument", dispose), isFunction(setup) ? setup : noop);
187
- host.$params = { ...setup.params, ...params };
188
- refresh(host);
189
- isFunction(ref) && ref(host);
190
- }, dispose = ({ target }) => {
191
- (globalThis.queueMicrotask ?? ((v) => v()))(() => {
192
- if (document.contains(target))
193
- return;
194
- if ("$cleanups" in target) {
195
- try {
196
- for (const fn of target.$cleanups)
197
- fn(target);
198
- } finally {
199
- target.$cleanups.clear();
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;
227
+ }
200
228
  }
201
229
  }
202
- });
230
+ }, dispose = (host) => {
231
+ if (host.nodeType != 1)
232
+ return;
233
+ for (const child of host.children)
234
+ dispose(child);
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();
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;
203
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, each, by, children, ref, ...props }) => h(is ?? 'div', {
5
- ...props, skip: true, ref: host => iterate(host, 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
 
@@ -45,53 +46,88 @@ export const
45
46
 
46
47
  while (child) {
47
48
  const next = child.nextSibling
48
- host.removeChild(child)
49
+ dispose(host.removeChild(child))
49
50
  child = next
50
51
  }
51
52
  },
52
53
 
53
- component = setup => ({ is, props, ref, ...params }) => h(is ?? setup.is ?? 'div', {
54
- ...setup.props, ...props, skip: true, ref: host => run(host, 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
 
@@ -100,30 +136,18 @@ const
100
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,92 +180,145 @@ const
155
180
  } else host.insertBefore(node, child)
156
181
  },
157
182
 
158
- iterate = (host, each, by, fn, ref) => {
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 => map.delete(node.$by),
167
- clr = each !== host.$each,
168
- len = (host.$each = each).length,
169
- a = from(host.childNodes),
170
- b = new Array(len)
189
+ if (init) (host[key] ??= new Set).add(stack[i] = [null, fn, deps])
171
190
 
172
- clr && map.clear()
191
+ else if (deps == null || some(deps, stack[i][2])) {
192
+ stack[i][1] = fn
193
+ stack[i][2] = deps
194
+ }
173
195
 
174
- for (let child, index = 0; index < len; index++) {
196
+ hooks[0]++
197
+ },
175
198
 
176
- const item = each[index], key = by(item, index)
199
+ schedule = host => {
177
200
 
178
- child = (clr ? a[index] : map.get(key))
201
+ if (host.$idle) return
179
202
 
180
- proxy.firstChild = child ? new Proxy(child, handler) : null
181
- render(fn(item, index), proxy)
203
+ if (globalThis.navigator?.scheduling?.isInputPending()) {
182
204
 
183
- child ??= proxy.firstChild
184
- proxy.firstChild = null
205
+ host.$idle = requestIdleCallback(() => {
206
+ host.$idle = null
207
+ schedule(host)
208
+ })
185
209
 
186
- map.set(child.$by = key, b[index] = child)
210
+ return
187
211
  }
188
212
 
189
- arrange(host, a, b, del)
190
- 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
+ })
191
225
  },
192
226
 
193
- arrange = (host, a, b, dispose = noop) => {
227
+ runComponent = host => {
194
228
 
195
- const aLen = a.length, bLen = b.length
229
+ if (host.$idle) {
230
+ cancelIdleCallback(host.$idle)
231
+ host.$idle = null
232
+ }
196
233
 
197
- let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i
234
+ current = host
198
235
 
199
- while (aIndex !== aLen || bIndex !== bLen) {
236
+ if (current.$hooks) current.$hooks[0] = 0
200
237
 
201
- 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
+ },
202
248
 
203
- if (aValue === null) aIndex++
204
- else if (bLen <= bIndex) aIndex++, dispose(host.removeChild(aValue))
205
- else if (aLen <= aIndex) bIndex++, host.appendChild(bValue)
206
- else if (aValue === bValue) aIndex++, bIndex++
207
- else {
249
+ runLayouts = () => {
208
250
 
209
- aMap ??= map(a), bMap ??= map(b)
251
+ layoutsId = null
210
252
 
211
- if (bMap.get(aValue) == null) aIndex++, dispose(host.removeChild(aValue))
212
- else {
253
+ for (const host of layoutsQueue) {
213
254
 
214
- host.insertBefore(bValue, aValue), bIndex++
255
+ layoutsQueue.delete(host)
215
256
 
216
- if ((i = aMap.get(bValue)) != null) {
217
- if (i > aIndex + 1) aIndex++
218
- a[i] = null
219
- }
220
- }
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)
221
266
  }
222
267
  }
223
268
  },
224
269
 
225
- run = (host, setup, params, ref) => {
270
+ runEffects = () => {
226
271
 
227
- host.$setup ??= (host.addEventListener('DOMNodeRemovedFromDocument', dispose), isFunction(setup) ? setup : noop)
228
- host.$params = { ...setup.params, ...params }
272
+ effectsId = null
229
273
 
230
- refresh(host)
231
- isFunction(ref) && ref(host)
274
+ for (const host of effectsQueue) {
275
+ effectsQueue.delete(host)
276
+ runFx(host, '$effect')
277
+ }
232
278
  },
233
279
 
234
- dispose = ({ target }) => {
235
- (globalThis.queueMicrotask ?? (v => v()))(() => {
236
- if (document.contains(target)) return
280
+ runFx = (host, key) => {
281
+
282
+ if (host[key]) for (const fx of host[key]) {
237
283
 
238
- if ('$cleanups' in target) {
284
+ const [cleanup, setup] = fx
285
+
286
+ if (isFunction(setup)) {
239
287
  try {
240
- for (const fn of target.$cleanups) fn(target)
288
+ if (isFunction(cleanup)) cleanup()
289
+ fx[0] = setup()
290
+ } catch (value) {
291
+ fx[0] = null
292
+ propagate(value, host.parentNode)
241
293
  } finally {
242
- target.$cleanups.clear()
294
+ fx[1] = null
243
295
  }
244
296
  }
245
- })
297
+ }
298
+ },
299
+
300
+ dispose = host => {
301
+
302
+ if (host.nodeType != 1) return
303
+
304
+ for (const child of host.children) dispose(child)
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()
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
246
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 y=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var F=(e,n)=>{for(var l in n)y(e,l,{get:n[l],enumerable:!0})},L=(e,n,l,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of B(n))!E.call(e,t)&&t!==l&&y(e,t,{get:()=>n[t],enumerable:!(i=j(n,t))||i.enumerable});return e};var R=e=>L(y({},"__esModule",{value:!0}),e);var re={};F(re,{For:()=>z,Fragment:()=>T,cleanup:()=>q,clx:()=>P,component:()=>D,consume:()=>O,h:()=>v,intercept:()=>V,keb:()=>Z,propagate:()=>k,provide:()=>I,refresh:()=>S,render:()=>g,stx:()=>W});const T=({children:e})=>e,z=({is:e,each:n,by:l,children:i,ref:t,...o})=>v(e??"div",{...o,skip:!0,ref:r=>ne(r,n,l,i,t)}),v=(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 x(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:o=l,nodeName:r,key:s,block:c,skip:u,children:f,ref:a,...d}=e;for(;t&&!(t.localName==r&&(t.$key??=s)==s);)t=t.nextSibling;t??=U(o,r,s),(c==null||J(t.$deps,t.$deps=c))&&(h(d,t),u||g(f,t,o),p(a)&&a(t))}t==i?i=i.nextSibling:ee(n,t,i)}for(;i;){const t=i.nextSibling;n.removeChild(i),i=t}},D=e=>({is:n,props:l,ref:i,...t})=>v(n??e.is??"div",{...e.props,...l,skip:!0,ref:o=>ie(o,e,t,i)}),S=e=>{try{g((e.$render??=e.$setup(e))(e.$params),e)}catch(n){k(e,n)}},I=(e,n,l)=>(e.$provisions??=new Map).set(n,l),O=(e,n,l)=>{for(let i;e;e=e.parentNode)if((i=e.$provisions)&&i.has(n))return i.get(n);return l},V=(e,n)=>p(n)&&(e.$interceptor=n),k=(e,n)=>{for(let l;e;e=e.parentNode)if(p(l=e.$interceptor))return g(l(n),e);throw n},q=(e,n)=>p(n)&&(e.$cleanups??=new Set).add(n),P=e=>M(e).filter(n=>e[n]).join(" ")||null,W=e=>G(e).map(n=>n.join(":")).join(";")||null,Z=e=>M(e).reduce((n,l)=>(n[l.replace(Y,_).toLowerCase()]=e[l],n),{}),{isArray:$,from:A}=Array,{keys:M,entries:G}=Object,p=e=>typeof e=="function",b=()=>{},C=e=>e.reduce(H,new Map),H=(e,n,l)=>(e.set(n,l),e),J=(e,n)=>$(e)&&$(n)?e.some((l,i)=>l!==n[i]):e!==n,K=e=>A(e).reduce(Q,{}),Q=(e,{name:n,value:l})=>(e[n]=l,e),U=(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},X={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)}},Y=/([a-z0-9])([A-Z])/g,_="$1-$2",x=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*x(e.nodeName(e),n,!1):$(e)?yield*x(e,n,!1):n.t+=e);l&&(i=n.t)&&(yield i)},h=(e,n)=>{const l=n.$props??=n.hasAttributes()?K(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))}},ee=(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)},ne=(e,n,l,i,t)=>{n=$(n)?n:[],l=p(l)?l:a=>a,i=p(i)?i:b;const o=e.$for??=new Map,r=a=>o.delete(a.$by),s=n!==e.$each,c=(e.$each=n).length,u=A(e.childNodes),f=new Array(c);s&&o.clear();for(let a,d=0;d<c;d++){const w=n[d],N=l(w,d);a=s?u[d]:o.get(N),m.firstChild=a?new Proxy(a,X):null,g(i(w,d),m),a??=m.firstChild,m.firstChild=null,o.set(a.$by=N,f[d]=a)}le(e,u,f,r),p(t)&&t(e)},le=(e,n,l,i=b)=>{const t=n.length,o=l.length;let r=0,s=0,c,u,f,a,d;for(;r!==t||s!==o;)c=n[r],u=l[s],c===null?r++:o<=s?(r++,i(e.removeChild(c))):t<=r?(s++,e.appendChild(u)):c===u?(r++,s++):(f??=C(n),a??=C(l),a.get(c)==null?(r++,i(e.removeChild(c))):(e.insertBefore(u,c),s++,(d=f.get(u))!=null&&(d>r+1&&r++,n[d]=null)))},ie=(e,n,l,i)=>{e.$setup??=(e.addEventListener("DOMNodeRemovedFromDocument",te),p(n)?n:b),e.$params={...n.params,...l},S(e),p(i)&&i(e)},te=({target:e})=>{(globalThis.queueMicrotask??(n=>n()))(()=>{if(!document.contains(e)&&"$cleanups"in e)try{for(const n of e.$cleanups)n(e)}finally{e.$cleanups.clear()}})};return R(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.20",
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)