@3sln/dodo 0.0.3 → 0.0.4

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/dodo.js CHANGED
@@ -39,22 +39,25 @@ function defaultFlattenSeq(items) {
39
39
  }
40
40
 
41
41
  function isIterable(x) {
42
- return Array.isArray(x) || (x != null && typeof x[Symbol.iterator] === 'function' && typeof x !== 'string');
42
+ return (
43
+ Array.isArray(x) ||
44
+ (x != null && typeof x[Symbol.iterator] === 'function' && typeof x !== 'string')
45
+ );
43
46
  }
44
47
 
45
48
  export const settings = {
46
49
  shouldUpdate: defaultShouldUpdate,
47
- isMap: (x) => x?.constructor === Object,
48
- mapIter: (m) => Object.entries(m),
50
+ isMap: x => x?.constructor === Object,
51
+ mapIter: m => Object.entries(m),
49
52
  mapGet: (m, k) => m[k],
50
53
  isSeq: isIterable,
51
54
  flattenSeq: defaultFlattenSeq,
52
- seqIter: (s) => s,
53
- convertTagName: (t) => t,
54
- convertPropName: (p) => p,
55
- convertStyleName: (s) => s,
56
- convertDataName: (d) => d,
57
- convertClassName: (c) => c,
55
+ seqIter: s => s,
56
+ convertTagName: t => t,
57
+ convertPropName: p => p,
58
+ convertStyleName: s => s,
59
+ convertDataName: d => d,
60
+ convertClassName: c => c,
58
61
  };
59
62
 
60
63
  function flattenVNodeChildrenIntoArray(array, children) {
@@ -107,7 +110,10 @@ export function h(tag, props, ...children) {
107
110
  children.unshift(props);
108
111
  props = EMPTY_OBJECT;
109
112
  }
110
- return new VNode(ELEMENT_NODE, settings.convertTagName(tag), [props ?? EMPTY_OBJECT, ...children]);
113
+ return new VNode(ELEMENT_NODE, settings.convertTagName(tag), [
114
+ props ?? EMPTY_OBJECT,
115
+ ...children,
116
+ ]);
111
117
  }
112
118
 
113
119
  export function o(tag, props) {
@@ -293,7 +299,10 @@ function reconcileListeners(target, hooks) {
293
299
  target.addEventListener(name, listener);
294
300
  break;
295
301
  case 'object':
296
- target.addEventListener(name, listener.listener, { capture: listener.capture, passive: listener.passive });
302
+ target.addEventListener(name, listener.listener, {
303
+ capture: listener.capture,
304
+ passive: listener.passive,
305
+ });
297
306
  break;
298
307
  }
299
308
  }
@@ -321,7 +330,10 @@ function reconcileListeners(target, hooks) {
321
330
  target.addEventListener(name, listener);
322
331
  break;
323
332
  case 'object':
324
- target.addEventListener(name, listener.listener, { capture: listener.capture, passive: listener.passive });
333
+ target.addEventListener(name, listener.listener, {
334
+ capture: listener.capture,
335
+ passive: listener.passive,
336
+ });
325
337
  break;
326
338
  }
327
339
  }
@@ -345,8 +357,10 @@ function reconcileListeners(target, hooks) {
345
357
  function newElementNamespace(parentNode, newNodeTag) {
346
358
  if (parentNode.namespaceURI === 'http://www.w3.org/1999/xhtml') {
347
359
  switch (newNodeTag) {
348
- case 'svg': return 'http://www.w3.org/2000/svg';
349
- case 'math': return 'http://www.w3.org/1998/Math/MathML';
360
+ case 'svg':
361
+ return 'http://www.w3.org/2000/svg';
362
+ case 'math':
363
+ return 'http://www.w3.org/1998/Math/MathML';
350
364
  }
351
365
  }
352
366
  return parentNode.namespaceURI ?? parentNode.host?.namespaceURI ?? 'http://www.w3.org/1999/xhtml';
@@ -404,7 +418,7 @@ function reconcileNode(target) {
404
418
 
405
419
  function createElementNode(parentNode, tag, vdom) {
406
420
  const el = parentNode.ownerDocument.createElementNS(newElementNamespace(parentNode, tag), tag);
407
- el[NODE_STATE] = { originalProps: {}, newVdom: vdom };
421
+ el[NODE_STATE] = {originalProps: {}, newVdom: vdom};
408
422
  return el;
409
423
  }
410
424
 
@@ -466,10 +480,10 @@ function removePathFromFocusWithinSet(set, newPath) {
466
480
 
467
481
  function installFocusTrackingForDocument(doc) {
468
482
  const focusWithinSet = new Set();
469
- doc.addEventListener('focusin', (event) => {
483
+ doc.addEventListener('focusin', event => {
470
484
  addPathToFocusWithinSet(focusWithinSet, event.composedPath());
471
485
  });
472
- doc.addEventListener('focusout', (event) => {
486
+ doc.addEventListener('focusout', event => {
473
487
  const newPath = event.relatedTarget ? event.composedPath() : [];
474
488
  removePathFromFocusWithinSet(focusWithinSet, newPath);
475
489
  });
@@ -524,7 +538,7 @@ function reconcileElementChildren(target, childrenIterable) {
524
538
 
525
539
  let oldNodesPoolForTag = oldVNodeNodesPool.get(vdom.tag);
526
540
  if (!oldNodesPoolForTag) {
527
- oldNodesPoolForTag = { nodesForKey: new Map(), nodesWithoutKey: [] };
541
+ oldNodesPoolForTag = {nodesForKey: new Map(), nodesWithoutKey: []};
528
542
  oldVNodeNodesPool.set(vdom.tag, oldNodesPoolForTag);
529
543
  }
530
544
 
@@ -597,7 +611,11 @@ function reconcileElementChildren(target, childrenIterable) {
597
611
  const newChild = newDomChildren[i];
598
612
  const existingChildAtPosition = target.childNodes[i];
599
613
  if (newChild !== existingChildAtPosition) {
600
- (newChild.isConnected ? moveBefore : insertBefore).call(target, newChild, existingChildAtPosition);
614
+ (newChild.isConnected ? moveBefore : insertBefore).call(
615
+ target,
616
+ newChild,
617
+ existingChildAtPosition,
618
+ );
601
619
  }
602
620
  const state = newChild[NODE_STATE];
603
621
  if (state?.newVdom) {
@@ -673,13 +691,13 @@ export function reconcile(target, vdom) {
673
691
  switch (vdom.type) {
674
692
  case ELEMENT_NODE:
675
693
  case OPAQUE_NODE:
676
- if (0 !== target.nodeName.localeCompare(vdom.tag, undefined, { sensitivity: 'base' })) {
694
+ if (0 !== target.nodeName.localeCompare(vdom.tag, undefined, {sensitivity: 'base'})) {
677
695
  throw new Error('incompatible target for vdom');
678
696
  }
679
697
  break;
680
698
  }
681
699
 
682
- target[NODE_STATE] = state = { originalProps: {}, newVdom: vdom };
700
+ target[NODE_STATE] = state = {originalProps: {}, newVdom: vdom};
683
701
  try {
684
702
  vdom.hooks?.$attach?.(target);
685
703
  if (vdom.type === SPECIAL_NODE) {
package/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import vdom from './src/vdom.js';
2
2
 
3
3
  // Create a default API instance by calling the factory with no settings.
4
- const { h, alias, special, reconcile, settings } = vdom();
4
+ const {h, alias, special, reconcile, settings} = vdom();
5
5
 
6
- export { vdom, h, alias, special, reconcile, settings };
6
+ export {vdom, h, alias, special, reconcile, settings};
7
7
 
8
8
  // Export all the HTML helpers.
9
9
  export * from './src/html.js';
10
- export { schedule, flush, clear } from './src/scheduler.js';
10
+ export {schedule, flush, clear} from './src/scheduler.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3sln/dodo",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "A minimal, configurable virtual DOM library.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -23,9 +23,13 @@
23
23
  ],
24
24
  "devDependencies": {
25
25
  "@types/bun": "latest",
26
- "happy-dom": "^18.0.1"
26
+ "happy-dom": "^18.0.1",
27
+ "prettier": "^3.6.2"
27
28
  },
28
29
  "publishConfig": {
29
30
  "access": "public"
31
+ },
32
+ "scripts": {
33
+ "format": "prettier -w \"**/*.js\""
30
34
  }
31
35
  }
package/src/html.js CHANGED
@@ -1,9 +1,9 @@
1
- import { h } from '../index.js';
1
+ import {h} from '../index.js';
2
2
 
3
3
  // Document metadata
4
4
  export const title = (...args) => h('title', ...args);
5
- export const meta = (props) => h('meta', props);
6
- export const link = (props) => h('link', props);
5
+ export const meta = props => h('meta', props);
6
+ export const link = props => h('link', props);
7
7
  export const style = (...args) => h('style', ...args);
8
8
  export const script = (...args) => h('script', ...args);
9
9
  export const noscript = (...args) => h('noscript', ...args);
@@ -67,21 +67,21 @@ export const u = (...args) => h('u', ...args);
67
67
  export const wbr = () => h('wbr');
68
68
 
69
69
  // Image and multimedia
70
- export const area = (props) => h('area', props);
70
+ export const area = props => h('area', props);
71
71
  export const audio = (...args) => h('audio', ...args);
72
- export const img = (props) => h('img', props);
72
+ export const img = props => h('img', props);
73
73
  export const map = (...args) => h('map', ...args);
74
- export const track = (props) => h('track', props);
74
+ export const track = props => h('track', props);
75
75
  export const video = (...args) => h('video', ...args);
76
76
 
77
77
  // Embedded content
78
- export const embed = (props) => h('embed', props);
78
+ export const embed = props => h('embed', props);
79
79
  export const iframe = (...args) => h('iframe', ...args);
80
80
  export const object = (...args) => h('object', ...args);
81
- export const param = (props) => h('param', props);
81
+ export const param = props => h('param', props);
82
82
  export const picture = (...args) => h('picture', ...args);
83
83
  export const portal = (...args) => h('portal', ...args);
84
- export const source = (props) => h('source', props);
84
+ export const source = props => h('source', props);
85
85
 
86
86
  // SVG and MathML
87
87
  export const svg = (...args) => h('svg', ...args);
@@ -92,7 +92,7 @@ export const canvas = (...args) => h('canvas', ...args);
92
92
 
93
93
  // Table content
94
94
  export const caption = (...args) => h('caption', ...args);
95
- export const col = (props) => h('col', props);
95
+ export const col = props => h('col', props);
96
96
  export const colgroup = (...args) => h('colgroup', ...args);
97
97
  export const table = (...args) => h('table', ...args);
98
98
  export const tbody = (...args) => h('tbody', ...args);
@@ -107,7 +107,7 @@ export const button = (...args) => h('button', ...args);
107
107
  export const datalist = (...args) => h('datalist', ...args);
108
108
  export const fieldset = (...args) => h('fieldset', ...args);
109
109
  export const form = (...args) => h('form', ...args);
110
- export const input = (props) => h('input', props);
110
+ export const input = props => h('input', props);
111
111
  export const label = (...args) => h('label', ...args);
112
112
  export const legend = (...args) => h('legend', ...args);
113
113
  export const meter = (...args) => h('meter', ...args);
package/src/scheduler.js CHANGED
@@ -6,74 +6,74 @@ const FRAME_BUDGET = 10; // ms
6
6
  const CHUNK_SIZE = 100;
7
7
 
8
8
  function _runTasks(tasks) {
9
- for (const f of tasks) {
10
- try {
11
- f();
12
- } catch (err) {
13
- console.error("Error in scheduled function:", err);
14
- }
9
+ for (const f of tasks) {
10
+ try {
11
+ f();
12
+ } catch (err) {
13
+ console.error('Error in scheduled function:', err);
15
14
  }
15
+ }
16
16
  }
17
17
 
18
18
  function runQueue() {
19
- const startTime = performance.now();
20
- frameId = 0;
19
+ const startTime = performance.now();
20
+ frameId = 0;
21
21
 
22
- while (queue.length > 0) {
23
- const chunk = queue.splice(0, CHUNK_SIZE);
24
- _runTasks(chunk);
22
+ while (queue.length > 0) {
23
+ const chunk = queue.splice(0, CHUNK_SIZE);
24
+ _runTasks(chunk);
25
25
 
26
- if (performance.now() - startTime > FRAME_BUDGET && queue.length > 0) {
27
- frameId = requestAnimationFrame(runQueue);
28
- return;
29
- }
26
+ if (performance.now() - startTime > FRAME_BUDGET && queue.length > 0) {
27
+ frameId = requestAnimationFrame(runQueue);
28
+ return;
30
29
  }
30
+ }
31
31
 
32
- scheduled = false;
32
+ scheduled = false;
33
33
  }
34
34
 
35
35
  // Schedules a function to be executed on the next animation frame.
36
- export function schedule(f, { signal } = {}) {
37
- if (signal?.aborted) {
38
- return;
39
- }
36
+ export function schedule(f, {signal} = {}) {
37
+ if (signal?.aborted) {
38
+ return;
39
+ }
40
40
 
41
- let task = f;
42
- // If a signal is provided, wrap the task to check for abortion before execution.
43
- if (signal) {
44
- task = () => {
45
- if (!signal.aborted) {
46
- f();
47
- }
48
- };
49
- }
41
+ let task = f;
42
+ // If a signal is provided, wrap the task to check for abortion before execution.
43
+ if (signal) {
44
+ task = () => {
45
+ if (!signal.aborted) {
46
+ f();
47
+ }
48
+ };
49
+ }
50
50
 
51
- queue.push(task);
51
+ queue.push(task);
52
52
 
53
- if (!scheduled) {
54
- scheduled = true;
55
- frameId = requestAnimationFrame(runQueue);
56
- }
53
+ if (!scheduled) {
54
+ scheduled = true;
55
+ frameId = requestAnimationFrame(runQueue);
56
+ }
57
57
  }
58
58
 
59
59
  // Immediately runs all queued tasks synchronously.
60
60
  export function flush() {
61
- if (frameId) {
62
- cancelAnimationFrame(frameId);
63
- }
64
- const toRun = queue;
65
- queue = [];
66
- _runTasks(toRun);
67
- scheduled = false;
68
- frameId = 0;
61
+ if (frameId) {
62
+ cancelAnimationFrame(frameId);
63
+ }
64
+ const toRun = queue;
65
+ queue = [];
66
+ _runTasks(toRun);
67
+ scheduled = false;
68
+ frameId = 0;
69
69
  }
70
70
 
71
71
  // Clears all pending tasks.
72
72
  export function clear() {
73
- if (frameId) {
74
- cancelAnimationFrame(frameId);
75
- }
76
- queue = [];
77
- scheduled = false;
78
- frameId = 0;
73
+ if (frameId) {
74
+ cancelAnimationFrame(frameId);
75
+ }
76
+ queue = [];
77
+ scheduled = false;
78
+ frameId = 0;
79
79
  }
package/src/vdom.js CHANGED
@@ -32,32 +32,35 @@ class VNode {
32
32
  }
33
33
 
34
34
  function defaultShouldUpdate(a, b) {
35
- if (a === b) return false;
36
- if (a && b && typeof a === 'object' && typeof b === 'object') {
37
- if (a.constructor !== b.constructor) return true;
38
-
39
- if (Array.isArray(a)) {
40
- if (a.length !== b.length) return true;
41
- for (let j = 0; j < a.length; j++) {
42
- if (a[j] !== b[j]) return true;
43
- }
44
- return false;
45
- }
35
+ if (a === b) return false;
36
+ if (a && b && typeof a === 'object' && typeof b === 'object') {
37
+ if (a.constructor !== b.constructor) return true;
38
+
39
+ if (Array.isArray(a)) {
40
+ if (a.length !== b.length) return true;
41
+ for (let j = 0; j < a.length; j++) {
42
+ if (a[j] !== b[j]) return true;
43
+ }
44
+ return false;
45
+ }
46
46
 
47
- if (a.constructor === Object) {
48
- const keysA = Object.keys(a);
49
- if (keysA.length !== Object.keys(b).length) return true;
50
- for (const key of keysA) {
51
- if (!b.hasOwnProperty(key) || a[key] !== b[key]) return true;
52
- }
53
- return false;
54
- }
47
+ if (a.constructor === Object) {
48
+ const keysA = Object.keys(a);
49
+ if (keysA.length !== Object.keys(b).length) return true;
50
+ for (const key of keysA) {
51
+ if (!b.hasOwnProperty(key) || a[key] !== b[key]) return true;
52
+ }
53
+ return false;
55
54
  }
56
- return true;
55
+ }
56
+ return true;
57
57
  }
58
58
 
59
59
  function isIterable(x) {
60
- return Array.isArray(x) || (x != null && typeof x[Symbol.iterator] === 'function' && typeof x !== 'string');
60
+ return (
61
+ Array.isArray(x) ||
62
+ (x != null && typeof x[Symbol.iterator] === 'function' && typeof x !== 'string')
63
+ );
61
64
  }
62
65
 
63
66
  function alias(f) {
@@ -71,8 +74,10 @@ function special(o) {
71
74
  function newElementNamespace(parentNode, newNodeTag) {
72
75
  if (parentNode.namespaceURI === 'http://www.w3.org/1999/xhtml') {
73
76
  switch (newNodeTag) {
74
- case 'svg': return 'http://www.w3.org/2000/svg';
75
- case 'math': return 'http://www.w3.org/1998/Math/MathML';
77
+ case 'svg':
78
+ return 'http://www.w3.org/2000/svg';
79
+ case 'math':
80
+ return 'http://www.w3.org/1998/Math/MathML';
76
81
  }
77
82
  }
78
83
  return parentNode.namespaceURI ?? parentNode.host?.namespaceURI ?? 'http://www.w3.org/1999/xhtml';
@@ -80,7 +85,7 @@ function newElementNamespace(parentNode, newNodeTag) {
80
85
 
81
86
  function createElementNode(parentNode, tag, vdom) {
82
87
  const el = parentNode.ownerDocument.createElementNS(newElementNamespace(parentNode, tag), tag);
83
- el[NODE_STATE] = { originalProps: {}, newVdom: vdom };
88
+ el[NODE_STATE] = {originalProps: {}, newVdom: vdom};
84
89
  return el;
85
90
  }
86
91
 
@@ -117,10 +122,10 @@ function removePathFromFocusWithinSet(set, newPath) {
117
122
 
118
123
  function installFocusTrackingForDocument(doc) {
119
124
  const focusWithinSet = new Set();
120
- doc.addEventListener('focusin', (event) => {
125
+ doc.addEventListener('focusin', event => {
121
126
  addPathToFocusWithinSet(focusWithinSet, event.composedPath());
122
127
  });
123
- doc.addEventListener('focusout', (event) => {
128
+ doc.addEventListener('focusout', event => {
124
129
  const newPath = event.relatedTarget ? event.composedPath() : [];
125
130
  removePathFromFocusWithinSet(focusWithinSet, newPath);
126
131
  });
@@ -129,21 +134,26 @@ function installFocusTrackingForDocument(doc) {
129
134
  return focusWithinSet;
130
135
  }
131
136
 
132
- export default (userSettings) => {
137
+ export default userSettings => {
133
138
  const shouldUpdate = userSettings?.shouldUpdate ?? defaultShouldUpdate;
134
- const isMap = userSettings?.isMap ?? ((x) => x?.constructor === Object);
135
- const mapIter = userSettings?.mapIter ?? ((m) => Object.entries(m));
139
+ const isMap = userSettings?.isMap ?? (x => x?.constructor === Object);
140
+ const mapIter = userSettings?.mapIter ?? (m => Object.entries(m));
136
141
  const mapGet = userSettings?.mapGet ?? ((m, k) => m[k]);
137
142
  const mapMerge = userSettings?.mapMerge ?? ((...maps) => Object.assign({}, ...maps));
138
- const newMap = userSettings?.newMap ?? ((obj) => ({...obj}));
139
- const mapPut = userSettings?.mapPut ?? ((m, k, v) => { m[k] = v; return m; });
143
+ const newMap = userSettings?.newMap ?? (obj => ({...obj}));
144
+ const mapPut =
145
+ userSettings?.mapPut ??
146
+ ((m, k, v) => {
147
+ m[k] = v;
148
+ return m;
149
+ });
140
150
  const isSeq = userSettings?.isSeq ?? isIterable;
141
- const seqIter = userSettings?.seqIter ?? ((s) => s);
142
- const convertTagName = userSettings?.convertTagName ?? ((t) => t);
143
- const convertPropName = userSettings?.convertPropName ?? ((p) => p);
144
- const convertStyleName = userSettings?.convertStyleName ?? ((s) => s);
145
- const convertDataName = userSettings?.convertDataName ?? ((d) => d);
146
- const convertClassName = userSettings?.convertClassName ?? ((c) => c);
151
+ const seqIter = userSettings?.seqIter ?? (s => s);
152
+ const convertTagName = userSettings?.convertTagName ?? (t => t);
153
+ const convertPropName = userSettings?.convertPropName ?? (p => p);
154
+ const convertStyleName = userSettings?.convertStyleName ?? (s => s);
155
+ const convertDataName = userSettings?.convertDataName ?? (d => d);
156
+ const convertClassName = userSettings?.convertClassName ?? (c => c);
147
157
  const listenerKey = userSettings?.listenerKey ?? 'listener';
148
158
  const captureKey = userSettings?.captureKey ?? 'capture';
149
159
  const passiveKey = userSettings?.passiveKey ?? 'passive';
@@ -244,86 +254,86 @@ export default (userSettings) => {
244
254
 
245
255
  // Handle new and changed props
246
256
  for (const [name, newValue] of mapIter(props)) {
247
- const propName = convertPropName(name);
248
- const oldValue = mapGet(oldProps, name);
257
+ const propName = convertPropName(name);
258
+ const oldValue = mapGet(oldProps, name);
249
259
 
250
- if (Object.is(newValue, oldValue)) continue;
260
+ if (Object.is(newValue, oldValue)) continue;
251
261
 
252
- switch (propName) {
253
- case '$styling': {
254
- if (!isMap(newValue)) throw new Error('invalid value for styling prop');
255
- reconcileElementStyling(target, oldValue ?? EMPTY_OBJECT, newValue ?? EMPTY_OBJECT);
256
- break;
257
- }
258
- case '$classes': {
259
- if (!isSeq(newValue)) throw new Error('invalid value for classes prop');
260
- reconcileElementClasses(target, oldValue ?? [], newValue ?? []);
261
- break;
262
- }
263
- case '$attrs': {
264
- if (!isMap(newValue)) throw new Error('invalid value for attrs prop');
265
- reconcileElementAttributes(target, oldValue ?? EMPTY_OBJECT, newValue ?? EMPTY_OBJECT);
266
- break;
262
+ switch (propName) {
263
+ case '$styling': {
264
+ if (!isMap(newValue)) throw new Error('invalid value for styling prop');
265
+ reconcileElementStyling(target, oldValue ?? EMPTY_OBJECT, newValue ?? EMPTY_OBJECT);
266
+ break;
267
+ }
268
+ case '$classes': {
269
+ if (!isSeq(newValue)) throw new Error('invalid value for classes prop');
270
+ reconcileElementClasses(target, oldValue ?? [], newValue ?? []);
271
+ break;
272
+ }
273
+ case '$attrs': {
274
+ if (!isMap(newValue)) throw new Error('invalid value for attrs prop');
275
+ reconcileElementAttributes(target, oldValue ?? EMPTY_OBJECT, newValue ?? EMPTY_OBJECT);
276
+ break;
277
+ }
278
+ case '$dataset': {
279
+ if (!isMap(newValue)) throw new Error('invalid value for dataset prop');
280
+ reconcileElementDataset(target, oldValue ?? EMPTY_OBJECT, newValue ?? EMPTY_OBJECT);
281
+ break;
282
+ }
283
+ default: {
284
+ if (isHtml) {
285
+ const originalProps = nodeState.originalProps;
286
+ if (!(propName in originalProps)) {
287
+ originalProps[propName] = target[propName];
267
288
  }
268
- case '$dataset': {
269
- if (!isMap(newValue)) throw new Error('invalid value for dataset prop');
270
- reconcileElementDataset(target, oldValue ?? EMPTY_OBJECT, newValue ?? EMPTY_OBJECT);
271
- break;
289
+ if (newValue === undefined) {
290
+ target[propName] = originalProps[propName];
291
+ } else {
292
+ target[propName] = newValue;
272
293
  }
273
- default: {
274
- if (isHtml) {
275
- const originalProps = nodeState.originalProps;
276
- if (!(propName in originalProps)) {
277
- originalProps[propName] = target[propName];
278
- }
279
- if (newValue === undefined) {
280
- target[propName] = originalProps[propName];
281
- } else {
282
- target[propName] = newValue;
283
- }
284
- } else {
285
- if (newValue === undefined) {
286
- target.removeAttribute(propName);
287
- } else {
288
- target.setAttribute(propName, newValue);
289
- }
290
- }
291
- break;
294
+ } else {
295
+ if (newValue === undefined) {
296
+ target.removeAttribute(propName);
297
+ } else {
298
+ target.setAttribute(propName, newValue);
292
299
  }
300
+ }
301
+ break;
293
302
  }
303
+ }
294
304
  }
295
305
 
296
306
  // Handle removed props
297
307
  for (const [name, oldValue] of mapIter(oldProps)) {
298
- if (mapGet(props, name) !== undefined) continue; // it wasn't removed
299
-
300
- const propName = convertPropName(name);
301
- switch (propName) {
302
- case '$styling':
303
- reconcileElementStyling(target, oldValue ?? EMPTY_OBJECT, EMPTY_OBJECT);
304
- break;
305
- case '$classes':
306
- reconcileElementClasses(target, oldValue ?? [], []);
307
- break;
308
- case '$attrs':
309
- reconcileElementAttributes(target, oldValue ?? EMPTY_OBJECT, EMPTY_OBJECT);
310
- break;
311
- case '$dataset':
312
- reconcileElementDataset(target, oldValue ?? EMPTY_OBJECT, EMPTY_OBJECT);
313
- break;
314
- default: {
315
- if (isHtml) {
316
- const originalProps = nodeState.originalProps;
317
- if (propName in originalProps) {
318
- target[propName] = originalProps[propName];
319
- delete originalProps[propName];
320
- }
321
- } else {
322
- target.removeAttribute(propName);
323
- }
324
- break;
308
+ if (mapGet(props, name) !== undefined) continue; // it wasn't removed
309
+
310
+ const propName = convertPropName(name);
311
+ switch (propName) {
312
+ case '$styling':
313
+ reconcileElementStyling(target, oldValue ?? EMPTY_OBJECT, EMPTY_OBJECT);
314
+ break;
315
+ case '$classes':
316
+ reconcileElementClasses(target, oldValue ?? [], []);
317
+ break;
318
+ case '$attrs':
319
+ reconcileElementAttributes(target, oldValue ?? EMPTY_OBJECT, EMPTY_OBJECT);
320
+ break;
321
+ case '$dataset':
322
+ reconcileElementDataset(target, oldValue ?? EMPTY_OBJECT, EMPTY_OBJECT);
323
+ break;
324
+ default: {
325
+ if (isHtml) {
326
+ const originalProps = nodeState.originalProps;
327
+ if (propName in originalProps) {
328
+ target[propName] = originalProps[propName];
329
+ delete originalProps[propName];
325
330
  }
331
+ } else {
332
+ target.removeAttribute(propName);
333
+ }
334
+ break;
326
335
  }
336
+ }
327
337
  }
328
338
  }
329
339
 
@@ -335,9 +345,9 @@ export default (userSettings) => {
335
345
  if (typeof listener === 'function') {
336
346
  target.addEventListener(name, listener);
337
347
  } else if (listener != null) {
338
- target.addEventListener(name, mapGet(listener, listenerKey), {
339
- capture: !!mapGet(listener, captureKey),
340
- passive: !!mapGet(listener, passiveKey)
348
+ target.addEventListener(name, mapGet(listener, listenerKey), {
349
+ capture: !!mapGet(listener, captureKey),
350
+ passive: !!mapGet(listener, passiveKey),
341
351
  });
342
352
  }
343
353
  }
@@ -351,15 +361,19 @@ export default (userSettings) => {
351
361
  if (typeof oldListener === 'function') {
352
362
  target.removeEventListener(name, oldListener);
353
363
  } else if (oldListener != null) {
354
- target.removeEventListener(name, mapGet(oldListener, listenerKey), !!mapGet(oldListener, captureKey));
364
+ target.removeEventListener(
365
+ name,
366
+ mapGet(oldListener, listenerKey),
367
+ !!mapGet(oldListener, captureKey),
368
+ );
355
369
  }
356
-
370
+
357
371
  if (typeof listener === 'function') {
358
372
  target.addEventListener(name, listener);
359
373
  } else if (listener != null) {
360
- target.addEventListener(name, mapGet(listener, listenerKey), {
361
- capture: !!mapGet(listener, captureKey),
362
- passive: !!mapGet(listener, passiveKey)
374
+ target.addEventListener(name, mapGet(listener, listenerKey), {
375
+ capture: !!mapGet(listener, captureKey),
376
+ passive: !!mapGet(listener, passiveKey),
363
377
  });
364
378
  }
365
379
  }
@@ -369,7 +383,11 @@ export default (userSettings) => {
369
383
  if (typeof oldListener === 'function') {
370
384
  target.removeEventListener(name, oldListener);
371
385
  } else if (oldListener != null) {
372
- target.removeEventListener(name, mapGet(oldListener, listenerKey), !!mapGet(oldListener, captureKey));
386
+ target.removeEventListener(
387
+ name,
388
+ mapGet(oldListener, listenerKey),
389
+ !!mapGet(oldListener, captureKey),
390
+ );
373
391
  }
374
392
  }
375
393
  }
@@ -459,7 +477,6 @@ export default (userSettings) => {
459
477
  function cleanupTarget(target) {
460
478
  const state = target[NODE_STATE];
461
479
  if (state) {
462
-
463
480
  switch (state.vdom.type) {
464
481
  case ELEMENT_NODE:
465
482
  reconcileElementProps(target, {});
@@ -509,7 +526,7 @@ export default (userSettings) => {
509
526
 
510
527
  let oldNodesPoolForTag = oldVNodeNodesPool.get(vdom.tag);
511
528
  if (!oldNodesPoolForTag) {
512
- oldNodesPoolForTag = { nodesForKey: new Map(), nodesWithoutKey: [] };
529
+ oldNodesPoolForTag = {nodesForKey: new Map(), nodesWithoutKey: []};
513
530
  oldVNodeNodesPool.set(vdom.tag, oldNodesPoolForTag);
514
531
  }
515
532
 
@@ -539,7 +556,10 @@ export default (userSettings) => {
539
556
  if (pool && pool.length > 0) {
540
557
  newDomNode = pool.shift();
541
558
  const state = newDomNode[NODE_STATE];
542
- if (shouldUpdate(state.vdom.args, newVdom.args)) {
559
+ if (
560
+ shouldUpdate(state.vdom.args, newVdom.args) ||
561
+ shouldUpdate(state.vdom.hooks, newVdom.hooks)
562
+ ) {
543
563
  state.newVdom = newVdom;
544
564
  }
545
565
  } else {
@@ -550,7 +570,10 @@ export default (userSettings) => {
550
570
  if (unkeyedOldNode) {
551
571
  newDomNode = unkeyedOldNode;
552
572
  const state = newDomNode[NODE_STATE];
553
- if (shouldUpdate(state.vdom.args, newVdom.args)) {
573
+ if (
574
+ shouldUpdate(state.vdom.args, newVdom.args) ||
575
+ shouldUpdate(state.vdom.hooks, newVdom.hooks)
576
+ ) {
554
577
  state.newVdom = newVdom;
555
578
  }
556
579
  } else {
@@ -583,7 +606,11 @@ export default (userSettings) => {
583
606
  const newChild = newDomChildren[i];
584
607
  const existingChildAtPosition = target.childNodes[i];
585
608
  if (newChild !== existingChildAtPosition) {
586
- (newChild.isConnected ? moveBefore : insertBefore).call(target, newChild, existingChildAtPosition);
609
+ (newChild.isConnected ? moveBefore : insertBefore).call(
610
+ target,
611
+ newChild,
612
+ existingChildAtPosition,
613
+ );
587
614
  }
588
615
  const state = newChild[NODE_STATE];
589
616
  if (state?.newVdom) {
@@ -675,7 +702,10 @@ export default (userSettings) => {
675
702
  if (vdom instanceof VNode) {
676
703
  if (state) {
677
704
  if (state.vdom.type === vdom.type) {
678
- if (shouldUpdate(state.vdom.args, vdom.args)) {
705
+ if (
706
+ shouldUpdate(state.vdom.args, vdom.args) ||
707
+ shouldUpdate(state.vdom.hooks, vdom.hooks)
708
+ ) {
679
709
  state.newVdom = vdom;
680
710
  reconcileNode(target);
681
711
  }
@@ -687,13 +717,18 @@ export default (userSettings) => {
687
717
  switch (vdom.type) {
688
718
  case ELEMENT_NODE:
689
719
  case OPAQUE_NODE:
690
- if (0 !== target.nodeName.localeCompare(convertTagName(vdom.tag), undefined, { sensitivity: 'base' })) {
720
+ if (
721
+ 0 !==
722
+ target.nodeName.localeCompare(convertTagName(vdom.tag), undefined, {
723
+ sensitivity: 'base',
724
+ })
725
+ ) {
691
726
  throw new Error('incompatible target for vdom');
692
727
  }
693
728
  break;
694
729
  }
695
730
 
696
- target[NODE_STATE] = { originalProps: {}, newVdom: vdom };
731
+ target[NODE_STATE] = {originalProps: {}, newVdom: vdom};
697
732
  try {
698
733
  vdom.hooks?.$attach?.(target);
699
734
  if (vdom.type === SPECIAL_NODE) {
@@ -709,9 +744,30 @@ export default (userSettings) => {
709
744
  throw new Error('invalid vdom');
710
745
  }
711
746
 
712
- return { h, alias, special, reconcile, settings: {
713
- shouldUpdate, isMap, mapIter, mapGet, mapMerge, newMap, mapPut, isSeq, flattenSeq, seqIter,
714
- convertTagName, convertPropName, convertStyleName, convertDataName, convertClassName,
715
- listenerKey, captureKey, passiveKey
716
- } };
717
- }
747
+ return {
748
+ h,
749
+ alias,
750
+ special,
751
+ reconcile,
752
+ settings: {
753
+ shouldUpdate,
754
+ isMap,
755
+ mapIter,
756
+ mapGet,
757
+ mapMerge,
758
+ newMap,
759
+ mapPut,
760
+ isSeq,
761
+ flattenSeq,
762
+ seqIter,
763
+ convertTagName,
764
+ convertPropName,
765
+ convertStyleName,
766
+ convertDataName,
767
+ convertClassName,
768
+ listenerKey,
769
+ captureKey,
770
+ passiveKey,
771
+ },
772
+ };
773
+ };