@simpreact/simpreact 0.0.2 → 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.
Files changed (48) hide show
  1. package/compat/context.d.ts +8 -0
  2. package/compat/context.js +7 -0
  3. package/compat/core.d.ts +43 -0
  4. package/compat/core.js +90 -0
  5. package/compat/dom.d.ts +11 -0
  6. package/compat/dom.js +10 -0
  7. package/compat/hooks.d.ts +24 -0
  8. package/compat/hooks.js +75 -0
  9. package/compat/index.d.ts +43 -0
  10. package/compat/index.js +17 -0
  11. package/compat/jsx-runtime.d.ts +10 -0
  12. package/compat/jsx-runtime.js +9 -0
  13. package/context/index.d.ts +24 -0
  14. package/context/index.js +64 -0
  15. package/core/createElement.d.ts +6 -9
  16. package/core/createElement.js +4 -25
  17. package/core/index.d.ts +4 -23
  18. package/core/index.js +3 -3
  19. package/core/internal.d.ts +1 -1
  20. package/core/internal.js +1 -1
  21. package/core/lifecycleEventBus.d.ts +4 -0
  22. package/core/memo.d.ts +10 -0
  23. package/core/memo.js +11 -0
  24. package/core/mounting.d.ts +6 -9
  25. package/core/mounting.js +44 -49
  26. package/core/patching.d.ts +4 -5
  27. package/core/patching.js +81 -68
  28. package/core/ref.d.ts +4 -4
  29. package/core/rerender.d.ts +7 -6
  30. package/core/rerender.js +91 -32
  31. package/core/unmounting.js +7 -3
  32. package/dom/attach-element-to-dom.js +11 -2
  33. package/dom/events.d.ts +9 -1
  34. package/dom/events.js +21 -5
  35. package/dom/props/controlled/input.js +5 -5
  36. package/dom/props/controlled/select.js +3 -3
  37. package/dom/props/controlled/textarea.js +5 -5
  38. package/dom/props/style.js +6 -3
  39. package/dom/render.js +3 -3
  40. package/hooks/index.d.ts +7 -3
  41. package/hooks/index.js +46 -14
  42. package/package.json +7 -1
  43. package/shared/index.d.ts +6 -0
  44. package/shared/index.js +12 -3
  45. package/shared/utils.d.ts +2 -0
  46. package/shared/utils.js +36 -0
  47. package/core/context.d.ts +0 -18
  48. package/core/context.js +0 -18
package/core/memo.js ADDED
@@ -0,0 +1,11 @@
1
+ import { shallowEqual } from '../shared/index.js';
2
+ export function memo(Component, compare = shallowEqual) {
3
+ const Memoized = (props => Component(props));
4
+ Memoized._isMemo = true;
5
+ Memoized._render = Component;
6
+ Memoized._compare = compare;
7
+ return Memoized;
8
+ }
9
+ export function isMemo(type) {
10
+ return type._isMemo;
11
+ }
@@ -1,13 +1,10 @@
1
1
  import type { Maybe, Nullable } from '../shared/index.js';
2
2
  import type { HostReference } from './hostAdapter.js';
3
3
  import type { SimpElement } from './createElement.js';
4
- import type { SimpContextMap } from './context.js';
5
- export declare function mount(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>, hostNamespace: Maybe<string>): void;
4
+ export declare function mount(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, context: unknown, hostNamespace: Maybe<string>): void;
6
5
  export declare function mountTextElement(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>): void;
7
- export declare function mountHostElement(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>, hostNamespace: Maybe<string>): void;
8
- export declare function mountFunctionalElement(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>, hostNamespace: Maybe<string>): void;
9
- export declare function mountFragment(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>, hostNamespace: Maybe<string>): void;
10
- export declare function mountArrayChildren(children: SimpElement[], reference: Nullable<HostReference>, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>, parentElement: SimpElement, hostNamespace: Maybe<string>): void;
11
- export declare function mountProvider(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>, hostNamespace: Maybe<string>): void;
12
- export declare function mountConsumer(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>, hostNamespace: Maybe<string>): void;
13
- export declare function mountPortal(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>): void;
6
+ export declare function mountHostElement(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, context: unknown, hostNamespace: Maybe<string>): void;
7
+ export declare function mountFunctionalElement(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, context: unknown, hostNamespace: Maybe<string>): void;
8
+ export declare function mountFragment(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, context: unknown, hostNamespace: Maybe<string>): void;
9
+ export declare function mountArrayChildren(children: SimpElement[], reference: Nullable<HostReference>, nextReference: Nullable<HostReference>, context: unknown, parentElement: SimpElement, hostNamespace: Maybe<string>): void;
10
+ export declare function mountPortal(element: SimpElement, parentReference: Nullable<HostReference>, nextReference: Nullable<HostReference>, context: unknown): void;
package/core/mounting.js CHANGED
@@ -1,29 +1,24 @@
1
- import { emptyMap, emptyObject } from '../shared/index.js';
1
+ import { emptyObject } from '../shared/index.js';
2
2
  import { hostAdapter } from './hostAdapter.js';
3
3
  import { createTextElement, normalizeRoot } from './createElement.js';
4
4
  import { applyRef } from './ref.js';
5
5
  import { lifecycleEventBus } from './lifecycleEventBus.js';
6
- export function mount(element, parentReference, nextReference, contextMap, hostNamespace) {
6
+ import { batchingRerenderLocker } from './rerender.js';
7
+ export function mount(element, parentReference, nextReference, context, hostNamespace) {
7
8
  if (element.flag === 'TEXT') {
8
9
  mountTextElement(element, parentReference, nextReference);
9
10
  }
10
11
  else if (element.flag === 'HOST') {
11
- mountHostElement(element, parentReference, nextReference, contextMap, hostNamespace);
12
+ mountHostElement(element, parentReference, nextReference, context, hostNamespace);
12
13
  }
13
14
  else if (element.flag === 'FC') {
14
- mountFunctionalElement(element, parentReference, nextReference, contextMap, hostNamespace);
15
+ mountFunctionalElement(element, parentReference, nextReference, context, hostNamespace);
15
16
  }
16
17
  else if (element.flag === 'FRAGMENT') {
17
- mountFragment(element, parentReference, nextReference, contextMap, hostNamespace);
18
- }
19
- else if (element.flag === 'PROVIDER') {
20
- mountProvider(element, parentReference, nextReference, contextMap, hostNamespace);
21
- }
22
- else if (element.flag === 'PORTAL') {
23
- mountPortal(element, parentReference, nextReference, contextMap);
18
+ mountFragment(element, parentReference, nextReference, context, hostNamespace);
24
19
  }
25
20
  else {
26
- mountConsumer(element, parentReference, nextReference, contextMap, hostNamespace);
21
+ mountPortal(element, parentReference, nextReference, context);
27
22
  }
28
23
  }
29
24
  export function mountTextElement(element, parentReference, nextReference) {
@@ -32,7 +27,7 @@ export function mountTextElement(element, parentReference, nextReference) {
32
27
  hostAdapter.insertOrAppend(parentReference, reference, nextReference);
33
28
  }
34
29
  }
35
- export function mountHostElement(element, parentReference, nextReference, contextMap, hostNamespace) {
30
+ export function mountHostElement(element, parentReference, nextReference, context, hostNamespace) {
36
31
  const hostNamespaces = hostAdapter.getHostNamespaces(element, hostNamespace);
37
32
  hostNamespace = hostNamespaces?.self;
38
33
  const hostReference = (element.reference = hostAdapter.createReference(element.type, hostNamespace));
@@ -40,11 +35,11 @@ export function mountHostElement(element, parentReference, nextReference, contex
40
35
  // HOST element always has Maybe<Many<SimpElement>> children due to normalization process.
41
36
  const children = element.children;
42
37
  if (Array.isArray(children)) {
43
- mountArrayChildren(children, hostReference, null, contextMap, element, hostNamespaces?.children);
38
+ mountArrayChildren(children, hostReference, null, context, element, hostNamespaces?.children);
44
39
  }
45
40
  else if (children) {
46
41
  children.parent = element;
47
- mount(children, hostReference, null, contextMap, hostNamespaces?.children);
42
+ mount(children, hostReference, null, context, hostNamespaces?.children);
48
43
  }
49
44
  if (element.props) {
50
45
  hostAdapter.mountProps(hostReference, element, hostNamespace);
@@ -61,9 +56,9 @@ export function mountHostElement(element, parentReference, nextReference, contex
61
56
  }
62
57
  applyRef(element);
63
58
  }
64
- export function mountFunctionalElement(element, parentReference, nextReference, contextMap, hostNamespace) {
65
- if (contextMap) {
66
- element.contextMap = contextMap;
59
+ export function mountFunctionalElement(element, parentReference, nextReference, context, hostNamespace) {
60
+ if (context) {
61
+ element.context = context;
67
62
  }
68
63
  if (element.unmounted) {
69
64
  element.unmounted = false;
@@ -74,61 +69,61 @@ export function mountFunctionalElement(element, parentReference, nextReference,
74
69
  }
75
70
  // FC element always has Maybe<SimpElement> children due to normalization process.
76
71
  let children;
72
+ let triedToRerenderUnsubscribe;
77
73
  try {
78
- lifecycleEventBus.publish({ type: 'beforeRender', element, phase: 'mounting' });
79
- children = normalizeRoot(element.type(element.props || emptyObject), false);
80
- lifecycleEventBus.publish({ type: 'afterRender', phase: 'mounting' });
74
+ let triedToRerender = false;
75
+ let rerenderCounter = 0;
76
+ triedToRerenderUnsubscribe = lifecycleEventBus.subscribe(event => {
77
+ if (event.type === 'triedToRerender' && event.element === element) {
78
+ triedToRerender = true;
79
+ }
80
+ });
81
+ do {
82
+ triedToRerender = false;
83
+ if (++rerenderCounter >= 25) {
84
+ throw new Error('Too many re-renders.');
85
+ }
86
+ lifecycleEventBus.publish({ type: 'beforeRender', element, phase: 'mounting' });
87
+ batchingRerenderLocker.lock();
88
+ children = element.type(element.props || emptyObject);
89
+ batchingRerenderLocker.flush();
90
+ lifecycleEventBus.publish({ type: 'afterRender', element, phase: 'mounting' });
91
+ } while (triedToRerender);
92
+ children = normalizeRoot(children, false);
81
93
  }
82
94
  catch (error) {
83
95
  lifecycleEventBus.publish({ type: 'errored', element, error, phase: 'mounting' });
84
96
  return;
85
97
  }
98
+ finally {
99
+ triedToRerenderUnsubscribe();
100
+ }
86
101
  if (children) {
87
102
  children.parent = element;
88
- mount((element.children = children), parentReference, nextReference, contextMap, hostNamespace);
103
+ mount((element.children = children), parentReference, nextReference, element.context, hostNamespace);
89
104
  }
90
105
  lifecycleEventBus.publish({ type: 'mounted', element });
91
106
  }
92
- export function mountFragment(element, parentReference, nextReference, contextMap, hostNamespace) {
107
+ export function mountFragment(element, parentReference, nextReference, context, hostNamespace) {
93
108
  // FRAGMENT element always has Maybe<Many<SimpElement>> children due to normalization process.
94
109
  if (Array.isArray(element.children)) {
95
- mountArrayChildren(element.children, parentReference, nextReference, contextMap, element, hostNamespace);
110
+ mountArrayChildren(element.children, parentReference, nextReference, context, element, hostNamespace);
96
111
  }
97
112
  else if (element.children) {
98
113
  element.children.parent = element;
99
- mount(element.children, parentReference, nextReference, contextMap, hostNamespace);
114
+ mount(element.children, parentReference, nextReference, context, hostNamespace);
100
115
  }
101
116
  }
102
- export function mountArrayChildren(children, reference, nextReference, contextMap, parentElement, hostNamespace) {
117
+ export function mountArrayChildren(children, reference, nextReference, context, parentElement, hostNamespace) {
103
118
  for (const child of children) {
104
119
  child.parent = parentElement;
105
- mount(child, reference, nextReference, contextMap, hostNamespace);
106
- }
107
- }
108
- export function mountProvider(element, parentReference, nextReference, contextMap, hostNamespace) {
109
- contextMap = new Map(contextMap);
110
- contextMap.set(element.type.context, element.props.value);
111
- // PROVIDER element always has Maybe<Many<SimpElement>> children due to normalization process.
112
- if (Array.isArray(element.children)) {
113
- mountArrayChildren(element.children, parentReference, nextReference, contextMap, element, hostNamespace);
114
- }
115
- else if (element.children) {
116
- element.children.parent = element;
117
- mount(element.children, parentReference, nextReference, contextMap, hostNamespace);
118
- }
119
- }
120
- export function mountConsumer(element, parentReference, nextReference, contextMap, hostNamespace) {
121
- const children = normalizeRoot(element.type(element.props || emptyObject, contextMap || emptyMap), false);
122
- if (!children) {
123
- return;
120
+ mount(child, reference, nextReference, context, hostNamespace);
124
121
  }
125
- children.parent = element;
126
- mount((element.children = children), parentReference, nextReference, contextMap, hostNamespace);
127
122
  }
128
- export function mountPortal(element, parentReference, nextReference, contextMap) {
123
+ export function mountPortal(element, parentReference, nextReference, context) {
129
124
  if (element.children) {
130
125
  element.children.parent = element;
131
- mount(element.children, element.ref, null, contextMap, hostAdapter.getHostNamespaces(element.children, undefined)?.self);
126
+ mount(element.children, element.ref, null, context, hostAdapter.getHostNamespaces(element.children, undefined)?.self);
132
127
  }
133
128
  const placeHolderElement = createTextElement('');
134
129
  mountTextElement(placeHolderElement, parentReference, nextReference);
@@ -1,10 +1,9 @@
1
1
  import type { Maybe, Nullable } from '../shared/index.js';
2
2
  import type { SimpElement } from './createElement.js';
3
3
  import type { HostReference } from './hostAdapter.js';
4
- import type { SimpContextMap } from './context.js';
5
- export declare function patch(prevElement: SimpElement, nextElement: SimpElement, parentReference: HostReference, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>, hostNamespace: Maybe<string>): void;
6
- export declare function patchPortal(prevElement: SimpElement, nextElement: SimpElement, contextMap: Nullable<SimpContextMap>): void;
7
- export declare function updateFunctionalComponent(element: SimpElement, parentReference: HostReference, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>, hostNamespace: Maybe<string>): void;
8
- export declare function patchKeyedChildren(prevChildren: SimpElement[], nextChildren: SimpElement[], parentReference: HostReference, nextReference: Nullable<HostReference>, contextMap: Nullable<SimpContextMap>, hostNamespace: Maybe<string>): void;
4
+ export declare function patch(prevElement: SimpElement, nextElement: SimpElement, parentReference: HostReference, nextReference: Nullable<HostReference>, context: unknown, hostNamespace: Maybe<string>): void;
5
+ export declare function patchPortal(prevElement: SimpElement, nextElement: SimpElement, context: unknown): void;
6
+ export declare function updateFunctionalComponent(element: SimpElement, parentReference: HostReference, nextReference: Nullable<HostReference>, context: unknown, hostNamespace: Maybe<string>): void;
7
+ export declare function patchKeyedChildren(prevChildren: SimpElement[], nextChildren: SimpElement[], parentReference: HostReference, nextReference: Nullable<HostReference>, context: unknown, hostNamespace: Maybe<string>): void;
9
8
  export declare function findParentReferenceFromElement(element: SimpElement): Nullable<HostReference>;
10
9
  export declare function findHostReferenceFromElement(element: SimpElement): Nullable<HostReference>;
package/core/patching.js CHANGED
@@ -1,49 +1,45 @@
1
- import { emptyMap, emptyObject } from '../shared/index.js';
1
+ import { emptyObject } from '../shared/index.js';
2
2
  import { normalizeRoot } from './createElement.js';
3
3
  import { hostAdapter } from './hostAdapter.js';
4
4
  import { clearElementHostReference, remove, unmount } from './unmounting.js';
5
- import { mount, mountArrayChildren } from './mounting.js';
5
+ import { mount, mountArrayChildren, mountFunctionalElement } from './mounting.js';
6
6
  import { applyRef } from './ref.js';
7
7
  import { lifecycleEventBus } from './lifecycleEventBus.js';
8
- export function patch(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace) {
8
+ import { batchingRerenderLocker } from './rerender.js';
9
+ import { isMemo } from './memo.js';
10
+ export function patch(prevElement, nextElement, parentReference, nextReference, context, hostNamespace) {
9
11
  if (prevElement.type !== nextElement.type || prevElement.key !== nextElement.key) {
10
- replaceWithNewElement(prevElement, nextElement, parentReference, contextMap, hostNamespace);
12
+ replaceWithNewElement(prevElement, nextElement, parentReference, context, hostNamespace);
11
13
  }
12
14
  else if (nextElement.flag === 'HOST') {
13
- patchHostElement(prevElement, nextElement, contextMap, hostNamespace);
15
+ patchHostElement(prevElement, nextElement, context, hostNamespace);
14
16
  }
15
17
  else if (nextElement.flag === 'FC') {
16
- patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace);
18
+ patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference, context, hostNamespace);
17
19
  }
18
20
  else if (nextElement.flag === 'TEXT') {
19
21
  patchTextElement(prevElement, nextElement);
20
22
  }
21
23
  else if (nextElement.flag === 'FRAGMENT') {
22
- patchFragment(prevElement, nextElement, parentReference, contextMap, hostNamespace);
23
- }
24
- else if (nextElement.flag === 'PROVIDER') {
25
- patchProvider(prevElement, nextElement, parentReference, contextMap, hostNamespace);
26
- }
27
- else if (nextElement.flag === 'PORTAL') {
28
- patchPortal(prevElement, nextElement, contextMap);
24
+ patchFragment(prevElement, nextElement, parentReference, context, hostNamespace);
29
25
  }
30
26
  else {
31
- patchConsumer(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace);
27
+ patchPortal(prevElement, nextElement, context);
32
28
  }
33
29
  }
34
- function replaceWithNewElement(prevElement, nextElement, parentReference, contextMap, hostNamespace) {
30
+ function replaceWithNewElement(prevElement, nextElement, parentReference, context, hostNamespace) {
35
31
  unmount(prevElement);
36
32
  nextElement.parent = prevElement.parent;
37
33
  if (nextElement.flag === 'HOST' && prevElement.flag === 'HOST') {
38
- mount(nextElement, null, null, contextMap, hostNamespace);
34
+ mount(nextElement, null, null, context, hostNamespace);
39
35
  hostAdapter.replaceChild(parentReference, nextElement.reference, prevElement.reference);
40
36
  }
41
37
  else {
42
- mount(nextElement, parentReference, findHostReferenceFromElement(prevElement), contextMap, hostNamespace);
38
+ mount(nextElement, parentReference, findHostReferenceFromElement(prevElement), context, hostNamespace);
43
39
  clearElementHostReference(prevElement, parentReference);
44
40
  }
45
41
  }
46
- function patchHostElement(prevElement, nextElement, contextMap, hostNamespace) {
42
+ function patchHostElement(prevElement, nextElement, context, hostNamespace) {
47
43
  if (prevElement.ref) {
48
44
  nextElement.ref = prevElement.ref;
49
45
  }
@@ -51,36 +47,70 @@ function patchHostElement(prevElement, nextElement, contextMap, hostNamespace) {
51
47
  hostNamespace = hostNamespaces?.self;
52
48
  nextElement.reference = prevElement.reference;
53
49
  hostAdapter.attachElementToReference(nextElement, nextElement.reference);
54
- patchChildren(prevElement.children || prevElement.props?.children, nextElement.children || nextElement.props?.children, nextElement.reference, null, nextElement, contextMap, hostNamespaces?.children);
50
+ patchChildren(prevElement.children || prevElement.props?.children, nextElement.children || nextElement.props?.children, nextElement.reference, null, nextElement, context, hostNamespaces?.children);
55
51
  hostAdapter.patchProps(nextElement.reference, prevElement, nextElement, hostNamespace);
56
52
  if (prevElement.className !== nextElement.className) {
57
53
  hostAdapter.setClassname(nextElement.reference, nextElement.className, hostNamespace);
58
54
  }
59
55
  applyRef(nextElement);
60
56
  }
61
- function patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace) {
62
- (nextElement.store = prevElement.store ||= {}).latestElement = nextElement;
57
+ function patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference, context, hostNamespace) {
58
+ nextElement.store = prevElement.store || {};
59
+ nextElement.store.latestElement = nextElement;
63
60
  if (hostNamespace) {
64
61
  nextElement.store.hostNamespace = hostNamespace;
65
62
  }
66
- if (contextMap) {
67
- nextElement.contextMap = contextMap;
63
+ let forceRender = false;
64
+ if (prevElement.store?.forceRender) {
65
+ forceRender = true;
66
+ prevElement.store.forceRender = false;
68
67
  }
68
+ if (prevElement.unmounted) {
69
+ mountFunctionalElement(nextElement, parentReference, nextReference, context, hostNamespace);
70
+ return;
71
+ }
72
+ if (!forceRender && isMemo(nextElement.type) && nextElement.type._compare(prevElement.props, nextElement.props)) {
73
+ return;
74
+ }
75
+ nextElement.context = prevElement.context || context;
76
+ // FC element always has Maybe<SimpElement> children due to normalization process.
69
77
  let nextChildren;
78
+ let triedToRerenderUnsubscribe;
70
79
  try {
71
- lifecycleEventBus.publish({ type: 'beforeRender', element: nextElement, phase: 'updating' });
72
- nextChildren = normalizeRoot(nextElement.type(nextElement.props || emptyObject), false);
73
- lifecycleEventBus.publish({ type: 'afterRender', phase: 'updating' });
80
+ let triedToRerender = false;
81
+ let rerenderCounter = 0;
82
+ triedToRerenderUnsubscribe = lifecycleEventBus.subscribe(event => {
83
+ if (event.type === 'triedToRerender' && event.element === nextElement) {
84
+ triedToRerender = true;
85
+ }
86
+ });
87
+ do {
88
+ triedToRerender = false;
89
+ if (++rerenderCounter >= 25) {
90
+ throw new Error('Too many re-renders.');
91
+ }
92
+ lifecycleEventBus.publish({ type: 'beforeRender', element: nextElement, phase: 'updating' });
93
+ batchingRerenderLocker.lock();
94
+ nextChildren = nextElement.type(nextElement.props || emptyObject);
95
+ batchingRerenderLocker.flush();
96
+ lifecycleEventBus.publish({ type: 'afterRender', element: nextElement, phase: 'updating' });
97
+ } while (triedToRerender);
98
+ nextChildren = normalizeRoot(nextChildren, false);
74
99
  }
75
100
  catch (error) {
76
101
  lifecycleEventBus.publish({ type: 'errored', element: nextElement, error, phase: 'updating' });
102
+ remove(prevElement, parentReference);
77
103
  return;
78
104
  }
105
+ finally {
106
+ triedToRerenderUnsubscribe();
107
+ }
108
+ // Keep prevElement's children reference when prev and next elements are identical to avoid reassignment.
79
109
  const prevChildren = prevElement.children;
80
110
  if (nextChildren) {
81
111
  nextElement.children = nextChildren;
82
112
  }
83
- patchChildren(prevChildren, nextChildren, parentReference, nextReference, nextElement, contextMap, hostNamespace);
113
+ patchChildren(prevChildren, nextChildren, parentReference, nextReference, nextElement, nextElement.context, hostNamespace);
84
114
  lifecycleEventBus.publish({ type: 'updated', element: nextElement });
85
115
  }
86
116
  function patchTextElement(prevElement, nextElement) {
@@ -89,57 +119,41 @@ function patchTextElement(prevElement, nextElement) {
89
119
  hostAdapter.setTextContent(nextElement.reference, nextElement.children);
90
120
  }
91
121
  }
92
- function patchFragment(prevElement, nextElement, parentReference, contextMap, hostNamespace) {
122
+ function patchFragment(prevElement, nextElement, parentReference, context, hostNamespace) {
93
123
  let nextReference = null;
94
124
  if (Array.isArray(prevElement.children) && !Array.isArray(nextElement.children) && nextElement.children) {
95
125
  nextReference = hostAdapter.findNextSiblingReference(findHostReferenceFromElement(prevElement.children[prevElement.children.length - 1]));
96
126
  }
97
- patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, nextElement, contextMap, hostNamespace);
127
+ patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, nextElement, context, hostNamespace);
98
128
  }
99
- function patchProvider(prevElement, nextElement, parentReference, contextMap, hostNamespace) {
100
- let nextReference = null;
101
- if (Array.isArray(prevElement.children) && !Array.isArray(nextElement.children) && nextElement.children) {
102
- nextReference = hostAdapter.findNextSiblingReference(findHostReferenceFromElement(prevElement.children[prevElement.children.length - 1]));
103
- }
104
- contextMap = new Map(contextMap);
105
- contextMap.set(nextElement.type.context, nextElement.props.value);
106
- patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, nextElement, contextMap, hostNamespace);
107
- }
108
- function patchConsumer(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace) {
109
- const children = normalizeRoot(nextElement.type(nextElement.props || emptyObject, contextMap || emptyMap), false);
110
- if (children) {
111
- nextElement.children = children;
112
- }
113
- patchChildren(prevElement.children, nextElement.children, parentReference, nextReference, nextElement, contextMap, hostNamespace);
114
- }
115
- export function patchPortal(prevElement, nextElement, contextMap) {
129
+ export function patchPortal(prevElement, nextElement, context) {
116
130
  const prevContainer = prevElement.ref;
117
131
  const nextContainer = nextElement.ref;
118
132
  const nextChildren = nextElement.children;
119
- patchChildren(prevElement.children, nextChildren, prevContainer, null, nextElement, contextMap, hostAdapter.getHostNamespaces(nextChildren, undefined)?.self);
133
+ patchChildren(prevElement.children, nextChildren, prevContainer, null, nextElement, context, hostAdapter.getHostNamespaces(nextChildren, undefined)?.self);
120
134
  nextElement.reference = prevElement.reference;
121
135
  if (prevContainer !== nextContainer && nextChildren != null) {
122
136
  hostAdapter.removeChild(prevContainer, nextChildren.reference);
123
137
  hostAdapter.appendChild(nextContainer, nextChildren.reference);
124
138
  }
125
139
  }
126
- export function updateFunctionalComponent(element, parentReference, nextReference, contextMap, hostNamespace) {
127
- patchFunctionalComponent(element, element, parentReference, nextReference, contextMap, hostNamespace);
140
+ export function updateFunctionalComponent(element, parentReference, nextReference, context, hostNamespace) {
141
+ patchFunctionalComponent(element, element, parentReference, nextReference, context, hostNamespace);
128
142
  }
129
- function patchChildren(prevChildren, nextChildren, parentReference, nextReference, nextElement, contextMap, hostNamespace) {
143
+ function patchChildren(prevChildren, nextChildren, parentReference, nextReference, nextElement, context, hostNamespace) {
130
144
  if (Array.isArray(prevChildren)) {
131
145
  if (Array.isArray(nextChildren)) {
132
146
  for (const child of nextChildren) {
133
147
  child.parent = nextElement;
134
148
  }
135
- patchKeyedChildren(prevChildren, nextChildren, parentReference, nextReference, contextMap, hostNamespace);
149
+ patchKeyedChildren(prevChildren, nextChildren, parentReference, nextReference, context, hostNamespace);
136
150
  }
137
151
  else if (typeof nextChildren === 'string') {
138
152
  unmount(prevChildren);
139
153
  hostAdapter.setTextContent(parentReference, nextChildren);
140
154
  }
141
155
  else if (nextChildren) {
142
- patchKeyedChildren(prevChildren, [nextChildren], parentReference, nextReference, contextMap, hostNamespace);
156
+ patchKeyedChildren(prevChildren, [nextChildren], parentReference, nextReference, context, hostNamespace);
143
157
  }
144
158
  else {
145
159
  unmount(prevChildren);
@@ -149,7 +163,7 @@ function patchChildren(prevChildren, nextChildren, parentReference, nextReferenc
149
163
  else if (typeof prevChildren === 'string') {
150
164
  if (Array.isArray(nextChildren)) {
151
165
  hostAdapter.clearNode(parentReference);
152
- mountArrayChildren(nextChildren, parentReference, nextReference, contextMap, nextElement, hostNamespace);
166
+ mountArrayChildren(nextChildren, parentReference, nextReference, context, nextElement, hostNamespace);
153
167
  }
154
168
  else if (typeof nextChildren === 'string') {
155
169
  if (prevChildren !== nextChildren) {
@@ -159,7 +173,7 @@ function patchChildren(prevChildren, nextChildren, parentReference, nextReferenc
159
173
  else if (nextChildren) {
160
174
  hostAdapter.clearNode(parentReference);
161
175
  nextChildren.parent = nextElement;
162
- mount(nextChildren, parentReference, nextReference, contextMap, hostNamespace);
176
+ mount(nextChildren, parentReference, nextReference, context, hostNamespace);
163
177
  }
164
178
  else {
165
179
  hostAdapter.clearNode(parentReference);
@@ -167,7 +181,7 @@ function patchChildren(prevChildren, nextChildren, parentReference, nextReferenc
167
181
  }
168
182
  else if (prevChildren) {
169
183
  if (Array.isArray(nextChildren)) {
170
- patchKeyedChildren([prevChildren], nextChildren, parentReference, nextReference, contextMap, hostNamespace);
184
+ patchKeyedChildren([prevChildren], nextChildren, parentReference, nextReference, context, hostNamespace);
171
185
  }
172
186
  else if (typeof nextChildren === 'string') {
173
187
  unmount(prevChildren);
@@ -175,27 +189,26 @@ function patchChildren(prevChildren, nextChildren, parentReference, nextReferenc
175
189
  }
176
190
  else if (nextChildren) {
177
191
  nextChildren.parent = nextElement;
178
- patch(prevChildren, nextChildren, parentReference, nextReference, contextMap, hostNamespace);
192
+ patch(prevChildren, nextChildren, parentReference, nextReference, context, hostNamespace);
179
193
  }
180
194
  else {
181
- unmount(prevChildren);
182
- hostAdapter.clearNode(parentReference);
195
+ remove(prevChildren, parentReference);
183
196
  }
184
197
  }
185
198
  else {
186
199
  if (Array.isArray(nextChildren)) {
187
- mountArrayChildren(nextChildren, parentReference, nextReference, contextMap, nextElement, hostNamespace);
200
+ mountArrayChildren(nextChildren, parentReference, nextReference, context, nextElement, hostNamespace);
188
201
  }
189
202
  else if (typeof nextChildren === 'string') {
190
203
  hostAdapter.setTextContent(parentReference, nextChildren);
191
204
  }
192
205
  else if (nextChildren) {
193
206
  nextChildren.parent = nextElement;
194
- mount(nextChildren, parentReference, nextReference, contextMap, hostNamespace);
207
+ mount(nextChildren, parentReference, nextReference, context, hostNamespace);
195
208
  }
196
209
  }
197
210
  }
198
- export function patchKeyedChildren(prevChildren, nextChildren, parentReference, nextReference, contextMap, hostNamespace) {
211
+ export function patchKeyedChildren(prevChildren, nextChildren, parentReference, nextReference, context, hostNamespace) {
199
212
  let prevStart = 0;
200
213
  let nextStart = 0;
201
214
  let prevEnd = prevChildren.length - 1;
@@ -204,21 +217,21 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
204
217
  while (prevStart <= prevEnd &&
205
218
  nextStart <= nextEnd &&
206
219
  prevChildren[prevStart].key === nextChildren[nextStart].key) {
207
- patch(prevChildren[prevStart], nextChildren[nextStart], parentReference, null, contextMap, hostNamespace);
220
+ patch(prevChildren[prevStart], nextChildren[nextStart], parentReference, null, context, hostNamespace);
208
221
  prevStart++;
209
222
  nextStart++;
210
223
  }
211
224
  // Step 2: Sync from end
212
225
  while (prevStart <= prevEnd && nextStart <= nextEnd && prevChildren[prevEnd].key === nextChildren[nextEnd].key) {
213
- patch(prevChildren[prevEnd], nextChildren[nextEnd], parentReference, null, contextMap, hostNamespace);
226
+ patch(prevChildren[prevEnd], nextChildren[nextEnd], parentReference, null, context, hostNamespace);
214
227
  prevEnd--;
215
228
  nextEnd--;
216
229
  }
217
230
  // Step 3: Mount new nodes if prev list is exhausted
218
231
  if (prevStart > prevEnd) {
219
- const before = nextChildren[nextEnd + 1]?.reference || nextReference;
232
+ const before = findHostReferenceFromElement(nextChildren[nextEnd + 1]) || nextReference;
220
233
  for (let i = nextStart; i <= nextEnd; i++) {
221
- mount(nextChildren[i], parentReference, before, contextMap, hostNamespace);
234
+ mount(nextChildren[i], parentReference, before, context, hostNamespace);
222
235
  }
223
236
  // Step 4: Remove prev nodes if next list is exhausted
224
237
  }
@@ -246,12 +259,12 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
246
259
  const prevIndex = keyToPrevIndexMap.get(nextChild.key);
247
260
  if (prevIndex != null) {
248
261
  const prevElement = prevChildren[prevIndex];
249
- patch(prevElement, nextChild, parentReference, null, contextMap, hostNamespace);
262
+ patch(prevElement, nextChild, parentReference, null, context, hostNamespace);
250
263
  toMove[i - nextStart] = prevIndex;
251
264
  usedIndices.add(prevIndex);
252
265
  }
253
266
  else {
254
- mount(nextChild, parentReference, nextChildren[i + 1]?.reference || nextReference, contextMap, hostNamespace);
267
+ mount(nextChild, parentReference, findHostReferenceFromElement(nextChildren[i + 1]) || nextReference, context, hostNamespace);
255
268
  toMove[i - nextStart] = -1;
256
269
  }
257
270
  }
@@ -264,7 +277,7 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
264
277
  // Insert in correct order
265
278
  for (let i = nextEnd; i >= nextStart; i--) {
266
279
  const currentChild = nextChildren[i];
267
- const reference = nextChildren[i + 1]?.reference || nextReference;
280
+ const reference = findHostReferenceFromElement(nextChildren[i + 1]) || nextReference;
268
281
  if (toMove[i - nextStart] !== -1) {
269
282
  hostAdapter.insertBefore(parentReference, currentChild.reference, reference);
270
283
  }
package/core/ref.d.ts CHANGED
@@ -5,13 +5,13 @@ interface RefSimpElement extends SimpElement {
5
5
  cleanup?: () => void;
6
6
  };
7
7
  }
8
- export interface RefObject<T> {
8
+ export type RefObject<T> = {
9
9
  current: T;
10
- }
10
+ };
11
11
  export type RefCallback<T> = {
12
- bivarianceHack(instance: T | null): (() => void) | void;
12
+ bivarianceHack(instance: T): (() => void | undefined) | void;
13
13
  }['bivarianceHack'];
14
- export type Ref<T> = RefCallback<T> | RefObject<T> | null;
14
+ export type Ref<T> = RefCallback<T> | RefObject<T | null> | null;
15
15
  export declare function unmountRef(element: RefSimpElement): void;
16
16
  export declare function applyRef(element: RefSimpElement): void;
17
17
  export {};
@@ -1,13 +1,14 @@
1
- import type { SimpElement } from './createElement.js';
1
+ import type { SimpElement, SimpElementStore } from './createElement.js';
2
2
  export declare function rerender(element: SimpElement): void;
3
3
  interface IRendererLocker {
4
4
  _isLocked: boolean;
5
- _elements: Set<SimpElement>;
6
- isLocked: boolean;
5
+ _elementStores: Set<SimpElementStore>;
6
+ _last: SimpElementStore | undefined;
7
+ _track(element: SimpElementStore): void;
8
+ _untrack(element: SimpElementStore): void;
7
9
  lock(): void;
8
- track(element: SimpElement): void;
9
10
  flush(): void;
10
11
  }
11
- export declare const syncRerenderLocker: IRendererLocker;
12
- export declare const asyncRerenderLocker: IRendererLocker;
12
+ export declare const batchingRerenderLocker: IRendererLocker;
13
+ export declare const renderingRerenderLocker: IRendererLocker;
13
14
  export {};