@simpreact/simpreact 0.0.8 → 0.0.9

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 (75) hide show
  1. package/compat/core.js +101 -15
  2. package/compat/hooks.js +15 -0
  3. package/compat/index.d.ts +10 -1
  4. package/compat/renderRuntime.js +47 -12
  5. package/component/index.js +96 -94
  6. package/context/index.js +27 -17
  7. package/core/createElement.js +14 -17
  8. package/core/flags.js +31 -0
  9. package/core/hostOperations.js +5 -13
  10. package/core/index.d.ts +38 -8
  11. package/core/index.js +3 -2
  12. package/core/internal.d.ts +136 -16
  13. package/core/internal.js +8 -16
  14. package/core/lifecycleEventBus.js +35 -16
  15. package/core/memo.js +4 -1
  16. package/core/mounting.js +70 -150
  17. package/core/mountingChildren.js +11 -29
  18. package/core/patching.js +122 -181
  19. package/core/patchingChildren.js +74 -145
  20. package/core/portal.js +1 -1
  21. package/core/processStack.js +115 -45
  22. package/core/ref.js +1 -0
  23. package/core/rerender.js +20 -22
  24. package/core/runtime.js +10 -2
  25. package/core/unmounting.js +41 -49
  26. package/core/unmountingChildren.js +9 -12
  27. package/core/utils.js +38 -16
  28. package/dom/attach-element-to-dom.js +16 -8
  29. package/dom/events.js +11 -15
  30. package/dom/index.d.ts +2 -0
  31. package/dom/props/attrMaps.js +90 -0
  32. package/dom/props/controlled/select.js +8 -10
  33. package/dom/props/props.js +13 -14
  34. package/hooks/index.d.ts +3 -0
  35. package/hooks/index.js +107 -84
  36. package/package.json +10 -5
  37. package/compat/context.d.ts +0 -7
  38. package/compat/core.d.ts +0 -48
  39. package/compat/dom.d.ts +0 -10
  40. package/compat/hooks.d.ts +0 -26
  41. package/compat/jsx-runtime.d.ts +0 -10
  42. package/compat/renderRuntime.d.ts +0 -6
  43. package/core/createElement.d.ts +0 -39
  44. package/core/fragment.d.ts +0 -5
  45. package/core/hostAdapter.d.ts +0 -23
  46. package/core/hostOperations.d.ts +0 -5
  47. package/core/lifecycleEventBus.d.ts +0 -39
  48. package/core/memo.d.ts +0 -8
  49. package/core/mounting.d.ts +0 -7
  50. package/core/mountingChildren.d.ts +0 -4
  51. package/core/patching.d.ts +0 -8
  52. package/core/patchingChildren.d.ts +0 -6
  53. package/core/portal.d.ts +0 -2
  54. package/core/processStack.d.ts +0 -106
  55. package/core/ref.d.ts +0 -18
  56. package/core/rerender.d.ts +0 -4
  57. package/core/runtime.d.ts +0 -17
  58. package/core/unmounting.d.ts +0 -7
  59. package/core/unmountingChildren.d.ts +0 -4
  60. package/core/utils.d.ts +0 -11
  61. package/dom/attach-element-to-dom.d.ts +0 -5
  62. package/dom/domAdapter.d.ts +0 -3
  63. package/dom/events.d.ts +0 -27
  64. package/dom/namespace.d.ts +0 -2
  65. package/dom/props/controlled/index.d.ts +0 -7
  66. package/dom/props/controlled/input.d.ts +0 -7
  67. package/dom/props/controlled/select.d.ts +0 -6
  68. package/dom/props/controlled/textarea.d.ts +0 -6
  69. package/dom/props/dangerInnerHTML.d.ts +0 -7
  70. package/dom/props/index.d.ts +0 -1
  71. package/dom/props/props.d.ts +0 -5
  72. package/dom/props/style.d.ts +0 -1
  73. package/dom/render.d.ts +0 -8
  74. package/shared/lang.d.ts +0 -3
  75. package/shared/utils.d.ts +0 -5
package/compat/core.js CHANGED
@@ -1,6 +1,23 @@
1
1
  import { createElement as _createElement, createPortal as _createPortal, Fragment as _Fragment, memo as _memo, } from '../core/index.js';
2
- import { SIMP_ELEMENT_FLAG_FRAGMENT, SIMP_ELEMENT_FLAG_PORTAL } from '../core/internal.js';
2
+ import { isFragment, isPortal, withSyncRerender } from '../core/internal.js';
3
3
  import { useCatch, useState } from './hooks.js';
4
+ import { renderRuntime } from './renderRuntime.js';
5
+ // Threads refs through FC elements without polluting string-keyed props.
6
+ // Symbol keys are invisible to `for…in` and Object.keys, so they never
7
+ // accidentally end up on HOST elements via `{...props}` spread.
8
+ export const REF_SYMBOL = Symbol('simpreact.compat.ref');
9
+ // Wraps core createElement to strip `ref` from FC props (matching React's
10
+ // model where ref is not part of the props the component receives).
11
+ export function createElement(type, props, ...args) {
12
+ if (typeof type === 'function' && props != null && 'ref' in props) {
13
+ const { ref, ...restProps } = props;
14
+ if (ref != null) {
15
+ restProps[REF_SYMBOL] = ref;
16
+ }
17
+ return _createElement(type, restProps, ...args);
18
+ }
19
+ return _createElement(type, props, ...args);
20
+ }
4
21
  export const Children = {
5
22
  map(children, fn) {
6
23
  return Children.toArray(children).map(fn);
@@ -40,47 +57,112 @@ export function cloneElement(element, props, ...children) {
40
57
  if (!isValidElement(element)) {
41
58
  throw new Error(`cloneElement: expected a SimpElement, got ${element}`);
42
59
  }
43
- if ((element.flag & SIMP_ELEMENT_FLAG_PORTAL) !== 0) {
60
+ if (isPortal(element)) {
44
61
  throw new Error('cloneElement: the argument must be a SimpElement, but you passed a portal instead.');
45
62
  }
46
- return createElement((element.flag & SIMP_ELEMENT_FLAG_FRAGMENT) !== 0 ? Fragment : element.type, Object.assign({}, element.props, props), arguments.length > 2 ? children : props.children || element.children);
63
+ const mergedProps = Object.assign({}, element.props, props);
64
+ const resolvedChildren = arguments.length > 2 ? children : mergedProps.children !== undefined ? mergedProps.children : element.children;
65
+ return createElement(isFragment(element) ? Fragment : element.type, mergedProps, resolvedChildren);
47
66
  }
48
67
  export function isValidElement(element) {
49
68
  return typeof element === 'object' && element !== null && 'flag' in element;
50
69
  }
51
70
  export function Suspense(props) {
52
- const [isSuspended, setIsSuspended] = useState(false);
71
+ const [pendingCount, setPendingCount] = useState(0);
53
72
  useCatch(error => {
54
73
  if (!(error instanceof Promise)) {
55
74
  throw error;
56
75
  }
57
- if (isSuspended) {
58
- return;
59
- }
60
- setIsSuspended(true);
61
- error.then(() => setIsSuspended(false));
76
+ setPendingCount(c => c + 1);
77
+ error.then(() => setPendingCount(c => c - 1));
62
78
  });
63
- return isSuspended ? props.fallback : props.children;
79
+ return pendingCount > 0 ? props.fallback : props.children;
64
80
  }
65
81
  export function StrictMode(props) {
66
82
  return props.children;
67
83
  }
84
+ // Reads the ref from the Symbol slot so that string-keyed `ref` never
85
+ // appears in the props the inner component receives.
68
86
  export function forwardRef(Component) {
69
87
  return function Forwarded(props) {
70
- return Component(props, props?.ref || null);
88
+ const ref = props?.[REF_SYMBOL] ?? null;
89
+ if (props != null && REF_SYMBOL in props) {
90
+ const { [REF_SYMBOL]: _, ...restProps } = props;
91
+ return Component(restProps, ref);
92
+ }
93
+ return Component(props, ref);
71
94
  };
72
95
  }
96
+ export const version = '18.3.1';
73
97
  export const Fragment = _Fragment;
74
- export const createElement = _createElement;
75
98
  export const createPortal = _createPortal;
76
99
  export const memo = _memo;
77
- export const flushSync = (value) => value;
100
+ // Minimal React / ReactDOM internals shim for react-dom/test-utils.
101
+ export const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
102
+ ReactCurrentOwner: { current: null },
103
+ ReactCurrentDispatcher: { current: null },
104
+ ReactCurrentBatchConfig: { transition: null },
105
+ // react-dom/test-utils: var EventInternals = SecretInternals.Events; EventInternals[0..4]
106
+ Events: [
107
+ /* getInstanceFromNode */ _node => null,
108
+ /* getNodeFromInstance */ _inst => null,
109
+ /* getFiberCurrentPropsFromNode */ _node => null,
110
+ /* enqueueStateRestore */ () => { },
111
+ /* restoreStateIfNeeded */ () => { },
112
+ ],
113
+ };
114
+ // react-dom/test-utils uses React.unstable_act / React.act for flushing updates.
115
+ export async function unstable_act(callback) {
116
+ const result = callback();
117
+ if (result && typeof result.then === 'function')
118
+ await result;
119
+ await Promise.resolve();
120
+ }
121
+ export { unstable_act as act };
122
+ export function flushSync(callback) {
123
+ let result;
124
+ withSyncRerender(renderRuntime, () => {
125
+ result = callback();
126
+ });
127
+ return result;
128
+ }
129
+ export function lazy(factory) {
130
+ let state = null;
131
+ return function LazyComponent(props) {
132
+ if (state === null) {
133
+ const promise = factory().then(mod => {
134
+ state = { status: 'resolved', component: mod.default };
135
+ }, reason => {
136
+ state = { status: 'rejected', reason };
137
+ });
138
+ state = { status: 'pending', promise };
139
+ }
140
+ if (state.status === 'pending') {
141
+ throw state.promise;
142
+ }
143
+ if (state.status === 'rejected') {
144
+ throw state.reason;
145
+ }
146
+ return createElement(state.component, props);
147
+ };
148
+ }
149
+ // Minimal base class for React-style class components.
150
+ // setState and forceUpdate are no-ops here; the compat renderer overrides
151
+ // them on each instance with real implementations backed by the render queue.
78
152
  export class Component {
79
- constructor() {
80
- throw new Error('Not implemented.');
153
+ constructor(props) {
154
+ this.props = props;
155
+ this.state = {};
156
+ }
157
+ setState(_updater, _callback) { }
158
+ forceUpdate(_callback) { }
159
+ render() {
160
+ throw new Error('Component.render() must be implemented by the subclass.');
81
161
  }
82
162
  }
163
+ Component.prototype.isReactComponent = true;
83
164
  export default {
165
+ version,
84
166
  Children,
85
167
  cloneElement,
86
168
  isValidElement,
@@ -92,5 +174,9 @@ export default {
92
174
  createPortal,
93
175
  memo,
94
176
  flushSync,
177
+ lazy,
95
178
  Component,
179
+ __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
180
+ unstable_act,
181
+ act: unstable_act,
96
182
  };
package/compat/hooks.js CHANGED
@@ -65,15 +65,30 @@ export function useMemo(factory, deps) {
65
65
  export function useCallback(cb, deps) {
66
66
  return useMemo(() => cb, deps);
67
67
  }
68
+ export function useImperativeHandle(ref, init, deps) {
69
+ useLayoutEffect(() => {
70
+ if (typeof ref === 'function') {
71
+ ref(init());
72
+ }
73
+ else if (ref != null) {
74
+ ref.current = init();
75
+ }
76
+ }, deps);
77
+ }
78
+ export function useDebugValue(_value, _format) { }
68
79
  export default {
69
80
  useSyncExternalStore,
70
81
  useReducer,
71
82
  useId,
72
83
  useMemo,
73
84
  useCallback,
85
+ useImperativeHandle,
86
+ useDebugValue,
74
87
  useState,
75
88
  useEffect,
76
89
  useLayoutEffect,
77
90
  useInsertionEffect,
78
91
  useRef,
92
+ useRerender,
93
+ useCatch,
79
94
  };
package/compat/index.d.ts CHANGED
@@ -50,6 +50,12 @@ export declare function useReducer<R extends (state: any, action: any) => any, I
50
50
  export declare function useId(prefix?: string): string;
51
51
  export declare function useMemo<T>(factory: () => T, deps: DependencyList): T;
52
52
  export declare function useCallback<T>(cb: T, deps: DependencyList): T;
53
+ export declare function useImperativeHandle<T>(
54
+ ref: { current: T | null } | ((value: T | null) => void) | null | undefined,
55
+ init: () => T,
56
+ deps?: DependencyList
57
+ ): void;
58
+ export declare function useDebugValue(value: unknown, format?: (value: unknown) => unknown): void;
53
59
  export declare function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
54
60
  export declare function useState<S>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
55
61
  export declare function useEffect(effect: Effect, deps?: DependencyList): void;
@@ -58,7 +64,7 @@ export declare function useInsertionEffect(effect: Effect, deps?: DependencyList
58
64
  export declare function useRef<T>(initialValue: T): RefObject<T>;
59
65
  export declare function useRef<T>(initialValue: T | null): RefObject<T | null>;
60
66
  export declare function useRef<T = undefined>(initialValue?: T): RefObject<T | undefined>;
61
- export declare function useCatch(errorBoundary: FC): [Error | null, (error: Error) => void];
67
+ export declare function useCatch(handler: (error: unknown) => void): void;
62
68
 
63
69
  export declare function hydrate(): void;
64
70
  export declare function render(element: SimpElement, parentReference: Nullable<HTMLElement>): void;
@@ -94,6 +100,9 @@ export declare function createPortal(children: SimpNode, container: any): SimpEl
94
100
 
95
101
  export declare function memo(Component: FC, compare: (objA: any, objB: any) => boolean): FC;
96
102
  export declare function flushSync(value: any): any;
103
+ export declare function lazy<T extends FC<any>>(factory: () => Promise<{ default: T }>): FC<any>;
104
+
105
+ export declare const version: string;
97
106
 
98
107
  export declare class Component {}
99
108
 
@@ -1,17 +1,52 @@
1
1
  import { domAdapter } from '../dom/index.js';
2
+ import { createUseEffect, createUseRef, createUseRerender } from '../hooks/index.js';
3
+ import { createRenderRuntime } from '../core/internal.js';
2
4
  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
- };
5
+ // Lazily assigned after renderRuntime is created to avoid a circular reference
6
+ // at module-evaluation time. All are set before any render can fire.
7
+ let useRef;
8
+ let useRerender;
9
+ let useLayoutEffect;
10
+ export const renderRuntime = createRenderRuntime(domAdapter, function renderer(component, element) {
11
+ const props = element.props || emptyObject;
12
+ const proto = component.prototype;
13
+ if (proto?.isReactComponent || proto?.render) {
14
+ // Persist the instance across re-renders so state is not reset on every
15
+ // render cycle. Hook calls run in the current FC render context.
16
+ const instRef = useRef(null);
17
+ const rerender = useRerender();
18
+ if (instRef.current === null) {
19
+ instRef.current = new component(props);
20
+ if (instRef.current.state == null) {
21
+ instRef.current.state = {};
22
+ }
23
+ }
24
+ const inst = instRef.current;
25
+ inst.props = props;
26
+ // Override the no-op stubs from Component base with real implementations.
27
+ inst.setState = function (updater, callback) {
28
+ const prevState = inst.state;
29
+ const patch = typeof updater === 'function' ? updater(prevState, inst.props) : updater;
30
+ inst.state = Object.assign({}, prevState, patch);
31
+ rerender();
32
+ callback?.();
33
+ };
34
+ inst.forceUpdate = function (callback) {
35
+ rerender();
36
+ callback?.();
37
+ };
38
+ useLayoutEffect(() => {
39
+ if (typeof inst.componentDidMount === 'function') {
40
+ inst.componentDidMount();
41
+ }
42
+ }, []);
43
+ return inst.render();
44
+ }
45
+ return component(props);
46
+ });
47
+ useRef = createUseRef(renderRuntime);
48
+ useRerender = createUseRerender(renderRuntime);
49
+ useLayoutEffect = createUseEffect(renderRuntime);
13
50
  export default {
14
51
  renderRuntime,
15
52
  };
16
- // TODO
17
- window.__SIMP_RUNTIME__ = renderRuntime;
@@ -1,11 +1,11 @@
1
- import { lifecycleEventBus, rerender, SIMP_ELEMENT_FLAG_FC, } from '../core/internal.js';
1
+ import { isFC, registerLifecyclePlugin, rerender, } from '../core/internal.js';
2
2
  import { emptyObject, shallowEqual, } from '../shared/index.js';
3
- const componentSpecificStoreByElementStore = new WeakMap();
4
- function getComponentSpecificStore(store, renderRuntime) {
5
- let hooksSpecificStore = componentSpecificStoreByElementStore.get(store);
3
+ const componentSpecificStoreByElement = new WeakMap();
4
+ function getComponentSpecificStore(element, renderRuntime) {
5
+ let hooksSpecificStore = componentSpecificStoreByElement.get(element);
6
6
  if (!hooksSpecificStore) {
7
7
  function _rerender() {
8
- rerender(store, renderRuntime);
8
+ rerender(element, renderRuntime);
9
9
  }
10
10
  hooksSpecificStore = {
11
11
  context: {
@@ -17,121 +17,123 @@ function getComponentSpecificStore(store, renderRuntime) {
17
17
  pendingEffectStates: null,
18
18
  effectStates: null,
19
19
  };
20
- componentSpecificStoreByElementStore.set(store, hooksSpecificStore);
20
+ componentSpecificStoreByElement.set(element, hooksSpecificStore);
21
21
  }
22
22
  return hooksSpecificStore;
23
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;
24
+ registerLifecyclePlugin(bus => {
25
+ bus.subscribe(event => {
26
+ if (event.type === 'errored') {
27
+ if (event.handled) {
28
+ return;
42
29
  }
43
- try {
44
- for (let i = 0; i < catchers.length; i++) {
45
- catchers[i](curError);
30
+ let element = event.element;
31
+ let curError = event.error;
32
+ let catchers;
33
+ while (element) {
34
+ if (!isComponentElement(element)) {
35
+ element = element.parent;
36
+ continue;
37
+ }
38
+ const store = getComponentSpecificStore(element, event.renderRuntime);
39
+ catchers = store.context.catchers;
40
+ if (!catchers) {
41
+ element = element.parent;
42
+ continue;
43
+ }
44
+ try {
45
+ for (let i = 0; i < catchers.length; i++) {
46
+ catchers[i](curError);
47
+ }
48
+ event.handled = true;
49
+ break;
50
+ }
51
+ catch (error) {
52
+ element = element.parent;
53
+ curError = error;
46
54
  }
47
- event.handled = true;
48
- break;
49
- }
50
- catch (error) {
51
- element = element.parent;
52
- curError = error;
53
55
  }
56
+ return;
54
57
  }
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;
58
+ if (!isComponentElement(event.element)) {
66
59
  return;
67
60
  }
68
- case 'afterRender': {
69
- if (!store.context.effects) {
61
+ const store = getComponentSpecificStore(event.element, event.renderRuntime);
62
+ switch (event.type) {
63
+ case 'beforeRender': {
64
+ store.context.effects = [];
65
+ store.context.catchers = [];
66
+ store.pendingEffectStates = null;
70
67
  return;
71
68
  }
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
- };
69
+ case 'afterRender': {
70
+ if (!store.context.effects) {
71
+ return;
81
72
  }
82
- if (!shallowEqual(renderEffectState.deps, state.deps)) {
83
- state.effect = renderEffectState.effect;
84
- state.deps = renderEffectState.deps || null;
85
- (store.pendingEffectStates ||= []).push(state);
73
+ for (let i = 0; i < store.context.effects.length; i++) {
74
+ const renderEffectState = store.context.effects[i];
75
+ let state = (store.effectStates ||= [])[i];
76
+ if (!state) {
77
+ state = store.effectStates[i] = {
78
+ effect: renderEffectState.effect,
79
+ deps: null,
80
+ cleanup: null,
81
+ };
82
+ }
83
+ if (!shallowEqual(renderEffectState.deps, state.deps)) {
84
+ state.effect = renderEffectState.effect;
85
+ state.deps = renderEffectState.deps || null;
86
+ (store.pendingEffectStates ||= []).push(state);
87
+ }
86
88
  }
87
- }
88
- return;
89
- }
90
- case 'mounted': {
91
- if (!store.pendingEffectStates) {
92
89
  return;
93
90
  }
94
- const effects = store.pendingEffectStates;
95
- store.pendingEffectStates = null;
96
- for (const state of effects) {
97
- if (typeof state.cleanup === 'function') {
98
- state.cleanup();
91
+ case 'mounted': {
92
+ if (!store.pendingEffectStates) {
93
+ return;
94
+ }
95
+ const effects = store.pendingEffectStates;
96
+ store.pendingEffectStates = null;
97
+ for (const state of effects) {
98
+ if (typeof state.cleanup === 'function') {
99
+ state.cleanup();
100
+ }
101
+ state.cleanup = state.effect() || null;
99
102
  }
100
- state.cleanup = state.effect() || null;
101
- }
102
- return;
103
- }
104
- case 'updated': {
105
- if (!store.pendingEffectStates) {
106
103
  return;
107
104
  }
108
- const effects = store.pendingEffectStates;
109
- store.pendingEffectStates = null;
110
- for (const state of effects) {
111
- if (typeof state.cleanup === 'function') {
112
- state.cleanup();
105
+ case 'updated': {
106
+ if (!store.pendingEffectStates) {
107
+ return;
108
+ }
109
+ const effects = store.pendingEffectStates;
110
+ store.pendingEffectStates = null;
111
+ for (const state of effects) {
112
+ if (typeof state.cleanup === 'function') {
113
+ state.cleanup();
114
+ }
115
+ state.cleanup = state.effect() || null;
113
116
  }
114
- state.cleanup = state.effect() || null;
115
- }
116
- return;
117
- }
118
- case 'unmounted': {
119
- if (!store.effectStates) {
120
117
  return;
121
118
  }
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();
119
+ case 'unmounted': {
120
+ if (!store.effectStates) {
121
+ return;
122
+ }
123
+ const effects = store.effectStates;
124
+ store.effectStates = null;
125
+ for (const state of effects) {
126
+ if (state && 'cleanup' in state && typeof state.cleanup === 'function') {
127
+ state.cleanup();
128
+ }
127
129
  }
128
130
  }
129
131
  }
130
- }
132
+ });
131
133
  });
132
134
  export function componentRenderer(component, element, renderRuntime) {
133
135
  if (isComponentElement(element)) {
134
- const store = getComponentSpecificStore(element.store, renderRuntime);
136
+ const store = getComponentSpecificStore(element, renderRuntime);
135
137
  return component(element.props || emptyObject, store.context);
136
138
  }
137
139
  else {
@@ -160,5 +162,5 @@ export function createState(onChange) {
160
162
  });
161
163
  }
162
164
  export function isComponentElement(element) {
163
- return element.flag & SIMP_ELEMENT_FLAG_FC && element.type._isComponent;
165
+ return isFC(element) && element.type._isComponent;
164
166
  }
package/context/index.js CHANGED
@@ -1,27 +1,39 @@
1
- import { lifecycleEventBus, MOUNTING_PHASE, rerender, } from '../core/internal.js';
2
- lifecycleEventBus.subscribe(event => {
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);
1
+ import { registerLifecyclePlugin, rerender, } from '../core/internal.js';
2
+ const currentFCByRuntime = new WeakMap();
3
+ registerLifecyclePlugin(bus => {
4
+ bus.subscribe(event => {
5
+ if (event.type === 'beforeRender') {
6
+ currentFCByRuntime.set(event.renderRuntime, event.element);
7
7
  }
8
- }
8
+ else if (event.type === 'afterRender' || event.type === 'errored') {
9
+ currentFCByRuntime.set(event.renderRuntime, null);
10
+ }
11
+ if (event.type === 'unmounted' && event.element.context) {
12
+ const contextMap = event.element.context;
13
+ for (const entry of contextMap.values()) {
14
+ entry.subs.delete(event.element);
15
+ }
16
+ }
17
+ });
9
18
  });
10
19
  export function createCreateContext(renderRuntime) {
11
20
  return defaultValue => {
21
+ const providerElements = new WeakSet();
12
22
  const context = {
13
23
  defaultValue,
14
24
  Provider(props) {
15
- const currentElement = renderRuntime.currentRenderingFCElement;
16
- const renderPhase = renderRuntime.renderPhase;
25
+ const currentElement = currentFCByRuntime.get(renderRuntime);
26
+ const isFirstRender = !providerElements.has(currentElement);
17
27
  let contextMap = currentElement.context;
18
28
  if (!contextMap) {
19
29
  currentElement.context = contextMap = new Map();
30
+ providerElements.add(currentElement);
20
31
  }
21
- else if (renderPhase === MOUNTING_PHASE) {
32
+ else if (isFirstRender) {
22
33
  currentElement.context = contextMap = new Map(currentElement.context);
34
+ providerElements.add(currentElement);
23
35
  }
24
- if (renderPhase === MOUNTING_PHASE) {
36
+ if (isFirstRender) {
25
37
  contextMap.set(context, { value: props.value, subs: new Set() });
26
38
  return props.children;
27
39
  }
@@ -40,14 +52,13 @@ export function createCreateContext(renderRuntime) {
40
52
  return props.children;
41
53
  },
42
54
  Consumer(props) {
43
- const currentElement = renderRuntime.currentRenderingFCElement;
55
+ const currentElement = currentFCByRuntime.get(renderRuntime);
44
56
  const contextMap = currentElement.context;
45
- const store = currentElement.store;
46
57
  const entry = contextMap?.get(context);
47
58
  if (!entry) {
48
59
  return props.children(defaultValue);
49
60
  }
50
- entry.subs.add(store);
61
+ entry.subs.add(currentElement);
51
62
  return props.children(entry.value);
52
63
  },
53
64
  };
@@ -56,14 +67,13 @@ export function createCreateContext(renderRuntime) {
56
67
  }
57
68
  export function createUseContext(renderRuntime) {
58
69
  return context => {
59
- const currentElement = renderRuntime.currentRenderingFCElement;
70
+ const currentElement = currentFCByRuntime.get(renderRuntime);
60
71
  const contextMap = currentElement.context;
61
- const store = currentElement.store;
62
72
  const entry = contextMap?.get(context);
63
73
  if (!entry) {
64
74
  return context.defaultValue;
65
75
  }
66
- entry.subs.add(store);
76
+ entry.subs.add(currentElement);
67
77
  return entry.value;
68
78
  };
69
79
  }