@simpreact/simpreact 0.0.5 → 0.0.7

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 (83) hide show
  1. package/LICENSE.txt +1 -1
  2. package/compat/context.d.ts +4 -4
  3. package/compat/context.js +3 -2
  4. package/compat/core.d.ts +4 -0
  5. package/compat/core.js +9 -5
  6. package/compat/dom.d.ts +4 -5
  7. package/compat/dom.js +3 -2
  8. package/compat/hooks.d.ts +16 -13
  9. package/compat/hooks.js +19 -15
  10. package/compat/index.d.ts +12 -9
  11. package/compat/index.js +3 -0
  12. package/compat/renderRuntime.d.ts +6 -0
  13. package/compat/renderRuntime.js +17 -0
  14. package/component/index.d.ts +16 -0
  15. package/component/index.js +164 -0
  16. package/context/index.d.ts +12 -5
  17. package/context/index.js +62 -57
  18. package/core/createElement.d.ts +29 -20
  19. package/core/createElement.js +159 -133
  20. package/core/hostAdapter.d.ts +8 -12
  21. package/core/hostAdapter.js +1 -4
  22. package/core/hostOperations.d.ts +5 -0
  23. package/core/hostOperations.js +15 -0
  24. package/core/index.d.ts +20 -6
  25. package/core/internal.d.ts +5 -0
  26. package/core/internal.js +5 -0
  27. package/core/lifecycleEventBus.d.ts +15 -6
  28. package/core/lifecycleEventBus.js +16 -2
  29. package/core/memo.d.ts +0 -2
  30. package/core/memo.js +1 -3
  31. package/core/mounting.d.ts +6 -9
  32. package/core/mounting.js +177 -86
  33. package/core/mountingChildren.d.ts +4 -0
  34. package/core/mountingChildren.js +47 -0
  35. package/core/patching.d.ts +7 -8
  36. package/core/patching.js +245 -252
  37. package/core/patchingChildren.d.ts +6 -0
  38. package/core/patchingChildren.js +343 -0
  39. package/core/portal.d.ts +1 -1
  40. package/core/portal.js +17 -7
  41. package/core/processStack.d.ts +106 -0
  42. package/core/processStack.js +75 -0
  43. package/core/ref.d.ts +4 -3
  44. package/core/rerender.d.ts +4 -14
  45. package/core/rerender.js +67 -112
  46. package/core/runtime.d.ts +17 -0
  47. package/core/runtime.js +2 -0
  48. package/core/unmounting.d.ts +6 -5
  49. package/core/unmounting.js +58 -57
  50. package/core/unmountingChildren.d.ts +4 -0
  51. package/core/unmountingChildren.js +23 -0
  52. package/core/utils.d.ts +11 -0
  53. package/core/utils.js +168 -0
  54. package/dom/attach-element-to-dom.d.ts +4 -3
  55. package/dom/attach-element-to-dom.js +12 -7
  56. package/dom/domAdapter.js +22 -25
  57. package/dom/events.d.ts +5 -5
  58. package/dom/events.js +33 -16
  59. package/dom/index.d.ts +16 -5
  60. package/dom/index.js +4 -3
  61. package/dom/props/controlled/index.d.ts +3 -3
  62. package/dom/props/controlled/index.js +8 -8
  63. package/dom/props/controlled/input.d.ts +3 -3
  64. package/dom/props/controlled/input.js +57 -34
  65. package/dom/props/controlled/select.d.ts +3 -3
  66. package/dom/props/controlled/select.js +39 -26
  67. package/dom/props/controlled/textarea.d.ts +3 -3
  68. package/dom/props/controlled/textarea.js +57 -34
  69. package/dom/props/dangerInnerHTML.d.ts +5 -5
  70. package/dom/props/dangerInnerHTML.js +10 -15
  71. package/dom/props/props.d.ts +4 -4
  72. package/dom/props/props.js +24 -21
  73. package/dom/render.d.ts +4 -5
  74. package/dom/render.js +38 -34
  75. package/hooks/index.d.ts +15 -13
  76. package/hooks/index.js +154 -157
  77. package/jsx-runtime/index.d.ts +2 -1
  78. package/package.json +9 -1
  79. package/shared/index.d.ts +10 -0
  80. package/shared/index.js +4 -7
  81. package/shared/utils.js +4 -4
  82. package/shared/EventBus.d.ts +0 -18
  83. package/shared/EventBus.js +0 -28
package/hooks/index.js CHANGED
@@ -1,194 +1,191 @@
1
- import { lifecycleEventBus, rerender as _rerender } from '../core/internal.js';
2
- import { callOrGet, noop } from '../shared/index.js';
3
- let currentIndex = 0;
4
- // In runtime this is a nullable variable.
5
- let currentElement;
1
+ import { rerender as _rerender, lifecycleEventBus, SIMP_ELEMENT_FLAG_FC, } from '../core/internal.js';
2
+ import { callOrGet, shallowEqual, } from '../shared/index.js';
3
+ const hooksSpecificStoreByElementStore = new WeakMap();
4
+ function getHooksSpecificStore(store) {
5
+ let hooksSpecificStore = hooksSpecificStoreByElementStore.get(store);
6
+ if (!hooksSpecificStore) {
7
+ hooksSpecificStore = { hooksIndex: 0, hookStates: null, effectsHookStates: null, catchHandlers: null };
8
+ hooksSpecificStoreByElementStore.set(store, hooksSpecificStore);
9
+ }
10
+ return hooksSpecificStore;
11
+ }
12
+ window.__SIMP_HOOKS_SPECIFIC_STORE_BY_ELEMENT_STORE__ = hooksSpecificStoreByElementStore;
6
13
  lifecycleEventBus.subscribe(event => {
7
- if (event.type === 'beforeRender') {
8
- currentElement = event.element;
9
- if (currentElement.store?.catchHandlers) {
10
- currentElement.store.catchHandlers = undefined;
14
+ if ((event.element.flag & SIMP_ELEMENT_FLAG_FC) === 0) {
15
+ return;
16
+ }
17
+ let store = getHooksSpecificStore(event.element.store);
18
+ switch (event.type) {
19
+ case 'beforeRender': {
20
+ store.hooksIndex = 0;
21
+ store.catchHandlers = null;
22
+ store.effectsHookStates = null;
23
+ break;
11
24
  }
12
- if (currentElement.store?.effectsHookStates) {
13
- currentElement.store.effectsHookStates = undefined;
25
+ case 'afterRender': {
26
+ store.hooksIndex = 0;
27
+ break;
14
28
  }
15
- }
16
- if (event.type === 'afterRender' || event.type === 'errored') {
17
- currentElement = null;
18
- currentIndex = 0;
19
- }
20
- if (event.type === 'mounted') {
21
- const element = event.element;
22
- if (element.store?.effectsHookStates) {
23
- const effects = element.store.effectsHookStates;
24
- element.store.effectsHookStates = undefined;
29
+ case 'mounted': {
30
+ if (!store.effectsHookStates) {
31
+ break;
32
+ }
33
+ const effects = store.effectsHookStates;
34
+ store.effectsHookStates = null;
25
35
  for (const state of effects) {
26
- state.cleanup = state.effect() || undefined;
36
+ state.cleanup = state.effect() || null;
27
37
  }
38
+ break;
28
39
  }
29
- }
30
- if (event.type === 'updated') {
31
- const element = event.element;
32
- if (element.store?.effectsHookStates) {
33
- const effects = element.store.effectsHookStates;
34
- element.store.effectsHookStates = undefined;
40
+ case 'updated': {
41
+ if (!store.effectsHookStates) {
42
+ break;
43
+ }
44
+ const effects = store.effectsHookStates;
45
+ store.effectsHookStates = null;
35
46
  for (const state of effects) {
36
47
  if (typeof state.cleanup === 'function') {
37
48
  state.cleanup();
38
49
  }
39
- state.cleanup = state.effect() || undefined;
50
+ state.cleanup = state.effect() || null;
40
51
  }
52
+ break;
41
53
  }
42
- }
43
- if (event.type === 'unmounted') {
44
- const element = event.element;
45
- if (element.store?.hookStates) {
46
- const hookStates = element.store.hookStates;
47
- element.store.hookStates = undefined;
54
+ case 'unmounted': {
55
+ if (!store.hookStates) {
56
+ break;
57
+ }
58
+ const hookStates = store.hookStates;
59
+ store.hookStates = null;
48
60
  for (const state of hookStates) {
49
61
  if (state && 'cleanup' in state && typeof state.cleanup === 'function') {
50
62
  state.cleanup();
51
63
  }
52
64
  }
65
+ break;
53
66
  }
54
- }
55
- if (event.type === 'errored') {
56
- function handleError(element, error) {
57
- element = findElementWithCatchHandlers(element);
58
- if (!element) {
59
- throw new Error('Error occurred during rendering a component', { cause: error });
67
+ case 'errored': {
68
+ store.hooksIndex = 0;
69
+ if (event.handled) {
70
+ break;
60
71
  }
61
- try {
62
- for (const state of element.store.catchHandlers) {
63
- state(error);
72
+ let element = event.element;
73
+ let curError = event.error;
74
+ let catchers = null;
75
+ while (element) {
76
+ if ((element.flag & SIMP_ELEMENT_FLAG_FC) === 0) {
77
+ element = element.parent;
78
+ continue;
79
+ }
80
+ store = getHooksSpecificStore(element.store);
81
+ catchers = store.catchHandlers;
82
+ if (!catchers) {
83
+ element = element.parent;
84
+ continue;
85
+ }
86
+ try {
87
+ for (let i = 0; i < catchers.length; i++) {
88
+ catchers[i](curError);
89
+ }
90
+ event.handled = true;
91
+ break;
92
+ }
93
+ catch (error) {
94
+ element = element.parent;
95
+ curError = error;
64
96
  }
65
- }
66
- catch (error) {
67
- handleError(element.parent, error);
68
97
  }
69
98
  }
70
- handleError(event.element, event.error);
71
99
  }
72
100
  });
73
- function findElementWithCatchHandlers(element) {
74
- let temp = element;
75
- while (temp != null) {
76
- if (temp.store?.catchHandlers) {
77
- return temp;
101
+ export function createUseRef(renderRuntime) {
102
+ return initialValue => {
103
+ const store = getHooksSpecificStore(renderRuntime.currentRenderingFCElement.store);
104
+ const hookStates = getOrCreateHookStates(store);
105
+ if (!hookStates[store.hooksIndex]) {
106
+ hookStates[store.hooksIndex] = { current: initialValue };
78
107
  }
79
- temp = temp.parent;
80
- }
81
- return null;
108
+ return hookStates[store.hooksIndex++];
109
+ };
82
110
  }
83
- export function useRef(initialValue) {
84
- const hookStates = getOrCreateHookStates(currentElement);
85
- if (!hookStates[currentIndex]) {
86
- hookStates[currentIndex] = { current: initialValue };
87
- }
88
- return hookStates[currentIndex++];
89
- }
90
- export function useRerender() {
91
- const hookStates = getOrCreateHookStates(currentElement);
92
- if (!hookStates[currentIndex]) {
93
- const elementStore = currentElement.store;
94
- hookStates[currentIndex] = function rerender() {
95
- elementStore.forceRender = true;
96
- _rerender(elementStore.latestElement);
97
- };
98
- }
99
- return hookStates[currentIndex++];
100
- }
101
- export function useState(initialState) {
102
- const hookStates = getOrCreateHookStates(currentElement);
103
- if (!hookStates[currentIndex]) {
104
- const elementStore = currentElement.store;
105
- const state = (hookStates[currentIndex] = [undefined, undefined]);
106
- state[0] = callOrGet(initialState);
107
- state[1] = function dispatch(action) {
108
- const nextValue = callOrGet(action, state[0]);
109
- if (Object.is(state[0], nextValue)) {
110
- return;
111
- }
112
- state[0] = nextValue;
113
- elementStore.forceRender = true;
114
- _rerender(elementStore.latestElement);
115
- };
116
- }
117
- return hookStates[currentIndex++];
118
- }
119
- export function useEffect(effect, deps) {
120
- const hookStates = getOrCreateHookStates(currentElement);
121
- let state = hookStates[currentIndex];
122
- if (!state) {
123
- state = hookStates[currentIndex] = { effect };
124
- }
125
- if (!areDepsEqual(deps, state.deps)) {
126
- state.effect = effect;
127
- state.deps = deps;
128
- getOrCreateEffectHookStates(currentElement).push(state);
129
- }
130
- currentIndex++;
131
- }
132
- export function useMounted(effect) {
133
- const hookStates = getOrCreateHookStates(currentElement);
134
- if (!hookStates[currentIndex]) {
135
- hookStates[currentIndex] = { effect };
136
- getOrCreateEffectHookStates(currentElement).push(hookStates[currentIndex]);
137
- }
138
- currentIndex++;
111
+ export function createUseRerender(renderRuntime) {
112
+ return () => {
113
+ const store = getHooksSpecificStore(renderRuntime.currentRenderingFCElement.store);
114
+ const hookStates = getOrCreateHookStates(store);
115
+ if (!hookStates[store.hooksIndex]) {
116
+ const elementStore = renderRuntime.currentRenderingFCElement.store;
117
+ hookStates[store.hooksIndex] = function rerender() {
118
+ _rerender(elementStore, renderRuntime);
119
+ };
120
+ }
121
+ return hookStates[store.hooksIndex++];
122
+ };
139
123
  }
140
- export function useUnmounted(cleanup) {
141
- const hookStates = getOrCreateHookStates(currentElement);
142
- if (!hookStates[currentIndex]) {
143
- hookStates[currentIndex] = { cleanup, effect: noop };
144
- }
145
- currentIndex++;
124
+ export function createUseState(renderRuntime) {
125
+ return (initialState => {
126
+ const store = getHooksSpecificStore(renderRuntime.currentRenderingFCElement.store);
127
+ const hookStates = getOrCreateHookStates(store);
128
+ if (!hookStates[store.hooksIndex]) {
129
+ const elementStore = renderRuntime.currentRenderingFCElement.store;
130
+ const state = (hookStates[store.hooksIndex] = [undefined, undefined]);
131
+ state[0] = callOrGet(initialState);
132
+ state[1] = function dispatch(action) {
133
+ const nextValue = callOrGet(action, state[0]);
134
+ if (Object.is(state[0], nextValue)) {
135
+ return;
136
+ }
137
+ state[0] = nextValue;
138
+ _rerender(elementStore, renderRuntime);
139
+ };
140
+ }
141
+ return hookStates[store.hooksIndex++];
142
+ });
146
143
  }
147
- export function useCatch(cb) {
148
- if (!currentElement.store) {
149
- currentElement.store = {};
150
- }
151
- if (!currentElement.store.catchHandlers) {
152
- currentElement.store.catchHandlers = [];
153
- }
154
- currentElement.store.catchHandlers.push(cb);
144
+ export function createUseEffect(renderRuntime) {
145
+ return (effect, deps) => {
146
+ const store = getHooksSpecificStore(renderRuntime.currentRenderingFCElement.store);
147
+ const hookStates = getOrCreateHookStates(store);
148
+ let state = hookStates[store.hooksIndex];
149
+ if (!state) {
150
+ state = hookStates[store.hooksIndex] = {
151
+ effect,
152
+ deps: null,
153
+ cleanup: null,
154
+ };
155
+ }
156
+ if (!shallowEqual(deps, state.deps)) {
157
+ state.effect = effect;
158
+ state.deps = deps || null;
159
+ getOrCreateEffectHookStates(store).push(state);
160
+ }
161
+ store.hooksIndex++;
162
+ };
155
163
  }
156
- export function areDepsEqual(nextDeps, prevDeps) {
157
- if (nextDeps == null || prevDeps == null || nextDeps.length !== prevDeps.length) {
158
- return false;
159
- }
160
- for (let i = 0; i < prevDeps.length; i++) {
161
- if (!Object.is(nextDeps[i], prevDeps[i])) {
162
- return false;
164
+ export function createUseCatch(renderRuntime) {
165
+ return cb => {
166
+ const store = getHooksSpecificStore(renderRuntime.currentRenderingFCElement.store);
167
+ if (!store.catchHandlers) {
168
+ store.catchHandlers = [];
163
169
  }
164
- }
165
- return true;
170
+ store.catchHandlers.push(cb);
171
+ };
166
172
  }
167
- function getOrCreateHookStates(element) {
168
- if (!element.store) {
169
- element.store = {};
173
+ function getOrCreateHookStates(store) {
174
+ if (!store.hookStates) {
175
+ store.hookStates = [];
170
176
  }
171
- if (!element.store.hookStates) {
172
- element.store.hookStates = [];
173
- }
174
- return element.store.hookStates;
177
+ return store.hookStates;
175
178
  }
176
- function getOrCreateEffectHookStates(element) {
177
- if (!element.store) {
178
- element.store = {};
179
- }
180
- if (!element.store.effectsHookStates) {
181
- element.store.effectsHookStates = [];
179
+ function getOrCreateEffectHookStates(store) {
180
+ if (!store.effectsHookStates) {
181
+ store.effectsHookStates = [];
182
182
  }
183
- return element.store.effectsHookStates;
183
+ return store.effectsHookStates;
184
184
  }
185
185
  export default {
186
- useRef,
187
- useRerender,
188
- useState,
189
- useEffect,
190
- useMounted,
191
- useUnmounted,
192
- useCatch,
193
- areDepsEqual,
186
+ createUseRef,
187
+ createUseRerender,
188
+ createUseState,
189
+ createUseEffect,
190
+ createUseCatch,
194
191
  };
@@ -1,5 +1,4 @@
1
1
  import type { FC, Fragment as FragmentType, Key, SimpElement } from '../core/index.js';
2
- import type { Maybe } from '../shared/index.js';
3
2
  import type {
4
3
  AnchorHTMLAttributes,
5
4
  AreaHTMLAttributes,
@@ -55,6 +54,7 @@ import type {
55
54
  VideoHTMLAttributes,
56
55
  WebViewHTMLAttributes,
57
56
  } from '../dom/index.js';
57
+ import type { Maybe } from '../shared/index.js';
58
58
 
59
59
  declare function jsx<P = {}>(type: string | FC<P>, props?: P, key?: Maybe<Key>): SimpElement<P>;
60
60
 
@@ -64,6 +64,7 @@ export { jsx as jsxs, jsx as jsxDEV };
64
64
  export { Fragment };
65
65
 
66
66
  declare global {
67
+ // biome-ignore lint/style/noNamespace: React-like API requirement.
67
68
  namespace JSX {
68
69
  interface IntrinsicElements {
69
70
  // HTML
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simpreact/simpreact",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "",
5
5
  "homepage": "https://github.com/dPaskhin/simpreact#readme",
6
6
  "main": "./core/index.js",
@@ -17,6 +17,14 @@
17
17
  "./compat/*": {
18
18
  "import": "./compat/index.js"
19
19
  },
20
+ "./component": {
21
+ "import": "./component/index.js",
22
+ "types": "./component/index.d.ts"
23
+ },
24
+ "./context/*": {
25
+ "import": "./context/index.js",
26
+ "types": "./context/index.d.ts"
27
+ },
20
28
  "./internal": {
21
29
  "import": "./core/internal.js",
22
30
  "types": "./core/internal.d.ts"
package/shared/index.d.ts CHANGED
@@ -16,6 +16,16 @@ declare class EventBus<Event = void> {
16
16
  public subscribe(subscriber: Subscriber<Event>): () => void;
17
17
  }
18
18
 
19
+ export type Cleanup = () => void;
20
+ export type Effect = () => void | Cleanup;
21
+ export type DependencyList = readonly unknown[];
22
+
23
+ export interface EffectState {
24
+ effect: Effect;
25
+ cleanup: Nullable<Cleanup>;
26
+ deps: Nullable<DependencyList>;
27
+ }
28
+
19
29
  declare function isSimpText(value: unknown): value is SimpText;
20
30
 
21
31
  declare function noop(): void;
package/shared/index.js CHANGED
@@ -1,14 +1,11 @@
1
- import { EventBus } from './EventBus.js';
2
1
  import { emptyArray, emptyMap, emptyObject } from './lang.js';
3
2
  import { callOrGet, isSimpText, noop, shallowEqual } from './utils.js';
4
- export { emptyObject, emptyMap, emptyArray, isSimpText, EventBus, noop, callOrGet, shallowEqual };
3
+ export { emptyObject, emptyMap, emptyArray, isSimpText, noop, callOrGet, shallowEqual };
5
4
  export default {
6
5
  isSimpText,
7
- EMPTY_MAP: emptyMap,
8
- EMPTY_ARRAY: emptyArray,
9
- EMPTY_OBJECT: emptyObject,
10
- EventBus,
6
+ emptyMap,
7
+ emptyArray,
8
+ emptyObject,
11
9
  noop,
12
10
  callOrGet,
13
- emptyObject,
14
11
  };
package/shared/utils.js CHANGED
@@ -22,17 +22,17 @@ export function shallowEqual(objA, objB) {
22
22
  if (Object.is(objA, objB)) {
23
23
  return true;
24
24
  }
25
- if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
25
+ if (typeof objA !== 'object' || objA == null || typeof objB !== 'object' || objB == null) {
26
26
  return false;
27
27
  }
28
- const keysA = Object.keys(objA);
29
- const keysB = Object.keys(objB);
28
+ const keysA = Reflect.ownKeys(objA);
29
+ const keysB = Reflect.ownKeys(objB);
30
30
  if (keysA.length !== keysB.length) {
31
31
  return false;
32
32
  }
33
33
  for (let i = 0; i < keysA.length; i++) {
34
34
  const currentKey = keysA[i];
35
- if (!Object.prototype.hasOwnProperty.call(objB, currentKey) || !Object.is(objA[currentKey], objB[currentKey])) {
35
+ if (!Object.hasOwn(objB, currentKey) || !Object.is(objA[currentKey], objB[currentKey])) {
36
36
  return false;
37
37
  }
38
38
  }
@@ -1,18 +0,0 @@
1
- type Subscriber<Event> = (event: Event) => boolean | void;
2
- export declare class EventBus<Event = void> {
3
- private _subscribers;
4
- /**
5
- * Synchronously invokes subscribers and passes the event.
6
- *
7
- * @param event The event to publish.
8
- */
9
- publish(event: Event): void;
10
- /**
11
- * Adds a subscriber to the event bus. Subscriber would receive all events published via {@link EventBus.publish}.
12
- *
13
- * @param subscriber The subscriber callback.
14
- * @returns The callback that unsubscribes the subscriber from the event bus.
15
- */
16
- subscribe(subscriber: Subscriber<Event>): () => void;
17
- }
18
- export {};
@@ -1,28 +0,0 @@
1
- export class EventBus {
2
- _subscribers = [];
3
- /**
4
- * Synchronously invokes subscribers and passes the event.
5
- *
6
- * @param event The event to publish.
7
- */
8
- publish(event) {
9
- for (const subscriber of this._subscribers) {
10
- subscriber(event);
11
- }
12
- }
13
- /**
14
- * Adds a subscriber to the event bus. Subscriber would receive all events published via {@link EventBus.publish}.
15
- *
16
- * @param subscriber The subscriber callback.
17
- * @returns The callback that unsubscribes the subscriber from the event bus.
18
- */
19
- subscribe(subscriber) {
20
- const subscribers = this._subscribers;
21
- if (subscribers.indexOf(subscriber) === -1) {
22
- subscribers.push(subscriber);
23
- }
24
- return () => {
25
- subscribers.splice(subscribers.indexOf(subscriber), 1);
26
- };
27
- }
28
- }