@simpreact/simpreact 0.0.4 → 0.0.6

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 (78) 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 +14 -9
  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 +10 -9
  11. package/compat/renderRuntime.d.ts +2 -0
  12. package/compat/renderRuntime.js +14 -0
  13. package/component/index.d.ts +16 -0
  14. package/component/index.js +164 -0
  15. package/context/index.d.ts +12 -5
  16. package/context/index.js +62 -57
  17. package/core/createElement.d.ts +29 -20
  18. package/core/createElement.js +159 -133
  19. package/core/hostAdapter.d.ts +8 -12
  20. package/core/hostAdapter.js +1 -4
  21. package/core/hostOperations.d.ts +5 -0
  22. package/core/hostOperations.js +15 -0
  23. package/core/index.d.ts +29 -6
  24. package/core/internal.d.ts +3 -0
  25. package/core/internal.js +3 -0
  26. package/core/lifecycleEventBus.d.ts +15 -6
  27. package/core/lifecycleEventBus.js +16 -2
  28. package/core/memo.d.ts +0 -2
  29. package/core/memo.js +1 -3
  30. package/core/mounting.d.ts +7 -9
  31. package/core/mounting.js +221 -82
  32. package/core/patching.d.ts +7 -8
  33. package/core/patching.js +235 -255
  34. package/core/patchingChildren.d.ts +6 -0
  35. package/core/patchingChildren.js +330 -0
  36. package/core/portal.d.ts +1 -1
  37. package/core/portal.js +17 -7
  38. package/core/processStack.d.ts +69 -0
  39. package/core/processStack.js +63 -0
  40. package/core/ref.d.ts +4 -3
  41. package/core/rerender.d.ts +4 -14
  42. package/core/rerender.js +67 -112
  43. package/core/runtime.d.ts +17 -0
  44. package/core/runtime.js +2 -0
  45. package/core/unmounting.d.ts +11 -6
  46. package/core/unmounting.js +81 -40
  47. package/core/utils.d.ts +10 -0
  48. package/core/utils.js +143 -0
  49. package/dom/attach-element-to-dom.d.ts +4 -3
  50. package/dom/attach-element-to-dom.js +12 -7
  51. package/dom/domAdapter.js +22 -25
  52. package/dom/events.d.ts +5 -5
  53. package/dom/events.js +33 -16
  54. package/dom/index.d.ts +16 -5
  55. package/dom/index.js +4 -3
  56. package/dom/props/controlled/index.d.ts +3 -3
  57. package/dom/props/controlled/index.js +8 -8
  58. package/dom/props/controlled/input.d.ts +3 -3
  59. package/dom/props/controlled/input.js +57 -34
  60. package/dom/props/controlled/select.d.ts +3 -3
  61. package/dom/props/controlled/select.js +39 -26
  62. package/dom/props/controlled/textarea.d.ts +3 -3
  63. package/dom/props/controlled/textarea.js +57 -34
  64. package/dom/props/dangerInnerHTML.d.ts +2 -2
  65. package/dom/props/dangerInnerHTML.js +3 -2
  66. package/dom/props/props.d.ts +4 -4
  67. package/dom/props/props.js +24 -21
  68. package/dom/render.d.ts +4 -5
  69. package/dom/render.js +38 -34
  70. package/hooks/index.d.ts +15 -13
  71. package/hooks/index.js +155 -159
  72. package/jsx-runtime/index.d.ts +2 -1
  73. package/package.json +9 -1
  74. package/shared/index.d.ts +10 -0
  75. package/shared/index.js +4 -7
  76. package/shared/utils.js +4 -4
  77. package/shared/EventBus.d.ts +0 -18
  78. package/shared/EventBus.js +0 -28
package/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Dmitrii Paskhin
3
+ Copyright (c) 2026 Dmitrii Paskhin
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,8 +1,8 @@
1
1
  import * as SimpReactContext from '../context/index.js';
2
- export declare const createContext: typeof SimpReactContext.createContext;
3
- export declare const useContext: typeof SimpReactContext.useContext;
2
+ export declare const createContext: SimpReactContext.CreateContext;
3
+ export declare const useContext: SimpReactContext.UseContext;
4
4
  declare const _default: {
5
- createContext: typeof SimpReactContext.createContext;
6
- useContext: typeof SimpReactContext.useContext;
5
+ createContext: SimpReactContext.CreateContext;
6
+ useContext: SimpReactContext.UseContext;
7
7
  };
8
8
  export default _default;
package/compat/context.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as SimpReactContext from '../context/index.js';
2
- export const createContext = SimpReactContext.createContext;
3
- export const useContext = SimpReactContext.useContext;
2
+ import { renderRuntime } from './renderRuntime.js';
3
+ export const createContext = SimpReactContext.createCreateContext(renderRuntime);
4
+ export const useContext = SimpReactContext.createUseContext(renderRuntime);
4
5
  export default {
5
6
  createContext,
6
7
  useContext,
package/compat/core.d.ts CHANGED
@@ -12,6 +12,9 @@ export declare function Suspense(props: {
12
12
  fallback: SimpReactInternal.SimpNode;
13
13
  children: SimpReactInternal.SimpNode;
14
14
  }): SimpReactInternal.SimpNode;
15
+ export declare function StrictMode(props: {
16
+ children: SimpReactInternal.SimpNode;
17
+ }): SimpReactInternal.SimpNode;
15
18
  export declare function forwardRef<P, T>(Component: (props: P, ref: SimpReactInternal.Ref<T>) => any): (props: P) => any;
16
19
  export declare const Fragment: SimpReactInternal.Fragment;
17
20
  export declare const createElement: typeof SimpReactInternal.createElement;
@@ -32,6 +35,7 @@ declare const _default: {
32
35
  cloneElement: typeof cloneElement;
33
36
  isValidElement: typeof isValidElement;
34
37
  Suspense: typeof Suspense;
38
+ StrictMode: typeof StrictMode;
35
39
  forwardRef: typeof forwardRef;
36
40
  Fragment: SimpReactInternal.Fragment;
37
41
  createElement: typeof SimpReactInternal.createElement;
package/compat/core.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as SimpReactInternal from '../core/internal.js';
2
- import * as SimpReactHooks from '../hooks/index.js';
2
+ import { useCatch, useState } from './hooks.js';
3
3
  export const Children = {
4
4
  map(children, fn) {
5
5
  return Children.toArray(children).map(fn);
@@ -39,27 +39,31 @@ export function cloneElement(element, props, ...children) {
39
39
  if (!isValidElement(element)) {
40
40
  throw new Error(`cloneElement: expected a SimpElement, got ${element}`);
41
41
  }
42
- if (element.flag === 'PORTAL') {
42
+ if ((element.flag & SimpReactInternal.SIMP_ELEMENT_FLAG_PORTAL) !== 0) {
43
43
  throw new Error('cloneElement: the argument must be a SimpElement, but you passed a portal instead.');
44
44
  }
45
- return SimpReactInternal.createElement(element.flag === 'FRAGMENT' ? SimpReactInternal.Fragment : element.type, Object.assign({}, element.props, props), arguments.length > 2 ? children : props.children || element.children);
45
+ return SimpReactInternal.createElement((element.flag & SimpReactInternal.SIMP_ELEMENT_FLAG_FRAGMENT) !== 0 ? SimpReactInternal.Fragment : element.type, Object.assign({}, element.props, props), arguments.length > 2 ? children : props.children || element.children);
46
46
  }
47
47
  export function isValidElement(element) {
48
48
  return typeof element === 'object' && element !== null && 'flag' in element;
49
49
  }
50
50
  export function Suspense(props) {
51
- const [isSuspended, setIsSuspended] = SimpReactHooks.useState(false);
52
- SimpReactHooks.useCatch(error => {
51
+ const [isSuspended, setIsSuspended] = useState(false);
52
+ useCatch(error => {
53
+ if (!(error instanceof Promise)) {
54
+ throw error;
55
+ }
53
56
  if (isSuspended) {
54
57
  return;
55
58
  }
56
- if (error instanceof Promise) {
57
- setIsSuspended(true);
58
- error.then(() => setIsSuspended(false));
59
- }
59
+ setIsSuspended(true);
60
+ error.then(() => setIsSuspended(false));
60
61
  });
61
62
  return isSuspended ? props.fallback : props.children;
62
63
  }
64
+ export function StrictMode(props) {
65
+ return props.children;
66
+ }
63
67
  export function forwardRef(Component) {
64
68
  return function Forwarded(props) {
65
69
  return Component(props, props?.ref || null);
@@ -80,6 +84,7 @@ export default {
80
84
  cloneElement,
81
85
  isValidElement,
82
86
  Suspense,
87
+ StrictMode,
83
88
  forwardRef,
84
89
  Fragment,
85
90
  createElement,
package/compat/dom.d.ts CHANGED
@@ -1,11 +1,10 @@
1
- import * as SimpReactDOM from '../dom/index.js';
2
1
  import * as SimpReactShared from '../shared/index.js';
3
2
  export declare const hydrate: typeof SimpReactShared.noop;
4
- export declare const render: typeof SimpReactDOM.render;
5
- export declare const createRoot: typeof SimpReactDOM.createRoot;
3
+ export declare const render: (element: SimpReactShared.Nullable<import("../core/createElement.js").SimpElement>, container: SimpReactShared.Nullable<Element | DocumentFragment>) => void;
4
+ export declare const createRoot: (container: Element | DocumentFragment) => import("../dom/render.js").SimpRoot;
6
5
  declare const _default: {
7
6
  hydrate: typeof SimpReactShared.noop;
8
- render: typeof SimpReactDOM.render;
9
- createRoot: typeof SimpReactDOM.createRoot;
7
+ render: (element: SimpReactShared.Nullable<import("../core/createElement.js").SimpElement>, container: SimpReactShared.Nullable<Element | DocumentFragment>) => void;
8
+ createRoot: (container: Element | DocumentFragment) => import("../dom/render.js").SimpRoot;
10
9
  };
11
10
  export default _default;
package/compat/dom.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as SimpReactDOM from '../dom/index.js';
2
2
  import * as SimpReactShared from '../shared/index.js';
3
+ import { renderRuntime } from './renderRuntime.js';
3
4
  export const hydrate = SimpReactShared.noop;
4
- export const render = SimpReactDOM.render;
5
- export const createRoot = SimpReactDOM.createRoot;
5
+ export const render = SimpReactDOM.createRenderer(renderRuntime);
6
+ export const createRoot = SimpReactDOM.createCreateRoot(renderRuntime);
6
7
  export default {
7
8
  hydrate,
8
9
  render,
package/compat/hooks.d.ts CHANGED
@@ -1,24 +1,27 @@
1
- import * as SimpReactHooks from '../hooks/index.js';
1
+ import { type DependencyList } from '../hooks/index.js';
2
+ import * as SimpReactShared from '../shared/index.js';
3
+ export declare const useRerender: () => () => void;
4
+ export declare const useState: import("../hooks/index.js").UseState;
5
+ export declare const useEffect: (effect: SimpReactShared.Effect, deps?: SimpReactShared.DependencyList) => void;
6
+ export declare const useLayoutEffect: (effect: SimpReactShared.Effect, deps?: SimpReactShared.DependencyList) => void;
7
+ export declare const useInsertionEffect: (effect: SimpReactShared.Effect, deps?: SimpReactShared.DependencyList) => void;
8
+ export declare const useRef: import("../hooks/index.js").UseRef;
9
+ export declare const useCatch: (cb: (error: any) => void) => void;
2
10
  export declare function useSyncExternalStore<T>(subscribe: (callback: () => void) => () => void, getSnapshot: () => T): T;
3
11
  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
12
  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;
13
+ export declare function useMemo<T>(factory: () => T, deps: DependencyList): T;
14
+ export declare function useCallback<T>(cb: T, deps: DependencyList): T;
12
15
  declare const _default: {
13
16
  useSyncExternalStore: typeof useSyncExternalStore;
14
17
  useReducer: typeof useReducer;
15
18
  useId: typeof useId;
16
19
  useMemo: typeof useMemo;
17
20
  useCallback: typeof useCallback;
18
- useState: typeof SimpReactHooks.useState;
19
- useEffect: typeof SimpReactHooks.useEffect;
20
- useLayoutEffect: typeof SimpReactHooks.useEffect;
21
- useInsertionEffect: typeof SimpReactHooks.useEffect;
22
- useRef: typeof SimpReactHooks.useRef;
21
+ useState: import("../hooks/index.js").UseState;
22
+ useEffect: (effect: SimpReactShared.Effect, deps?: SimpReactShared.DependencyList) => void;
23
+ useLayoutEffect: (effect: SimpReactShared.Effect, deps?: SimpReactShared.DependencyList) => void;
24
+ useInsertionEffect: (effect: SimpReactShared.Effect, deps?: SimpReactShared.DependencyList) => void;
25
+ useRef: import("../hooks/index.js").UseRef;
23
26
  };
24
27
  export default _default;
package/compat/hooks.js CHANGED
@@ -1,8 +1,17 @@
1
- import * as SimpReactHooks from '../hooks/index.js';
1
+ import { createUseCatch, createUseEffect, createUseRef, createUseRerender, createUseState, } from '../hooks/index.js';
2
+ import * as SimpReactShared from '../shared/index.js';
3
+ import { renderRuntime } from './renderRuntime.js';
4
+ export const useRerender = createUseRerender(renderRuntime);
5
+ export const useState = createUseState(renderRuntime);
6
+ export const useEffect = createUseEffect(renderRuntime);
7
+ export const useLayoutEffect = createUseEffect(renderRuntime);
8
+ export const useInsertionEffect = createUseEffect(renderRuntime);
9
+ export const useRef = createUseRef(renderRuntime);
10
+ export const useCatch = createUseCatch(renderRuntime);
2
11
  export function useSyncExternalStore(subscribe, getSnapshot) {
3
- const rerender = SimpReactHooks.useRerender();
4
- const lastSnapshotRef = SimpReactHooks.useRef(getSnapshot());
5
- SimpReactHooks.useEffect(() => {
12
+ const rerender = useRerender();
13
+ const lastSnapshotRef = useRef(getSnapshot());
14
+ useEffect(() => {
6
15
  function checkForUpdates() {
7
16
  const nextSnapshot = getSnapshot();
8
17
  if (!Object.is(lastSnapshotRef.current, nextSnapshot)) {
@@ -17,10 +26,10 @@ export function useSyncExternalStore(subscribe, getSnapshot) {
17
26
  return lastSnapshotRef.current;
18
27
  }
19
28
  export function useReducer(reducer, initializerArg, initializer) {
20
- const rerender = SimpReactHooks.useRerender();
21
- const reducerRef = SimpReactHooks.useRef(reducer);
29
+ const rerender = useRerender();
30
+ const reducerRef = useRef(reducer);
22
31
  reducerRef.current = reducer;
23
- const stateRef = SimpReactHooks.useRef([
32
+ const stateRef = useRef([
24
33
  initializer ? initializer(initializerArg) : initializerArg,
25
34
  function dispatch(action) {
26
35
  const newState = reducerRef.current(stateRef.current[0], action);
@@ -35,7 +44,7 @@ export function useReducer(reducer, initializerArg, initializer) {
35
44
  }
36
45
  let globalId = 0;
37
46
  export function useId(prefix = 'id') {
38
- const idRef = SimpReactHooks.useRef('');
47
+ const idRef = useRef('');
39
48
  if (idRef.current === '') {
40
49
  globalId += 1;
41
50
  idRef.current = `${prefix}-${globalId}`;
@@ -43,11 +52,11 @@ export function useId(prefix = 'id') {
43
52
  return idRef.current;
44
53
  }
45
54
  export function useMemo(factory, deps) {
46
- const ref = SimpReactHooks.useRef({
55
+ const ref = useRef({
47
56
  deps: undefined,
48
57
  value: undefined,
49
58
  });
50
- if (!SimpReactHooks.areDepsEqual(ref.current.deps, deps)) {
59
+ if (!SimpReactShared.shallowEqual(ref.current.deps, deps)) {
51
60
  ref.current.value = factory();
52
61
  ref.current.deps = deps;
53
62
  }
@@ -56,11 +65,6 @@ export function useMemo(factory, deps) {
56
65
  export function useCallback(cb, deps) {
57
66
  return useMemo(() => cb, deps);
58
67
  }
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
68
  export default {
65
69
  useSyncExternalStore,
66
70
  useReducer,
package/compat/index.d.ts CHANGED
@@ -12,14 +12,14 @@ declare const _default: {
12
12
  useId: typeof import("./hooks.js").useId;
13
13
  useMemo: typeof import("./hooks.js").useMemo;
14
14
  useCallback: typeof import("./hooks.js").useCallback;
15
- useState: typeof import("../hooks/index.js").useState;
16
- useEffect: typeof import("../hooks/index.js").useEffect;
17
- useLayoutEffect: typeof import("../hooks/index.js").useEffect;
18
- useInsertionEffect: typeof import("../hooks/index.js").useEffect;
19
- useRef: typeof import("../hooks/index.js").useRef;
15
+ useState: import("../hooks/index.js").UseState;
16
+ useEffect: (effect: import("../shared/public.js").Effect, deps?: import("../shared/public.js").DependencyList) => void;
17
+ useLayoutEffect: (effect: import("../shared/public.js").Effect, deps?: import("../shared/public.js").DependencyList) => void;
18
+ useInsertionEffect: (effect: import("../shared/public.js").Effect, deps?: import("../shared/public.js").DependencyList) => void;
19
+ useRef: import("../hooks/index.js").UseRef;
20
20
  hydrate: typeof import("../shared/utils.js").noop;
21
- render: typeof import("../dom/render.js").render;
22
- createRoot: typeof import("../dom/render.js").createRoot;
21
+ render: (element: import("../shared/public.js").Nullable<import("../core/createElement.js").SimpElement>, container: import("../shared/public.js").Nullable<Element | DocumentFragment>) => void;
22
+ createRoot: (container: Element | DocumentFragment) => import("../dom/render.js").SimpRoot;
23
23
  Children: {
24
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
25
  forEach(children: import("../core/createElement.js").SimpNode, fn: (child: import("../core/createElement.js").SimpNode, index: number) => void): void;
@@ -30,6 +30,7 @@ declare const _default: {
30
30
  cloneElement: typeof import("./core.js").cloneElement;
31
31
  isValidElement: typeof import("./core.js").isValidElement;
32
32
  Suspense: typeof import("./core.js").Suspense;
33
+ StrictMode: typeof import("./core.js").StrictMode;
33
34
  forwardRef: typeof import("./core.js").forwardRef;
34
35
  Fragment: import("../core/fragment.js").Fragment;
35
36
  createElement: typeof import("../core/createElement.js").createElement;
@@ -37,7 +38,7 @@ declare const _default: {
37
38
  memo: typeof import("../core/memo.js").memo;
38
39
  flushSync: (value: any) => any;
39
40
  Component: typeof import("./core.js").Component;
40
- createContext: typeof import("../context/index.js").createContext;
41
- useContext: typeof import("../context/index.js").useContext;
41
+ createContext: import("../context/index.js").CreateContext;
42
+ useContext: import("../context/index.js").UseContext;
42
43
  };
43
44
  export default _default;
@@ -0,0 +1,2 @@
1
+ import type { SimpRenderRuntime } from '../core/internal.js';
2
+ export declare const renderRuntime: SimpRenderRuntime;
@@ -0,0 +1,14 @@
1
+ import { domAdapter } from '../dom/index.js';
2
+ import { emptyObject } from '../shared/index.js';
3
+ export const renderRuntime = {
4
+ hostAdapter: domAdapter,
5
+ renderer(component, element) {
6
+ return component(element.props || emptyObject);
7
+ },
8
+ elementToHostMap: new Map(),
9
+ renderStack: [],
10
+ currentRenderingFCElement: null,
11
+ renderPhase: null,
12
+ };
13
+ // TODO
14
+ window.__SIMP_RUNTIME__ = renderRuntime;
@@ -0,0 +1,16 @@
1
+ import type { FC, SimpNode } from '../core/index.js';
2
+
3
+ export type Cleanup = () => void;
4
+ export type Effect = () => void | Cleanup;
5
+ export type DependencyList = readonly unknown[];
6
+
7
+ export interface ComponentRenderContext<S = {}> {
8
+ state: S;
9
+ rerender: () => void;
10
+ effects: Array<{ effect: Effect; deps?: DependencyList }>;
11
+ catchers: Array<(error: any) => void>;
12
+ }
13
+
14
+ declare function component<P = {}, S = {}>(Component: (props: P, ctx: ComponentRenderContext<S>) => SimpNode): FC<P>;
15
+
16
+ export function componentRenderer(component: FC, element: SimpElement): SimpNode;
@@ -0,0 +1,164 @@
1
+ import { lifecycleEventBus, rerender, SIMP_ELEMENT_FLAG_FC, } from '../core/internal.js';
2
+ import { emptyObject, shallowEqual, } from '../shared/index.js';
3
+ const componentSpecificStoreByElementStore = new WeakMap();
4
+ function getComponentSpecificStore(store, renderRuntime) {
5
+ let hooksSpecificStore = componentSpecificStoreByElementStore.get(store);
6
+ if (!hooksSpecificStore) {
7
+ function _rerender() {
8
+ rerender(store, renderRuntime);
9
+ }
10
+ hooksSpecificStore = {
11
+ context: {
12
+ state: createState(_rerender),
13
+ rerender: _rerender,
14
+ catchers: null,
15
+ effects: null,
16
+ },
17
+ pendingEffectStates: null,
18
+ effectStates: null,
19
+ };
20
+ componentSpecificStoreByElementStore.set(store, hooksSpecificStore);
21
+ }
22
+ return hooksSpecificStore;
23
+ }
24
+ lifecycleEventBus.subscribe(event => {
25
+ if (event.type === 'errored') {
26
+ if (event.handled) {
27
+ return;
28
+ }
29
+ let element = event.element;
30
+ let curError = event.error;
31
+ let catchers;
32
+ while (element) {
33
+ if (!isComponentElement(element)) {
34
+ element = element.parent;
35
+ continue;
36
+ }
37
+ const store = getComponentSpecificStore(element.store, event.renderRuntime);
38
+ catchers = store.context.catchers;
39
+ if (!catchers) {
40
+ element = element.parent;
41
+ continue;
42
+ }
43
+ try {
44
+ for (let i = 0; i < catchers.length; i++) {
45
+ catchers[i](curError);
46
+ }
47
+ event.handled = true;
48
+ break;
49
+ }
50
+ catch (error) {
51
+ element = element.parent;
52
+ curError = error;
53
+ }
54
+ }
55
+ return;
56
+ }
57
+ if (!isComponentElement(event.element)) {
58
+ return;
59
+ }
60
+ const store = getComponentSpecificStore(event.element.store, event.renderRuntime);
61
+ switch (event.type) {
62
+ case 'beforeRender': {
63
+ store.context.effects = [];
64
+ store.context.catchers = [];
65
+ store.pendingEffectStates = null;
66
+ return;
67
+ }
68
+ case 'afterRender': {
69
+ if (!store.context.effects) {
70
+ return;
71
+ }
72
+ for (let i = 0; i < store.context.effects.length; i++) {
73
+ const renderEffectState = store.context.effects[i];
74
+ let state = (store.effectStates ||= [])[i];
75
+ if (!state) {
76
+ state = store.effectStates[i] = {
77
+ effect: renderEffectState.effect,
78
+ deps: null,
79
+ cleanup: null,
80
+ };
81
+ }
82
+ if (!shallowEqual(renderEffectState.deps, state.deps)) {
83
+ state.effect = renderEffectState.effect;
84
+ state.deps = renderEffectState.deps || null;
85
+ (store.pendingEffectStates ||= []).push(state);
86
+ }
87
+ }
88
+ return;
89
+ }
90
+ case 'mounted': {
91
+ if (!store.pendingEffectStates) {
92
+ return;
93
+ }
94
+ const effects = store.pendingEffectStates;
95
+ store.pendingEffectStates = null;
96
+ for (const state of effects) {
97
+ if (typeof state.cleanup === 'function') {
98
+ state.cleanup();
99
+ }
100
+ state.cleanup = state.effect() || null;
101
+ }
102
+ return;
103
+ }
104
+ case 'updated': {
105
+ if (!store.pendingEffectStates) {
106
+ return;
107
+ }
108
+ const effects = store.pendingEffectStates;
109
+ store.pendingEffectStates = null;
110
+ for (const state of effects) {
111
+ if (typeof state.cleanup === 'function') {
112
+ state.cleanup();
113
+ }
114
+ state.cleanup = state.effect() || null;
115
+ }
116
+ return;
117
+ }
118
+ case 'unmounted': {
119
+ if (!store.effectStates) {
120
+ return;
121
+ }
122
+ const effects = store.effectStates;
123
+ store.effectStates = null;
124
+ for (const state of effects) {
125
+ if (state && 'cleanup' in state && typeof state.cleanup === 'function') {
126
+ state.cleanup();
127
+ }
128
+ }
129
+ }
130
+ }
131
+ });
132
+ export function componentRenderer(component, element, renderRuntime) {
133
+ if (isComponentElement(element)) {
134
+ const store = getComponentSpecificStore(element.store, renderRuntime);
135
+ return component(element.props || emptyObject, store.context);
136
+ }
137
+ else {
138
+ return component(element.props || emptyObject);
139
+ }
140
+ }
141
+ export function component(Component) {
142
+ const Wrapped = ((props, ctx) => Component(props, ctx));
143
+ Wrapped._isComponent = 1;
144
+ return Wrapped;
145
+ }
146
+ export function createState(onChange) {
147
+ return new Proxy({}, {
148
+ set(target, prop, value) {
149
+ const prevValue = target[prop];
150
+ if (Object.is(prevValue, value)) {
151
+ return true;
152
+ }
153
+ const hadProperty = Object.hasOwn(target, prop);
154
+ target[prop] = value;
155
+ if (hadProperty) {
156
+ onChange();
157
+ }
158
+ return true;
159
+ },
160
+ });
161
+ }
162
+ export function isComponentElement(element) {
163
+ return element.flag & SIMP_ELEMENT_FLAG_FC && element.type._isComponent;
164
+ }
@@ -1,4 +1,4 @@
1
- import type { FunctionComponent, SimpNode } from '../core/index.js';
1
+ import type { FunctionalComponent, SimpNode } from '../core/index.js';
2
2
 
3
3
  export interface ProviderProps<T> {
4
4
  value: T;
@@ -16,9 +16,16 @@ export type SimpContext<T> = {
16
16
 
17
17
  export type ContextType<C extends SimpContext<any>> = C extends SimpContext<infer T> ? T : never;
18
18
 
19
- export type Provider<T> = FunctionComponent<ProviderProps<T>>;
20
- export type Consumer<T> = FunctionComponent<ConsumerProps<T>>;
19
+ export type Provider<T> = FunctionalComponent<ProviderProps<T>>;
20
+ export type Consumer<T> = FunctionalComponent<ConsumerProps<T>>;
21
21
 
22
- declare function createContext<T>(defaultValue: T): SimpContext<T>;
22
+ export interface CreateContext {
23
+ <T>(defaultValue: T): SimpContext<T>;
24
+ }
25
+
26
+ declare function createCreateContext(renderRuntime: SimpRenderRuntime): CreateContext;
23
27
 
24
- declare function useContext<T>(context: SimpContext<T>): T;
28
+ export interface UseContext {
29
+ <T>(context: SimpContext<T>): T;
30
+ }
31
+ declare function createUseContext(renderRuntime: SimpRenderRuntime): UseContext;
package/context/index.js CHANGED
@@ -1,64 +1,69 @@
1
- import { lifecycleEventBus, rerender } from '../core/internal.js';
2
- // In runtime these variables are nullable.
3
- let currentElement;
4
- let phase;
1
+ import { lifecycleEventBus, MOUNTING_PHASE, rerender, } from '../core/internal.js';
5
2
  lifecycleEventBus.subscribe(event => {
6
- if (event.type === 'beforeRender') {
7
- currentElement = event.element;
8
- phase = event.phase;
9
- }
10
- if (event.type === 'afterRender') {
11
- currentElement = null;
12
- phase = null;
13
- }
14
- if (event.type === 'unmounted') {
15
- event.element.context?.forEach(value => value.subs.delete(event.element.store));
3
+ if (event.type === 'unmounted' && event.element.context) {
4
+ const contextMap = event.element.context;
5
+ for (const entry of contextMap.values()) {
6
+ entry.subs.delete(event.element.store);
7
+ }
16
8
  }
17
9
  });
18
- export function createContext(defaultValue) {
19
- const context = {
20
- defaultValue,
21
- Provider(props) {
22
- if (!currentElement.context) {
23
- currentElement.context = new Map();
24
- }
25
- else if (phase === 'mounting') {
26
- currentElement.context = new Map(currentElement.context);
27
- }
28
- if (phase === 'mounting') {
29
- currentElement.context.set(context, { value: props.value, subs: new Set() });
30
- return props.children;
31
- }
32
- let contextEntry = currentElement.context.get(context);
33
- if (!contextEntry) {
34
- contextEntry = { value: props.value, subs: new Set() };
35
- currentElement.context.set(context, contextEntry);
36
- return props.children;
37
- }
38
- if (Object.is(contextEntry.value, props.value)) {
10
+ export function createCreateContext(renderRuntime) {
11
+ return defaultValue => {
12
+ const context = {
13
+ defaultValue,
14
+ Provider(props) {
15
+ const currentElement = renderRuntime.currentRenderingFCElement;
16
+ const renderPhase = renderRuntime.renderPhase;
17
+ let contextMap = currentElement.context;
18
+ if (!contextMap) {
19
+ currentElement.context = contextMap = new Map();
20
+ }
21
+ if (contextMap && renderPhase === MOUNTING_PHASE) {
22
+ currentElement.context = contextMap = new Map(currentElement.context);
23
+ }
24
+ if (renderPhase === MOUNTING_PHASE) {
25
+ contextMap.set(context, { value: props.value, subs: new Set() });
26
+ return props.children;
27
+ }
28
+ const entry = contextMap.get(context);
29
+ if (!entry) {
30
+ contextMap.set(context, { value: props.value, subs: new Set() });
31
+ return props.children;
32
+ }
33
+ if (Object.is(entry.value, props.value)) {
34
+ return props.children;
35
+ }
36
+ entry.value = props.value;
37
+ for (const sub of entry.subs) {
38
+ rerender(sub, renderRuntime);
39
+ }
39
40
  return props.children;
40
- }
41
- contextEntry.value = props.value;
42
- for (let sub of contextEntry.subs) {
43
- sub.forceRender = true;
44
- rerender(sub.latestElement);
45
- }
46
- return props.children;
47
- },
48
- Consumer(props) {
49
- return props.children(currentElement.context?.get(context)?.value ?? defaultValue);
50
- },
41
+ },
42
+ Consumer(props) {
43
+ const currentElement = renderRuntime.currentRenderingFCElement;
44
+ const contextMap = currentElement.context;
45
+ const store = currentElement.store;
46
+ const entry = contextMap?.get(context);
47
+ if (!entry) {
48
+ return props.children(defaultValue);
49
+ }
50
+ entry.subs.add(store);
51
+ return props.children(entry.value);
52
+ },
53
+ };
54
+ return context;
51
55
  };
52
- return context;
53
56
  }
54
- export function useContext(context) {
55
- const contextEntry = currentElement.context?.get(context);
56
- if (!contextEntry) {
57
- // No provider above: just return the default value, don't subscribe
58
- return context.defaultValue;
59
- }
60
- if (!contextEntry.subs.has(currentElement.store)) {
61
- contextEntry.subs.add(currentElement.store);
62
- }
63
- return contextEntry.value;
57
+ export function createUseContext(renderRuntime) {
58
+ return context => {
59
+ const currentElement = renderRuntime.currentRenderingFCElement;
60
+ const contextMap = currentElement.context;
61
+ const store = currentElement.store;
62
+ const entry = contextMap?.get(context);
63
+ if (!entry) {
64
+ return context.defaultValue;
65
+ }
66
+ entry.subs.add(store);
67
+ return entry.value;
68
+ };
64
69
  }