@simpreact/simpreact 0.0.2 → 0.0.3

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.
@@ -0,0 +1,46 @@
1
+ import * as SimpReactInternal from '../core/internal.js';
2
+ import { identity } from './utils.js';
3
+ export declare const Children: {
4
+ map(children: SimpReactInternal.SimpNode, fn: (child: SimpReactInternal.SimpNode, index: number) => SimpReactInternal.SimpNode): SimpReactInternal.SimpNode[];
5
+ forEach(children: SimpReactInternal.SimpNode, fn: (child: SimpReactInternal.SimpNode, index: number) => void): void;
6
+ count(children: SimpReactInternal.SimpNode): number;
7
+ toArray(children: SimpReactInternal.SimpNode): SimpReactInternal.SimpNode[];
8
+ only(children: SimpReactInternal.SimpNode): SimpReactInternal.SimpElement;
9
+ };
10
+ export declare function cloneElement(element: SimpReactInternal.SimpElement, props?: any, ...children: SimpReactInternal.SimpNode[]): SimpReactInternal.SimpElement;
11
+ export declare function isValidElement(element: unknown): element is SimpReactInternal.SimpElement;
12
+ export declare function Suspense(props: {
13
+ fallback: SimpReactInternal.SimpNode;
14
+ children: SimpReactInternal.SimpNode;
15
+ }): SimpReactInternal.SimpNode;
16
+ export declare function forwardRef<P, T>(Component: (props: P, ref: SimpReactInternal.Ref<T>) => any): (props: P) => any;
17
+ export declare const Fragment: SimpReactInternal.Fragment;
18
+ export declare const createElement: typeof SimpReactInternal.createElement;
19
+ export declare const createContext: typeof SimpReactInternal.createContext;
20
+ export declare const createPortal: typeof SimpReactInternal.createPortal;
21
+ export declare const memo: typeof identity;
22
+ export declare const flushSync: typeof identity;
23
+ export declare class Component {
24
+ constructor();
25
+ }
26
+ declare const _default: {
27
+ Children: {
28
+ map(children: SimpReactInternal.SimpNode, fn: (child: SimpReactInternal.SimpNode, index: number) => SimpReactInternal.SimpNode): SimpReactInternal.SimpNode[];
29
+ forEach(children: SimpReactInternal.SimpNode, fn: (child: SimpReactInternal.SimpNode, index: number) => void): void;
30
+ count(children: SimpReactInternal.SimpNode): number;
31
+ toArray(children: SimpReactInternal.SimpNode): SimpReactInternal.SimpNode[];
32
+ only(children: SimpReactInternal.SimpNode): SimpReactInternal.SimpElement;
33
+ };
34
+ cloneElement: typeof cloneElement;
35
+ isValidElement: typeof isValidElement;
36
+ Suspense: typeof Suspense;
37
+ forwardRef: typeof forwardRef;
38
+ Fragment: SimpReactInternal.Fragment;
39
+ createElement: typeof SimpReactInternal.createElement;
40
+ createContext: typeof SimpReactInternal.createContext;
41
+ createPortal: typeof SimpReactInternal.createPortal;
42
+ memo: typeof identity;
43
+ flushSync: typeof identity;
44
+ Component: typeof Component;
45
+ };
46
+ export default _default;
package/compat/core.js ADDED
@@ -0,0 +1,93 @@
1
+ import * as SimpReactInternal from '../core/internal.js';
2
+ import * as SimpReactHooks from '../hooks/index.js';
3
+ import { identity } from './utils.js';
4
+ export const Children = {
5
+ map(children, fn) {
6
+ return Children.toArray(children).map(fn);
7
+ },
8
+ forEach(children, fn) {
9
+ Children.toArray(children).forEach(fn);
10
+ },
11
+ count(children) {
12
+ return Children.toArray(children).length;
13
+ },
14
+ toArray(children) {
15
+ const result = [];
16
+ function traverse(node) {
17
+ if (node == null || typeof node === 'boolean') {
18
+ return;
19
+ }
20
+ if (Array.isArray(node)) {
21
+ for (const child of node) {
22
+ traverse(child);
23
+ }
24
+ return;
25
+ }
26
+ result.push(node);
27
+ }
28
+ traverse(children);
29
+ return result;
30
+ },
31
+ only(children) {
32
+ const array = Children.toArray(children);
33
+ if (array.length !== 1 || !isValidElement(array[0])) {
34
+ throw new Error('Children.only expected a single SimpElement child.');
35
+ }
36
+ return array[0];
37
+ },
38
+ };
39
+ export function cloneElement(element, props, ...children) {
40
+ if (!isValidElement(element)) {
41
+ throw new Error(`cloneElement: expected a SimpElement, got ${element}`);
42
+ }
43
+ if (element.flag === 'PORTAL') {
44
+ throw new Error('cloneElement: the argument must be a SimpElement, but you passed a portal instead.');
45
+ }
46
+ return SimpReactInternal.createElement(element.flag === 'FRAGMENT' ? SimpReactInternal.Fragment : element.type, Object.assign({}, element.props, props), arguments.length > 2 ? children : props.children || element.children);
47
+ }
48
+ export function isValidElement(element) {
49
+ return typeof element === 'object' && element !== null && 'flag' in element;
50
+ }
51
+ export function Suspense(props) {
52
+ const [isSuspended, setIsSuspended] = SimpReactHooks.useState(false);
53
+ SimpReactHooks.useCatch(error => {
54
+ if (isSuspended) {
55
+ return;
56
+ }
57
+ if (error instanceof Promise) {
58
+ setIsSuspended(true);
59
+ error.then(() => setIsSuspended(false));
60
+ }
61
+ });
62
+ return isSuspended ? props.fallback : props.children;
63
+ }
64
+ export function forwardRef(Component) {
65
+ return function Forwarded(props) {
66
+ return Component(props, props?.ref || null);
67
+ };
68
+ }
69
+ export const Fragment = SimpReactInternal.Fragment;
70
+ export const createElement = SimpReactInternal.createElement;
71
+ export const createContext = SimpReactInternal.createContext;
72
+ export const createPortal = SimpReactInternal.createPortal;
73
+ export const memo = identity;
74
+ export const flushSync = identity;
75
+ export class Component {
76
+ constructor() {
77
+ throw new Error('Not implemented.');
78
+ }
79
+ }
80
+ export default {
81
+ Children,
82
+ cloneElement,
83
+ isValidElement,
84
+ Suspense,
85
+ forwardRef,
86
+ Fragment,
87
+ createElement,
88
+ createContext,
89
+ createPortal,
90
+ memo,
91
+ flushSync,
92
+ Component,
93
+ };
@@ -0,0 +1,11 @@
1
+ import * as SimpReactDOM from '../dom/index.js';
2
+ import * as SimpReactShared from '../shared/index.js';
3
+ export declare const hydrate: typeof SimpReactShared.noop;
4
+ export declare const render: typeof SimpReactDOM.render;
5
+ export declare const createRoot: typeof SimpReactDOM.createRoot;
6
+ declare const _default: {
7
+ hydrate: typeof SimpReactShared.noop;
8
+ render: typeof SimpReactDOM.render;
9
+ createRoot: typeof SimpReactDOM.createRoot;
10
+ };
11
+ export default _default;
package/compat/dom.js ADDED
@@ -0,0 +1,10 @@
1
+ import * as SimpReactDOM from '../dom/index.js';
2
+ import * as SimpReactShared from '../shared/index.js';
3
+ export const hydrate = SimpReactShared.noop;
4
+ export const render = SimpReactDOM.render;
5
+ export const createRoot = SimpReactDOM.createRoot;
6
+ export default {
7
+ hydrate,
8
+ render,
9
+ createRoot,
10
+ };
@@ -0,0 +1,26 @@
1
+ import * as SimpReactHooks from '../hooks/index.js';
2
+ export declare function useSyncExternalStore<T>(subscribe: (callback: () => void) => () => void, getSnapshot: () => T): T;
3
+ export declare function useReducer<R extends (state: any, action: any) => any, I>(reducer: R, initializerArg: I, initializer?: (arg: I) => ReturnType<R>): [ReturnType<R>, (action: Parameters<R>[1]) => void];
4
+ export declare function useId(prefix?: string): string;
5
+ export declare function useMemo<T>(factory: () => T, deps: SimpReactHooks.DependencyList): T;
6
+ export declare function useCallback<T>(cb: T, deps: SimpReactHooks.DependencyList): T;
7
+ export declare const useState: typeof SimpReactHooks.useState;
8
+ export declare const useEffect: typeof SimpReactHooks.useEffect;
9
+ export declare const useLayoutEffect: typeof SimpReactHooks.useEffect;
10
+ export declare const useInsertionEffect: typeof SimpReactHooks.useEffect;
11
+ export declare const useRef: typeof SimpReactHooks.useRef;
12
+ export declare const useContext: typeof SimpReactHooks.useContext;
13
+ declare const _default: {
14
+ useSyncExternalStore: typeof useSyncExternalStore;
15
+ useReducer: typeof useReducer;
16
+ useId: typeof useId;
17
+ useMemo: typeof useMemo;
18
+ useCallback: typeof useCallback;
19
+ useState: typeof SimpReactHooks.useState;
20
+ useEffect: typeof SimpReactHooks.useEffect;
21
+ useLayoutEffect: typeof SimpReactHooks.useEffect;
22
+ useInsertionEffect: typeof SimpReactHooks.useEffect;
23
+ useRef: typeof SimpReactHooks.useRef;
24
+ useContext: typeof SimpReactHooks.useContext;
25
+ };
26
+ export default _default;
@@ -0,0 +1,77 @@
1
+ import * as SimpReactHooks from '../hooks/index.js';
2
+ export function useSyncExternalStore(subscribe, getSnapshot) {
3
+ const rerender = SimpReactHooks.useRerender();
4
+ const lastSnapshotRef = SimpReactHooks.useRef(getSnapshot());
5
+ SimpReactHooks.useEffect(() => {
6
+ function checkForUpdates() {
7
+ const nextSnapshot = getSnapshot();
8
+ if (!Object.is(lastSnapshotRef.current, nextSnapshot)) {
9
+ lastSnapshotRef.current = nextSnapshot;
10
+ rerender();
11
+ }
12
+ }
13
+ const unsubscribe = subscribe(checkForUpdates);
14
+ checkForUpdates();
15
+ return unsubscribe;
16
+ }, [subscribe, getSnapshot]);
17
+ return lastSnapshotRef.current;
18
+ }
19
+ export function useReducer(reducer, initializerArg, initializer) {
20
+ const rerender = SimpReactHooks.useRerender();
21
+ const reducerRef = SimpReactHooks.useRef(reducer);
22
+ reducerRef.current = reducer;
23
+ const stateRef = SimpReactHooks.useRef([
24
+ initializer ? initializer(initializerArg) : initializerArg,
25
+ function dispatch(action) {
26
+ const newState = reducerRef.current(stateRef.current[0], action);
27
+ if (Object.is(newState, stateRef.current[0])) {
28
+ return;
29
+ }
30
+ stateRef.current[0] = newState;
31
+ rerender();
32
+ },
33
+ ]);
34
+ return stateRef.current;
35
+ }
36
+ let globalId = 0;
37
+ export function useId(prefix = 'id') {
38
+ const idRef = SimpReactHooks.useRef('');
39
+ if (idRef.current === '') {
40
+ globalId += 1;
41
+ idRef.current = `${prefix}-${globalId}`;
42
+ }
43
+ return idRef.current;
44
+ }
45
+ export function useMemo(factory, deps) {
46
+ const ref = SimpReactHooks.useRef({
47
+ deps: undefined,
48
+ value: undefined,
49
+ });
50
+ if (!SimpReactHooks.areDepsEqual(ref.current.deps, deps)) {
51
+ ref.current.value = factory();
52
+ ref.current.deps = deps;
53
+ }
54
+ return ref.current.value;
55
+ }
56
+ export function useCallback(cb, deps) {
57
+ return useMemo(() => cb, deps);
58
+ }
59
+ export const useState = SimpReactHooks.useState;
60
+ export const useEffect = SimpReactHooks.useEffect;
61
+ export const useLayoutEffect = SimpReactHooks.useEffect;
62
+ export const useInsertionEffect = SimpReactHooks.useEffect;
63
+ export const useRef = SimpReactHooks.useRef;
64
+ export const useContext = SimpReactHooks.useContext;
65
+ export default {
66
+ useSyncExternalStore,
67
+ useReducer,
68
+ useId,
69
+ useMemo,
70
+ useCallback,
71
+ useState,
72
+ useEffect,
73
+ useLayoutEffect,
74
+ useInsertionEffect,
75
+ useRef,
76
+ useContext,
77
+ };
@@ -0,0 +1,42 @@
1
+ export * from './core.js';
2
+ export * from './dom.js';
3
+ export * from './hooks.js';
4
+ export * from './jsx-runtime.js';
5
+ declare const _default: {
6
+ jsx: typeof import("../jsx-runtime/index.js").jsx;
7
+ jsxDEV: typeof import("../jsx-runtime/index.js").jsx;
8
+ jsxs: typeof import("../jsx-runtime/index.js").jsx;
9
+ useSyncExternalStore: typeof import("./hooks.js").useSyncExternalStore;
10
+ useReducer: typeof import("./hooks.js").useReducer;
11
+ useId: typeof import("./hooks.js").useId;
12
+ useMemo: typeof import("./hooks.js").useMemo;
13
+ useCallback: typeof import("./hooks.js").useCallback;
14
+ useState: typeof import("../hooks/index.js").useState;
15
+ useEffect: typeof import("../hooks/index.js").useEffect;
16
+ useLayoutEffect: typeof import("../hooks/index.js").useEffect;
17
+ useInsertionEffect: typeof import("../hooks/index.js").useEffect;
18
+ useRef: typeof import("../hooks/index.js").useRef;
19
+ useContext: typeof import("../hooks/index.js").useContext;
20
+ hydrate: typeof import("../shared/utils.js").noop;
21
+ render: typeof import("../dom/render.js").render;
22
+ createRoot: typeof import("../dom/render.js").createRoot;
23
+ Children: {
24
+ map(children: import("../core/createElement.js").SimpNode, fn: (child: import("../core/createElement.js").SimpNode, index: number) => import("../core/createElement.js").SimpNode): import("../core/createElement.js").SimpNode[];
25
+ forEach(children: import("../core/createElement.js").SimpNode, fn: (child: import("../core/createElement.js").SimpNode, index: number) => void): void;
26
+ count(children: import("../core/createElement.js").SimpNode): number;
27
+ toArray(children: import("../core/createElement.js").SimpNode): import("../core/createElement.js").SimpNode[];
28
+ only(children: import("../core/createElement.js").SimpNode): import("../core/createElement.js").SimpElement;
29
+ };
30
+ cloneElement: typeof import("./core.js").cloneElement;
31
+ isValidElement: typeof import("./core.js").isValidElement;
32
+ Suspense: typeof import("./core.js").Suspense;
33
+ forwardRef: typeof import("./core.js").forwardRef;
34
+ Fragment: import("../core/fragment.js").Fragment;
35
+ createElement: typeof import("../core/createElement.js").createElement;
36
+ createContext: typeof import("../core/context.js").createContext;
37
+ createPortal: typeof import("../core/portal.js").createPortal;
38
+ memo: typeof import("./utils.js").identity;
39
+ flushSync: typeof import("./utils.js").identity;
40
+ Component: typeof import("./core.js").Component;
41
+ };
42
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import coreDefault from './core.js';
2
+ import domDefault from './dom.js';
3
+ import hooksDefault from './hooks.js';
4
+ import jsxRuntimeDefault from './jsx-runtime.js';
5
+ export * from './core.js';
6
+ export * from './dom.js';
7
+ export * from './hooks.js';
8
+ export * from './jsx-runtime.js';
9
+ export default {
10
+ ...coreDefault,
11
+ ...domDefault,
12
+ ...hooksDefault,
13
+ ...jsxRuntimeDefault,
14
+ };
@@ -0,0 +1,10 @@
1
+ import * as SimpReactJSXRuntime from '../jsx-runtime/index.js';
2
+ export declare const jsx: typeof SimpReactJSXRuntime.jsx;
3
+ export declare const jsxDEV: typeof SimpReactJSXRuntime.jsx;
4
+ export declare const jsxs: typeof SimpReactJSXRuntime.jsx;
5
+ declare const _default: {
6
+ jsx: typeof SimpReactJSXRuntime.jsx;
7
+ jsxDEV: typeof SimpReactJSXRuntime.jsx;
8
+ jsxs: typeof SimpReactJSXRuntime.jsx;
9
+ };
10
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import * as SimpReactJSXRuntime from '../jsx-runtime/index.js';
2
+ export const jsx = SimpReactJSXRuntime.jsx;
3
+ export const jsxDEV = SimpReactJSXRuntime.jsxDEV;
4
+ export const jsxs = SimpReactJSXRuntime.jsxs;
5
+ export default {
6
+ jsx,
7
+ jsxDEV,
8
+ jsxs,
9
+ };
@@ -0,0 +1 @@
1
+ export declare function identity(value: any): any;
@@ -0,0 +1,3 @@
1
+ export function identity(value) {
2
+ return value;
3
+ }
@@ -17,7 +17,7 @@ export interface SimpElement {
17
17
  flag: SimpElementFlag;
18
18
  parent: Nullable<SimpElement>;
19
19
  key?: Maybe<Key>;
20
- type?: Maybe<string | FunctionComponent>;
20
+ type?: string | FunctionComponent;
21
21
  props?: any;
22
22
  children?: Maybe<SimpNode>;
23
23
  className?: Maybe<string>;
@@ -31,7 +31,9 @@ export function createElement(type, props, ...children) {
31
31
  definedChildren = props[propName];
32
32
  }
33
33
  }
34
- else if (propName === 'ref') {
34
+ else if (propName === 'ref' &&
35
+ // Handle only a callback ref or an object ref with `current`.
36
+ props[propName]) {
35
37
  ref = {
36
38
  value: props[propName],
37
39
  };
package/core/index.d.ts CHANGED
@@ -4,9 +4,9 @@ export type ComponentType<P = {}> = FunctionComponent<P>;
4
4
 
5
5
  export type RefObject<T> = { current: T };
6
6
  export type RefCallback<T> = {
7
- bivarianceHack(instance: T | null): (() => void | undefined) | void;
7
+ bivarianceHack(instance: T): (() => void | undefined) | void;
8
8
  }['bivarianceHack'];
9
- export type Ref<T> = RefCallback<T> | RefObject<T> | null;
9
+ export type Ref<T> = RefCallback<T> | RefObject<T | null> | null;
10
10
 
11
11
  export type Key = string | number | bigint;
12
12
 
@@ -6,7 +6,11 @@ export type LifecycleEvent = {
6
6
  phase: 'mounting' | 'updating';
7
7
  } | {
8
8
  type: 'afterRender';
9
+ element: SimpElement;
9
10
  phase: 'mounting' | 'updating';
11
+ } | {
12
+ type: 'triedToRerender';
13
+ element: SimpElement;
10
14
  } | {
11
15
  type: 'mounted';
12
16
  element: SimpElement;
package/core/mounting.js CHANGED
@@ -3,6 +3,7 @@ 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
+ import { batchingRerenderLocker } from './rerender.js';
6
7
  export function mount(element, parentReference, nextReference, contextMap, hostNamespace) {
7
8
  if (element.flag === 'TEXT') {
8
9
  mountTextElement(element, parentReference, nextReference);
@@ -74,15 +75,41 @@ export function mountFunctionalElement(element, parentReference, nextReference,
74
75
  }
75
76
  // FC element always has Maybe<SimpElement> children due to normalization process.
76
77
  let children;
78
+ let triedToRerenderUnsubscribe;
77
79
  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' });
80
+ let triedToRerender = false;
81
+ let rerenderCounter = 0;
82
+ triedToRerenderUnsubscribe = lifecycleEventBus.subscribe(event => {
83
+ if (event.type === 'triedToRerender' && event.element === element) {
84
+ triedToRerender = true;
85
+ }
86
+ });
87
+ do {
88
+ triedToRerender = false;
89
+ if (++rerenderCounter >= 25) {
90
+ lifecycleEventBus.publish({
91
+ type: 'errored',
92
+ element,
93
+ error: new Error('Too many re-renders. SimpReact limits the number of renders to prevent an infinite loop.'),
94
+ phase: 'mounting',
95
+ });
96
+ return;
97
+ }
98
+ lifecycleEventBus.publish({ type: 'beforeRender', element, phase: 'mounting' });
99
+ batchingRerenderLocker.lock();
100
+ children = element.type(element.props || emptyObject);
101
+ batchingRerenderLocker.flush();
102
+ lifecycleEventBus.publish({ type: 'afterRender', element, phase: 'mounting' });
103
+ } while (triedToRerender);
104
+ children = normalizeRoot(children, false);
81
105
  }
82
106
  catch (error) {
83
107
  lifecycleEventBus.publish({ type: 'errored', element, error, phase: 'mounting' });
84
108
  return;
85
109
  }
110
+ finally {
111
+ triedToRerenderUnsubscribe();
112
+ }
86
113
  if (children) {
87
114
  children.parent = element;
88
115
  mount((element.children = children), parentReference, nextReference, contextMap, hostNamespace);
package/core/patching.js CHANGED
@@ -5,6 +5,7 @@ import { clearElementHostReference, remove, unmount } from './unmounting.js';
5
5
  import { mount, mountArrayChildren } from './mounting.js';
6
6
  import { applyRef } from './ref.js';
7
7
  import { lifecycleEventBus } from './lifecycleEventBus.js';
8
+ import { batchingRerenderLocker } from './rerender.js';
8
9
  export function patch(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace) {
9
10
  if (prevElement.type !== nextElement.type || prevElement.key !== nextElement.key) {
10
11
  replaceWithNewElement(prevElement, nextElement, parentReference, contextMap, hostNamespace);
@@ -59,29 +60,69 @@ function patchHostElement(prevElement, nextElement, contextMap, hostNamespace) {
59
60
  applyRef(nextElement);
60
61
  }
61
62
  function patchFunctionalComponent(prevElement, nextElement, parentReference, nextReference, contextMap, hostNamespace) {
62
- (nextElement.store = prevElement.store ||= {}).latestElement = nextElement;
63
+ const prevStore = !prevElement.store || prevElement.unmounted
64
+ ? { hostNamespace: prevElement.store?.hostNamespace }
65
+ : prevElement.store;
66
+ prevStore.latestElement = nextElement;
67
+ nextElement.store = prevStore;
63
68
  if (hostNamespace) {
64
69
  nextElement.store.hostNamespace = hostNamespace;
65
70
  }
66
71
  if (contextMap) {
67
72
  nextElement.contextMap = contextMap;
68
73
  }
74
+ // FC element always has Maybe<SimpElement> children due to normalization process.
69
75
  let nextChildren;
76
+ let triedToRerenderUnsubscribe;
70
77
  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' });
78
+ let triedToRerender = false;
79
+ let rerenderCounter = 0;
80
+ triedToRerenderUnsubscribe = lifecycleEventBus.subscribe(event => {
81
+ if (event.type === 'triedToRerender' && event.element === nextElement) {
82
+ triedToRerender = true;
83
+ }
84
+ });
85
+ do {
86
+ triedToRerender = false;
87
+ if (++rerenderCounter >= 25) {
88
+ lifecycleEventBus.publish({
89
+ type: 'errored',
90
+ element: nextElement,
91
+ error: new Error('Too many re-renders. SimpReact limits the number of renders to prevent an infinite loop.'),
92
+ phase: 'updating',
93
+ });
94
+ return;
95
+ }
96
+ lifecycleEventBus.publish({ type: 'beforeRender', element: nextElement, phase: 'updating' });
97
+ batchingRerenderLocker.lock();
98
+ nextChildren = nextElement.type(nextElement.props || emptyObject);
99
+ batchingRerenderLocker.flush();
100
+ lifecycleEventBus.publish({ type: 'afterRender', element: nextElement, phase: 'updating' });
101
+ } while (triedToRerender);
102
+ nextChildren = normalizeRoot(nextChildren, false);
74
103
  }
75
104
  catch (error) {
76
105
  lifecycleEventBus.publish({ type: 'errored', element: nextElement, error, phase: 'updating' });
77
106
  return;
78
107
  }
108
+ finally {
109
+ triedToRerenderUnsubscribe();
110
+ }
79
111
  const prevChildren = prevElement.children;
80
112
  if (nextChildren) {
81
113
  nextElement.children = nextChildren;
82
114
  }
83
- patchChildren(prevChildren, nextChildren, parentReference, nextReference, nextElement, contextMap, hostNamespace);
84
- lifecycleEventBus.publish({ type: 'updated', element: nextElement });
115
+ if (!prevElement.unmounted) {
116
+ patchChildren(prevChildren, nextChildren, parentReference, nextReference, nextElement, contextMap, hostNamespace);
117
+ lifecycleEventBus.publish({ type: 'updated', element: nextElement });
118
+ return;
119
+ }
120
+ prevElement.unmounted = false;
121
+ if (nextChildren) {
122
+ nextChildren.parent = nextElement;
123
+ mount(nextChildren, parentReference, nextReference, contextMap, hostNamespace);
124
+ }
125
+ lifecycleEventBus.publish({ type: 'mounted', element: nextElement });
85
126
  }
86
127
  function patchTextElement(prevElement, nextElement) {
87
128
  nextElement.reference = prevElement.reference;
@@ -178,8 +219,7 @@ function patchChildren(prevChildren, nextChildren, parentReference, nextReferenc
178
219
  patch(prevChildren, nextChildren, parentReference, nextReference, contextMap, hostNamespace);
179
220
  }
180
221
  else {
181
- unmount(prevChildren);
182
- hostAdapter.clearNode(parentReference);
222
+ remove(prevChildren, parentReference);
183
223
  }
184
224
  }
185
225
  else {
@@ -216,7 +256,7 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
216
256
  }
217
257
  // Step 3: Mount new nodes if prev list is exhausted
218
258
  if (prevStart > prevEnd) {
219
- const before = nextChildren[nextEnd + 1]?.reference || nextReference;
259
+ const before = findHostReferenceFromElement(nextChildren[nextEnd + 1]) || nextReference;
220
260
  for (let i = nextStart; i <= nextEnd; i++) {
221
261
  mount(nextChildren[i], parentReference, before, contextMap, hostNamespace);
222
262
  }
@@ -251,7 +291,7 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
251
291
  usedIndices.add(prevIndex);
252
292
  }
253
293
  else {
254
- mount(nextChild, parentReference, nextChildren[i + 1]?.reference || nextReference, contextMap, hostNamespace);
294
+ mount(nextChild, parentReference, findHostReferenceFromElement(nextChildren[i + 1]) || nextReference, contextMap, hostNamespace);
255
295
  toMove[i - nextStart] = -1;
256
296
  }
257
297
  }
@@ -264,7 +304,7 @@ export function patchKeyedChildren(prevChildren, nextChildren, parentReference,
264
304
  // Insert in correct order
265
305
  for (let i = nextEnd; i >= nextStart; i--) {
266
306
  const currentChild = nextChildren[i];
267
- const reference = nextChildren[i + 1]?.reference || nextReference;
307
+ const reference = findHostReferenceFromElement(nextChildren[i + 1]) || nextReference;
268
308
  if (toMove[i - nextStart] !== -1) {
269
309
  hostAdapter.insertBefore(parentReference, currentChild.reference, reference);
270
310
  }
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 {};
@@ -3,11 +3,11 @@ export declare function rerender(element: SimpElement): void;
3
3
  interface IRendererLocker {
4
4
  _isLocked: boolean;
5
5
  _elements: Set<SimpElement>;
6
- isLocked: boolean;
6
+ _track(element: SimpElement): void;
7
+ _untrack(element: SimpElement): void;
7
8
  lock(): void;
8
- track(element: SimpElement): void;
9
9
  flush(): void;
10
10
  }
11
- export declare const syncRerenderLocker: IRendererLocker;
12
- export declare const asyncRerenderLocker: IRendererLocker;
11
+ export declare const batchingRerenderLocker: IRendererLocker;
12
+ export declare const renderingRerenderLocker: IRendererLocker;
13
13
  export {};
package/core/rerender.js CHANGED
@@ -1,59 +1,66 @@
1
1
  import { findParentReferenceFromElement, updateFunctionalComponent } from './patching.js';
2
+ import { lifecycleEventBus } from './lifecycleEventBus.js';
3
+ lifecycleEventBus.subscribe(event => {
4
+ if (event.type === 'afterRender') {
5
+ batchingRerenderLocker._untrack(event.element);
6
+ renderingRerenderLocker._untrack(event.element);
7
+ }
8
+ });
2
9
  export function rerender(element) {
3
10
  if (element.flag !== 'FC') {
4
11
  throw new TypeError('Re-rendering is only supported for FC elements.');
5
12
  }
6
13
  if (element.unmounted) {
7
- console.warn('The component unmounted.');
14
+ console.warn('The component is unmounted.');
8
15
  }
9
- if (syncRerenderLocker.isLocked) {
10
- syncRerenderLocker.track(element);
16
+ lifecycleEventBus.publish({ type: 'triedToRerender', element });
17
+ if (batchingRerenderLocker._isLocked) {
18
+ batchingRerenderLocker._track(element);
11
19
  return;
12
20
  }
13
- else if (asyncRerenderLocker.isLocked) {
14
- asyncRerenderLocker.track(element);
15
- }
16
- else {
17
- asyncRerenderLocker.lock();
18
- updateFunctionalComponent(element, findParentReferenceFromElement(element), null, element.contextMap || null, element.store.hostNamespace);
19
- asyncRerenderLocker.flush();
21
+ if (renderingRerenderLocker._isLocked) {
22
+ renderingRerenderLocker._track(element);
23
+ return;
20
24
  }
25
+ renderingRerenderLocker.lock();
26
+ updateFunctionalComponent(element, findParentReferenceFromElement(element), null, element.contextMap || null, element.store.hostNamespace);
27
+ renderingRerenderLocker.flush();
21
28
  }
22
- export const syncRerenderLocker = {
29
+ export const batchingRerenderLocker = {
23
30
  _isLocked: false,
24
31
  _elements: new Set(),
25
- get isLocked() {
26
- return this._isLocked;
32
+ _track(element) {
33
+ this._elements.add(element);
34
+ },
35
+ _untrack(element) {
36
+ this._elements.delete(element);
27
37
  },
28
38
  lock() {
29
39
  this._isLocked = true;
30
40
  },
31
- track(element) {
32
- this._elements.add(element);
33
- },
34
41
  flush() {
35
42
  this._isLocked = false;
36
43
  if (this._elements.size === 0) {
37
44
  return;
38
45
  }
39
46
  for (const element of this._elements) {
40
- this._elements.delete(element);
47
+ this._untrack(element);
41
48
  rerender(element.store.latestElement);
42
49
  }
43
50
  },
44
51
  };
45
- export const asyncRerenderLocker = {
52
+ export const renderingRerenderLocker = {
46
53
  _isLocked: false,
47
54
  _elements: new Set(),
48
- get isLocked() {
49
- return this._isLocked;
55
+ _track(element) {
56
+ this._elements.add(element);
57
+ },
58
+ _untrack(element) {
59
+ this._elements.delete(element);
50
60
  },
51
61
  lock() {
52
62
  this._isLocked = true;
53
63
  },
54
- track(element) {
55
- this._elements.add(element);
56
- },
57
64
  flush() {
58
65
  this._isLocked = false;
59
66
  if (this._elements.size === 0) {
@@ -61,7 +68,7 @@ export const asyncRerenderLocker = {
61
68
  }
62
69
  queueMicrotask(() => {
63
70
  for (const element of this._elements) {
64
- this._elements.delete(element);
71
+ this._untrack(element);
65
72
  rerender(element.store.latestElement);
66
73
  }
67
74
  });
@@ -9,6 +9,10 @@ export function unmount(element) {
9
9
  return;
10
10
  }
11
11
  if (element.flag === 'FC') {
12
+ // Skip — element is already unmounted.
13
+ if (element.unmounted) {
14
+ return;
15
+ }
12
16
  // FC element always has Maybe<SimpElement> due to normalization.
13
17
  if (element.children) {
14
18
  unmount(element.children);
@@ -1,6 +1,6 @@
1
1
  const elementPropertyName = '__SIMP_ELEMENT__';
2
2
  export function attachElementToDom(element, dom) {
3
- if (dom.nodeType !== Node.TEXT_NODE) {
3
+ if (element.flag !== 'TEXT') {
4
4
  Object.defineProperty(dom, elementPropertyName, { value: element, writable: true });
5
5
  }
6
6
  }
package/dom/events.d.ts CHANGED
@@ -5,6 +5,9 @@ export declare class SyntheticEvent {
5
5
  currentTarget: Nullable<EventTarget>;
6
6
  isPropagationStopped: boolean;
7
7
  isDefaultPrevented: boolean;
8
+ button?: number;
9
+ buttons?: number;
10
+ pointerId?: number;
8
11
  constructor(event: Event);
9
12
  get target(): EventTarget | null;
10
13
  get type(): string;
package/dom/events.js CHANGED
@@ -1,4 +1,4 @@
1
- import { syncRerenderLocker } from '../core/internal.js';
1
+ import { batchingRerenderLocker } from '../core/internal.js';
2
2
  import { getElementFromDom } from './attach-element-to-dom.js';
3
3
  const eventNameByTypes = {
4
4
  click: 'onClick',
@@ -40,8 +40,14 @@ export class SyntheticEvent {
40
40
  currentTarget = null;
41
41
  isPropagationStopped = false;
42
42
  isDefaultPrevented = false;
43
+ button;
44
+ buttons;
45
+ pointerId;
43
46
  constructor(event) {
44
47
  this.nativeEvent = event;
48
+ this.button = event.button;
49
+ this.buttons = event.buttons;
50
+ this.pointerId = event.pointerId;
45
51
  }
46
52
  get target() {
47
53
  return this.nativeEvent.target;
@@ -59,7 +65,7 @@ export class SyntheticEvent {
59
65
  }
60
66
  }
61
67
  export function dispatchDelegatedEvent(event) {
62
- syncRerenderLocker.lock();
68
+ batchingRerenderLocker.lock();
63
69
  const syntheticEvent = new SyntheticEvent(event);
64
70
  const captureHandlers = [];
65
71
  const bubbleHandlers = [];
@@ -91,7 +97,7 @@ export function dispatchDelegatedEvent(event) {
91
97
  return;
92
98
  }
93
99
  }
94
- syncRerenderLocker.flush();
100
+ batchingRerenderLocker.flush();
95
101
  }
96
102
  const captureRegex = /Capture/;
97
103
  export function patchEvent(name, prevValue, nextValue, dom) {
@@ -1,4 +1,4 @@
1
- import { syncRerenderLocker } from '../../../core/internal.js';
1
+ import { batchingRerenderLocker } from '../../../core/internal.js';
2
2
  import { getElementFromDom } from '../../attach-element-to-dom.js';
3
3
  export function isCheckedType(type) {
4
4
  return type === 'checkbox' || type === 'radio';
@@ -12,9 +12,9 @@ function onControlledInputInput(event) {
12
12
  return;
13
13
  }
14
14
  if (element.props['onInput']) {
15
- syncRerenderLocker.lock();
15
+ batchingRerenderLocker.lock();
16
16
  element.props['onInput'](event);
17
- syncRerenderLocker.flush();
17
+ batchingRerenderLocker.flush();
18
18
  element = getElementFromDom(event.target);
19
19
  }
20
20
  if (element) {
@@ -27,9 +27,9 @@ function onControlledInputChange(event) {
27
27
  return;
28
28
  }
29
29
  if (element.props['onChange']) {
30
- syncRerenderLocker.lock();
30
+ batchingRerenderLocker.lock();
31
31
  element.props['onChange'](event);
32
- syncRerenderLocker.flush();
32
+ batchingRerenderLocker.flush();
33
33
  element = getElementFromDom(event.target);
34
34
  }
35
35
  if (element) {
@@ -1,4 +1,4 @@
1
- import { syncRerenderLocker } from '../../../core/internal.js';
1
+ import { batchingRerenderLocker } from '../../../core/internal.js';
2
2
  import { emptyObject } from '../../../shared/index.js';
3
3
  import { getElementFromDom } from '../../attach-element-to-dom.js';
4
4
  export function isEventNameIgnored(eventName) {
@@ -10,9 +10,9 @@ function onControlledInputChange(event) {
10
10
  return;
11
11
  }
12
12
  if (element.props['onChange']) {
13
- syncRerenderLocker.lock();
13
+ batchingRerenderLocker.lock();
14
14
  element.props['onChange'](event);
15
- syncRerenderLocker.flush();
15
+ batchingRerenderLocker.flush();
16
16
  element = getElementFromDom(event.target);
17
17
  }
18
18
  if (element) {
@@ -1,4 +1,4 @@
1
- import { syncRerenderLocker } from '../../../core/internal.js';
1
+ import { batchingRerenderLocker } from '../../../core/internal.js';
2
2
  import { getElementFromDom } from '../../attach-element-to-dom.js';
3
3
  export function isEventNameIgnored(eventName) {
4
4
  return eventName === 'onChange' || eventName === 'onInput';
@@ -9,9 +9,9 @@ function onControlledTextareaChange(event) {
9
9
  return;
10
10
  }
11
11
  if (element.props['onChange']) {
12
- syncRerenderLocker.lock();
12
+ batchingRerenderLocker.lock();
13
13
  element.props['onChange'](event);
14
- syncRerenderLocker.flush();
14
+ batchingRerenderLocker.flush();
15
15
  element = getElementFromDom(event.target);
16
16
  }
17
17
  if (element) {
@@ -24,9 +24,9 @@ function onControlledTextareaInput(event) {
24
24
  return;
25
25
  }
26
26
  if (element.props['onInput']) {
27
- syncRerenderLocker.lock();
27
+ batchingRerenderLocker.lock();
28
28
  element.props['onInput'](event);
29
- syncRerenderLocker.flush();
29
+ batchingRerenderLocker.flush();
30
30
  element = getElementFromDom(event.target);
31
31
  }
32
32
  if (element) {
@@ -14,19 +14,22 @@ export function patchStyle(prevAttrValue, nextAttrValue, dom) {
14
14
  for (style in nextAttrValue) {
15
15
  value = nextAttrValue[style];
16
16
  if (value !== prevAttrValue[style]) {
17
- domStyle.setProperty(style, value);
17
+ domStyle.setProperty(camelToKebab(style), value);
18
18
  }
19
19
  }
20
20
  for (style in prevAttrValue) {
21
21
  if (nextAttrValue[style] == null) {
22
- domStyle.removeProperty(style);
22
+ domStyle.removeProperty(camelToKebab(style));
23
23
  }
24
24
  }
25
25
  }
26
26
  else {
27
27
  for (style in nextAttrValue) {
28
28
  value = nextAttrValue[style];
29
- domStyle.setProperty(style, value);
29
+ domStyle.setProperty(camelToKebab(style), value);
30
30
  }
31
31
  }
32
32
  }
33
+ function camelToKebab(name) {
34
+ return name.replace(/[A-Z]/g, match => '-' + match.toLowerCase());
35
+ }
package/dom/render.js CHANGED
@@ -1,4 +1,4 @@
1
- import { asyncRerenderLocker, hostAdapter, mount, patch, provideHostAdapter, remove, } from '../core/internal.js';
1
+ import { renderingRerenderLocker, hostAdapter, mount, patch, provideHostAdapter, remove, } from '../core/internal.js';
2
2
  import { domAdapter } from './domAdapter.js';
3
3
  import { attachElementToDom, getElementFromDom } from './attach-element-to-dom.js';
4
4
  provideHostAdapter(domAdapter);
@@ -7,7 +7,7 @@ export function render(element, container) {
7
7
  return;
8
8
  }
9
9
  const currentRootElement = getElementFromDom(container);
10
- asyncRerenderLocker.lock();
10
+ renderingRerenderLocker.lock();
11
11
  if (!currentRootElement) {
12
12
  if (element) {
13
13
  hostAdapter.clearNode(container);
@@ -27,7 +27,7 @@ export function render(element, container) {
27
27
  patch(prevChildren, element, container, null, null, hostAdapter.getHostNamespaces(element, undefined)?.self);
28
28
  }
29
29
  }
30
- asyncRerenderLocker.flush();
30
+ renderingRerenderLocker.flush();
31
31
  }
32
32
  export function createRoot(container) {
33
33
  return {
package/hooks/index.d.ts CHANGED
@@ -4,12 +4,18 @@ export type Cleanup = () => void;
4
4
  export type Effect = () => void | Cleanup;
5
5
  export type DependencyList = readonly unknown[];
6
6
 
7
+ export type Dispatch<A> = (value: A) => void;
8
+ export type SetStateAction<S> = S | ((prevState: S) => S);
9
+
7
10
  declare function useRef<T>(initialValue: T): RefObject<T>;
8
11
  declare function useRef<T>(initialValue: T | null): RefObject<T | null>;
9
12
  declare function useRef<T>(initialValue: T | undefined): RefObject<T | undefined>;
10
13
 
11
14
  declare function useRerender(): () => void;
12
15
 
16
+ export function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
17
+ export function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
18
+
13
19
  declare function useEffect(effect: Effect, deps?: DependencyList): void;
14
20
 
15
21
  declare function useMounted(effect: Effect): void;
package/hooks/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import { lifecycleEventBus, rerender, syncRerenderLocker } from '../core/internal.js';
1
+ import { batchingRerenderLocker, lifecycleEventBus, rerender as _rerender } from '../core/internal.js';
2
2
  import { noop } from '../shared/index.js';
3
+ import { callOrGet } from '../shared/utils.js';
3
4
  let currentIndex = 0;
4
5
  // In runtime this is a nullable variable.
5
6
  let currentElement;
@@ -9,6 +10,9 @@ lifecycleEventBus.subscribe(event => {
9
10
  if (currentElement.store?.catchHandlers) {
10
11
  currentElement.store.catchHandlers = undefined;
11
12
  }
13
+ if (currentElement.store?.effectsHookStates) {
14
+ currentElement.store.effectsHookStates = undefined;
15
+ }
12
16
  }
13
17
  if (event.type === 'afterRender' || event.type === 'errored') {
14
18
  currentElement = null;
@@ -17,19 +21,19 @@ lifecycleEventBus.subscribe(event => {
17
21
  if (event.type === 'mounted') {
18
22
  const element = event.element;
19
23
  if (element.store?.effectsHookStates) {
20
- syncRerenderLocker.lock();
24
+ batchingRerenderLocker.lock();
21
25
  const effects = element.store.effectsHookStates;
22
26
  element.store.effectsHookStates = undefined;
23
27
  for (const state of effects) {
24
28
  state.cleanup = state.effect() || undefined;
25
29
  }
26
- syncRerenderLocker.flush();
30
+ batchingRerenderLocker.flush();
27
31
  }
28
32
  }
29
33
  if (event.type === 'updated') {
30
34
  const element = event.element;
31
35
  if (element.store?.effectsHookStates) {
32
- syncRerenderLocker.lock();
36
+ batchingRerenderLocker.lock();
33
37
  const effects = element.store.effectsHookStates;
34
38
  element.store.effectsHookStates = undefined;
35
39
  for (const state of effects) {
@@ -38,7 +42,7 @@ lifecycleEventBus.subscribe(event => {
38
42
  }
39
43
  state.cleanup = state.effect() || undefined;
40
44
  }
41
- syncRerenderLocker.flush();
45
+ batchingRerenderLocker.flush();
42
46
  }
43
47
  }
44
48
  if (event.type === 'unmounted') {
@@ -57,11 +61,11 @@ lifecycleEventBus.subscribe(event => {
57
61
  throw new Error('Error occurred during rendering a component', { cause: event.error });
58
62
  }
59
63
  if (element.store.catchHandlers) {
60
- syncRerenderLocker.lock();
64
+ batchingRerenderLocker.lock();
61
65
  for (const state of element.store.catchHandlers) {
62
66
  state(event.error);
63
67
  }
64
- syncRerenderLocker.flush();
68
+ batchingRerenderLocker.flush();
65
69
  }
66
70
  }
67
71
  });
@@ -86,7 +90,26 @@ export function useRerender() {
86
90
  const hookStates = getOrCreateHookStates(currentElement);
87
91
  if (!hookStates[currentIndex]) {
88
92
  const elementStore = currentElement.store;
89
- hookStates[currentIndex] = () => rerender(elementStore.latestElement);
93
+ hookStates[currentIndex] = function rerender() {
94
+ _rerender(elementStore.latestElement);
95
+ };
96
+ }
97
+ return hookStates[currentIndex++];
98
+ }
99
+ export function useState(initialState) {
100
+ const hookStates = getOrCreateHookStates(currentElement);
101
+ if (!hookStates[currentIndex]) {
102
+ const elementStore = currentElement.store;
103
+ const state = (hookStates[currentIndex] = [undefined, undefined]);
104
+ state[0] = callOrGet(initialState);
105
+ state[1] = function dispatch(action) {
106
+ const nextValue = callOrGet(action, state[0]);
107
+ if (Object.is(state[0], nextValue)) {
108
+ return;
109
+ }
110
+ state[0] = nextValue;
111
+ _rerender(elementStore.latestElement);
112
+ };
90
113
  }
91
114
  return hookStates[currentIndex++];
92
115
  }
@@ -159,4 +182,14 @@ function getOrCreateEffectHookStates(element) {
159
182
  }
160
183
  return element.store.effectsHookStates;
161
184
  }
162
- export default { useRef, useRerender, useEffect, useMounted, useUnmounted, useContext, useCatch, areDepsEqual };
185
+ export default {
186
+ useRef,
187
+ useRerender,
188
+ useState,
189
+ useEffect,
190
+ useMounted,
191
+ useUnmounted,
192
+ useContext,
193
+ useCatch,
194
+ areDepsEqual,
195
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simpreact/simpreact",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "",
5
5
  "homepage": "https://github.com/dPaskhin/simpreact#readme",
6
6
  "main": "./core/index.js",
@@ -11,6 +11,12 @@
11
11
  "import": "./core/index.js",
12
12
  "types": "./core/index.d.ts"
13
13
  },
14
+ "./compat": {
15
+ "import": "./compat/index.js"
16
+ },
17
+ "./compat/*": {
18
+ "import": "./compat/index.js"
19
+ },
14
20
  "./internal": {
15
21
  "import": "./core/internal.js",
16
22
  "types": "./core/internal.d.ts"
package/shared/utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type { SimpText } from './public.js';
2
2
  export declare function isSimpText(value: unknown): value is SimpText;
3
3
  export declare function noop(): void;
4
+ export declare function callOrGet<T, A extends any[]>(value: T | ((...args: A) => T), ...args: A): T;
package/shared/utils.js CHANGED
@@ -2,3 +2,19 @@ export function isSimpText(value) {
2
2
  return typeof value === 'string' || typeof value === 'number' || typeof value === 'bigint';
3
3
  }
4
4
  export function noop() { }
5
+ export function callOrGet(value) {
6
+ if (typeof value !== 'function') {
7
+ return value;
8
+ }
9
+ if (arguments.length === 1) {
10
+ return value();
11
+ }
12
+ if (arguments.length === 2) {
13
+ return value(arguments[1]);
14
+ }
15
+ const args = [];
16
+ for (let i = 1; i < arguments.length; ++i) {
17
+ args.push(arguments[i]);
18
+ }
19
+ return value(...args);
20
+ }