@lynx-js/react 0.109.2 → 0.110.0

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 (131) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/package.json +1 -1
  3. package/refresh/.turbo/turbo-build.log +1 -1
  4. package/runtime/lib/backgroundSnapshot.d.ts +2 -1
  5. package/runtime/lib/backgroundSnapshot.js +65 -42
  6. package/runtime/lib/backgroundSnapshot.js.map +1 -1
  7. package/runtime/lib/hooks/react.js +3 -3
  8. package/runtime/lib/hooks/react.js.map +1 -1
  9. package/runtime/lib/lifecycle/destroy.js +1 -0
  10. package/runtime/lib/lifecycle/destroy.js.map +1 -1
  11. package/runtime/lib/lifecycle/event/delayEvents.js +3 -0
  12. package/runtime/lib/lifecycle/event/delayEvents.js.map +1 -1
  13. package/runtime/lib/lifecycle/event/delayLifecycleEvents.d.ts +3 -2
  14. package/runtime/lib/lifecycle/event/delayLifecycleEvents.js +1 -12
  15. package/runtime/lib/lifecycle/event/delayLifecycleEvents.js.map +1 -1
  16. package/runtime/lib/lifecycle/event/jsReady.d.ts +1 -1
  17. package/runtime/lib/lifecycle/event/jsReady.js +3 -2
  18. package/runtime/lib/lifecycle/event/jsReady.js.map +1 -1
  19. package/runtime/lib/lifecycle/patch/commit.d.ts +0 -2
  20. package/runtime/lib/lifecycle/patch/commit.js +8 -36
  21. package/runtime/lib/lifecycle/patch/commit.js.map +1 -1
  22. package/runtime/lib/lifecycle/patch/error.d.ts +8 -0
  23. package/runtime/lib/lifecycle/patch/error.js +47 -0
  24. package/runtime/lib/lifecycle/patch/error.js.map +1 -0
  25. package/runtime/lib/lifecycle/patch/isMainThreadHydrationFinished.js +3 -0
  26. package/runtime/lib/lifecycle/patch/isMainThreadHydrationFinished.js.map +1 -1
  27. package/runtime/lib/lifecycle/patch/snapshotPatch.d.ts +2 -2
  28. package/runtime/lib/lifecycle/patch/snapshotPatch.js.map +1 -1
  29. package/runtime/lib/lifecycle/patch/snapshotPatchApply.js +10 -10
  30. package/runtime/lib/lifecycle/patch/snapshotPatchApply.js.map +1 -1
  31. package/runtime/lib/lifecycle/patch/updateMainThread.d.ts +0 -1
  32. package/runtime/lib/lifecycle/patch/updateMainThread.js +6 -13
  33. package/runtime/lib/lifecycle/patch/updateMainThread.js.map +1 -1
  34. package/runtime/lib/lifecycle/ref/delay.d.ts +31 -0
  35. package/runtime/lib/lifecycle/ref/delay.js +80 -0
  36. package/runtime/lib/lifecycle/ref/delay.js.map +1 -0
  37. package/runtime/lib/lifecycle/reload.d.ts +1 -1
  38. package/runtime/lib/lifecycle/reload.js +7 -4
  39. package/runtime/lib/lifecycle/reload.js.map +1 -1
  40. package/runtime/lib/lifecycle/render.js +3 -2
  41. package/runtime/lib/lifecycle/render.js.map +1 -1
  42. package/runtime/lib/lifecycleConstant.d.ts +10 -7
  43. package/runtime/lib/lifecycleConstant.js +8 -8
  44. package/runtime/lib/lifecycleConstant.js.map +1 -1
  45. package/runtime/lib/list.d.ts +1 -1
  46. package/runtime/lib/list.js +9 -7
  47. package/runtime/lib/list.js.map +1 -1
  48. package/runtime/lib/lynx/calledByNative.js +8 -5
  49. package/runtime/lib/lynx/calledByNative.js.map +1 -1
  50. package/runtime/lib/lynx/component.js +7 -0
  51. package/runtime/lib/lynx/component.js.map +1 -1
  52. package/runtime/lib/lynx/dynamic-js.d.ts +5 -1
  53. package/runtime/lib/lynx/dynamic-js.js +1 -0
  54. package/runtime/lib/lynx/dynamic-js.js.map +1 -1
  55. package/runtime/lib/lynx/env.d.ts +1 -1
  56. package/runtime/lib/lynx/env.js +13 -13
  57. package/runtime/lib/lynx/env.js.map +1 -1
  58. package/runtime/lib/lynx/lazy-bundle.js +7 -5
  59. package/runtime/lib/lynx/lazy-bundle.js.map +1 -1
  60. package/runtime/lib/lynx/performance.js +1 -1
  61. package/runtime/lib/lynx/performance.js.map +1 -1
  62. package/runtime/lib/lynx/runWithForce.js +3 -0
  63. package/runtime/lib/lynx/runWithForce.js.map +1 -1
  64. package/runtime/lib/lynx/tt.js +12 -33
  65. package/runtime/lib/lynx/tt.js.map +1 -1
  66. package/runtime/lib/lynx-api.d.ts +1 -1
  67. package/runtime/lib/lynx.js +5 -6
  68. package/runtime/lib/lynx.js.map +1 -1
  69. package/runtime/lib/snapshot/ref.d.ts +14 -9
  70. package/runtime/lib/snapshot/ref.js +64 -73
  71. package/runtime/lib/snapshot/ref.js.map +1 -1
  72. package/runtime/lib/snapshot/spread.js +9 -8
  73. package/runtime/lib/snapshot/spread.js.map +1 -1
  74. package/runtime/lib/snapshot/workletRef.d.ts +3 -3
  75. package/runtime/lib/snapshot/workletRef.js +27 -8
  76. package/runtime/lib/snapshot/workletRef.js.map +1 -1
  77. package/runtime/lib/snapshot.d.ts +4 -4
  78. package/runtime/lib/snapshot.js +28 -13
  79. package/runtime/lib/snapshot.js.map +1 -1
  80. package/runtime/lib/snapshotInstanceHydrationMap.d.ts +9 -0
  81. package/runtime/lib/snapshotInstanceHydrationMap.js +16 -0
  82. package/runtime/lib/snapshotInstanceHydrationMap.js.map +1 -0
  83. package/runtime/lib/worklet/workletRef.js +1 -2
  84. package/runtime/lib/worklet/workletRef.js.map +1 -1
  85. package/runtime/src/backgroundSnapshot.ts +74 -47
  86. package/runtime/src/hooks/react.ts +3 -3
  87. package/runtime/src/lifecycle/destroy.ts +1 -0
  88. package/runtime/src/lifecycle/event/delayEvents.ts +3 -0
  89. package/runtime/src/lifecycle/event/delayLifecycleEvents.ts +7 -13
  90. package/runtime/src/lifecycle/event/jsReady.ts +4 -3
  91. package/runtime/src/lifecycle/patch/commit.ts +10 -41
  92. package/runtime/src/lifecycle/patch/error.ts +61 -0
  93. package/runtime/src/lifecycle/patch/isMainThreadHydrationFinished.ts +3 -0
  94. package/runtime/src/lifecycle/patch/snapshotPatch.ts +2 -2
  95. package/runtime/src/lifecycle/patch/snapshotPatchApply.ts +28 -28
  96. package/runtime/src/lifecycle/patch/updateMainThread.ts +7 -16
  97. package/runtime/src/lifecycle/ref/delay.ts +99 -0
  98. package/runtime/src/lifecycle/reload.ts +9 -6
  99. package/runtime/src/lifecycle/render.ts +3 -2
  100. package/runtime/src/lifecycleConstant.ts +11 -7
  101. package/runtime/src/list.ts +9 -7
  102. package/runtime/src/lynx/calledByNative.ts +14 -9
  103. package/runtime/src/lynx/component.ts +9 -0
  104. package/runtime/src/lynx/dynamic-js.ts +5 -1
  105. package/runtime/src/lynx/env.ts +17 -17
  106. package/runtime/src/lynx/lazy-bundle.ts +22 -14
  107. package/runtime/src/lynx/performance.ts +1 -1
  108. package/runtime/src/lynx/runWithForce.ts +9 -5
  109. package/runtime/src/lynx/tt.ts +19 -37
  110. package/runtime/src/lynx-api.ts +1 -1
  111. package/runtime/src/lynx.ts +6 -6
  112. package/runtime/src/snapshot/ref.ts +77 -87
  113. package/runtime/src/snapshot/spread.ts +8 -8
  114. package/runtime/src/snapshot/workletRef.ts +35 -9
  115. package/runtime/src/snapshot.ts +39 -20
  116. package/runtime/src/snapshotInstanceHydrationMap.ts +17 -0
  117. package/runtime/src/worklet/workletRef.ts +1 -2
  118. package/testing-library/dist/env/vitest.js +5 -3
  119. package/testing-library/dist/pure.js +4 -3
  120. package/transform/dist/wasm.cjs +1 -1
  121. package/worklet-runtime/dist/dev.js +1 -4
  122. package/worklet-runtime/dist/main.js +0 -3
  123. package/worklet-runtime/lib/bindings/observers.d.ts +14 -1
  124. package/worklet-runtime/lib/bindings/observers.js +7 -7
  125. package/worklet-runtime/lib/bindings/observers.js.map +1 -1
  126. package/worklet-runtime/lib/hydrate.js +0 -5
  127. package/worklet-runtime/lib/hydrate.js.map +1 -1
  128. package/runtime/lib/lifecycle/delayUnmount.d.ts +0 -8
  129. package/runtime/lib/lifecycle/delayUnmount.js +0 -65
  130. package/runtime/lib/lifecycle/delayUnmount.js.map +0 -1
  131. package/runtime/src/lifecycle/delayUnmount.ts +0 -77
@@ -3,21 +3,18 @@
3
3
  // LICENSE file in the root directory of this source tree.
4
4
  import type { Element, Worklet, WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings';
5
5
 
6
- import { nextCommitTaskId } from '../lifecycle/patch/commit.js';
7
- import { SnapshotInstance, backgroundSnapshotInstanceManager } from '../snapshot.js';
6
+ import type { SnapshotInstance } from '../snapshot.js';
8
7
  import { workletUnRef } from './workletRef.js';
8
+ import { RefProxy } from '../lifecycle/ref/delay.js';
9
9
 
10
- let globalRefPatch: Record<string, number | null> = {};
11
- const globalRefsToRemove: Map</* commitId */ number, Map</* sign */ string, /* ref */ any>> = /* @__PURE__ */ new Map();
12
- const globalRefsToSet: Map</* commitId */ number, Record<string, number>> = /* @__PURE__ */ new Map();
13
- let nextRefId = 1;
10
+ const refsToClear: Ref[] = [];
11
+ const refsToApply: (Ref | [snapshotInstanceId: number, expIndex: number])[] = [];
14
12
 
15
- function unref(snapshot: SnapshotInstance, recursive: boolean): void {
16
- snapshot.__ref_set?.forEach(v => {
17
- globalRefPatch[v] = null;
18
- });
19
- snapshot.__ref_set?.clear();
13
+ type Ref = (((ref: RefProxy) => () => void) | { current: RefProxy | null }) & {
14
+ _unmount?: () => void;
15
+ };
20
16
 
17
+ function unref(snapshot: SnapshotInstance, recursive: boolean): void {
21
18
  snapshot.__worklet_ref_set?.forEach(v => {
22
19
  if (v) {
23
20
  workletUnRef(v as Worklet | WorkletRefImpl<Element>);
@@ -32,91 +29,65 @@ function unref(snapshot: SnapshotInstance, recursive: boolean): void {
32
29
  }
33
30
  }
34
31
 
35
- function applyRef(ref: any, value: any) {
36
- // TODO: ref: exceptions thrown in user functions should be able to be caught by an Error Boundary
37
- if (typeof ref == 'function') {
38
- const hasRefUnmount = typeof ref._unmount == 'function';
39
- if (hasRefUnmount) {
40
- // @ts-ignore TS doesn't like moving narrowing checks into variables
41
- ref._unmount();
42
- }
32
+ // This function is modified from preact source code.
33
+ function applyRef(ref: Ref, value: null | [snapshotInstanceId: number, expIndex: number]): void {
34
+ const newRef = value && new RefProxy(value);
43
35
 
44
- if (!hasRefUnmount || value != null) {
45
- // Store the cleanup function on the function
46
- // instance object itself to avoid shape
47
- // transitioning vnode
48
- ref._unmount = ref(value);
49
- }
50
- } else ref.current = value;
51
- }
36
+ try {
37
+ if (typeof ref == 'function') {
38
+ const hasRefUnmount = typeof ref._unmount == 'function';
39
+ if (hasRefUnmount) {
40
+ ref._unmount!();
41
+ }
52
42
 
53
- function updateBackgroundRefs(commitId: number): void {
54
- const oldRefMap = globalRefsToRemove.get(commitId);
55
- if (oldRefMap) {
56
- globalRefsToRemove.delete(commitId);
57
- for (const ref of oldRefMap.values()) {
58
- applyRef(ref, null);
59
- }
60
- }
61
- const newRefMap = globalRefsToSet.get(commitId);
62
- if (newRefMap) {
63
- globalRefsToSet.delete(commitId);
64
- for (const sign in newRefMap) {
65
- const ref = backgroundSnapshotInstanceManager.getValueBySign(sign);
66
- if (ref) {
67
- // TODO: ref: support __REF_FIRE_IMMEDIATELY__
68
- const v = newRefMap[sign] && lynx.createSelectorQuery().selectUniqueID(newRefMap[sign]);
69
- applyRef(ref, v);
43
+ if (!hasRefUnmount || newRef != null) {
44
+ // Store the cleanup function on the function
45
+ // instance object itself to avoid shape
46
+ // transitioning vnode
47
+ ref._unmount = ref(newRef!);
70
48
  }
71
- }
49
+ } else ref.current = newRef;
50
+ /* v8 ignore start */
51
+ } catch (e) {
52
+ lynx.reportError(e as Error);
72
53
  }
54
+ /* v8 ignore stop */
73
55
  }
74
56
 
75
57
  function updateRef(
76
58
  snapshot: SnapshotInstance,
77
59
  expIndex: number,
78
- oldValue: any,
60
+ oldValue: string | null,
79
61
  elementIndex: number,
80
- spreadKey: string,
81
62
  ): void {
82
- const value = snapshot.__values![expIndex];
63
+ const value: unknown = snapshot.__values![expIndex];
83
64
  let ref;
84
- if (!value) {
85
- ref = undefined;
86
- } else if (typeof value === 'string') {
65
+ if (typeof value === 'string') {
87
66
  ref = value;
88
67
  } else {
89
- ref = `${snapshot.__id}:${expIndex}:${spreadKey}`;
68
+ ref = `react-ref-${snapshot.__id}-${expIndex}`;
90
69
  }
91
70
 
92
71
  snapshot.__values![expIndex] = ref;
93
- if (snapshot.__elements && ref) {
94
- __SetAttribute(snapshot.__elements[elementIndex]!, 'has-react-ref', true);
95
- const uid = __GetElementUniqueID(snapshot.__elements[elementIndex]!);
96
- globalRefPatch[ref] = uid;
97
- snapshot.__ref_set ??= new Set();
98
- snapshot.__ref_set.add(ref);
99
- }
100
- if (oldValue !== ref) {
101
- snapshot.__ref_set?.delete(oldValue);
72
+ if (snapshot.__elements && oldValue !== ref) {
73
+ if (oldValue) {
74
+ __SetAttribute(snapshot.__elements[elementIndex]!, oldValue, undefined);
75
+ }
76
+ if (ref) {
77
+ __SetAttribute(snapshot.__elements[elementIndex]!, ref, 1);
78
+ }
102
79
  }
103
80
  }
104
81
 
105
- function takeGlobalRefPatchMap(): Record<string, number | null> {
106
- const patch = globalRefPatch;
107
- globalRefPatch = {};
108
- return patch;
109
- }
110
-
111
- function transformRef(ref: unknown): Function | (object & Record<'current', unknown>) | null | undefined {
82
+ function transformRef(ref: unknown): Ref | null | undefined {
112
83
  if (ref === undefined || ref === null) {
113
84
  return ref;
114
85
  }
115
86
  if (typeof ref === 'function' || (typeof ref === 'object' && 'current' in ref)) {
116
87
  if ('__ref' in ref) {
117
- return ref;
88
+ return ref as Ref;
118
89
  }
119
- return Object.defineProperty(ref, '__ref', { value: nextRefId++ });
90
+ return Object.defineProperty(ref, '__ref', { value: 1 }) as Ref;
120
91
  }
121
92
  throw new Error(
122
93
  `Elements' "ref" property should be a function, or an object created `
@@ -124,25 +95,44 @@ function transformRef(ref: unknown): Function | (object & Record<'current', unkn
124
95
  );
125
96
  }
126
97
 
127
- function markRefToRemove(sign: string, ref: unknown): void {
128
- if (!ref) {
98
+ function applyQueuedRefs(): void {
99
+ try {
100
+ for (const ref of refsToClear) {
101
+ applyRef(ref, null);
102
+ }
103
+ for (let i = 0; i < refsToApply.length; i += 2) {
104
+ const ref = refsToApply[i] as Ref;
105
+ const value = refsToApply[i + 1] as [snapshotInstanceId: number, expIndex: number] | null;
106
+ applyRef(ref, value);
107
+ }
108
+ } finally {
109
+ clearQueuedRefs();
110
+ }
111
+ }
112
+
113
+ function queueRefAttrUpdate(
114
+ oldRef: Ref | null | undefined,
115
+ newRef: Ref | null | undefined,
116
+ snapshotInstanceId: number,
117
+ expIndex: number,
118
+ ): void {
119
+ if (oldRef === newRef) {
129
120
  return;
130
121
  }
131
- let oldRefs = globalRefsToRemove.get(nextCommitTaskId);
132
- if (!oldRefs) {
133
- oldRefs = new Map();
134
- globalRefsToRemove.set(nextCommitTaskId, oldRefs);
122
+ if (oldRef) {
123
+ refsToClear.push(oldRef);
124
+ }
125
+ if (newRef) {
126
+ refsToApply.push(newRef, [snapshotInstanceId, expIndex]);
135
127
  }
136
- oldRefs.set(sign, ref);
137
128
  }
138
129
 
139
- export {
140
- globalRefsToRemove,
141
- globalRefsToSet,
142
- markRefToRemove,
143
- takeGlobalRefPatchMap,
144
- transformRef,
145
- unref,
146
- updateBackgroundRefs,
147
- updateRef,
148
- };
130
+ function clearQueuedRefs(): void {
131
+ refsToClear.length = 0;
132
+ refsToApply.length = 0;
133
+ }
134
+
135
+ /**
136
+ * @internal
137
+ */
138
+ export { queueRefAttrUpdate, updateRef, unref, transformRef, applyRef, applyQueuedRefs, clearQueuedRefs, type Ref };
@@ -93,7 +93,6 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
93
93
  } else if (key.startsWith('data-')) {
94
94
  // collected below
95
95
  } else if (key === 'ref') {
96
- snapshot.__ref_set ??= new Set();
97
96
  const fakeSnapshot = {
98
97
  __values: {
99
98
  get [index]() {
@@ -106,9 +105,8 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
106
105
  },
107
106
  __id: snapshot.__id,
108
107
  __elements: snapshot.__elements,
109
- __ref_set: snapshot.__ref_set,
110
108
  } as SnapshotInstance;
111
- updateRef(fakeSnapshot, index, oldValue[key], elementIndex, key);
109
+ updateRef(fakeSnapshot, index, oldValue[key], elementIndex);
112
110
  } else if (key.endsWith(':ref')) {
113
111
  snapshot.__worklet_ref_set ??= new Set();
114
112
  const fakeSnapshot = {
@@ -189,7 +187,6 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
189
187
  } else if (key.startsWith('data-')) {
190
188
  // collected below
191
189
  } else if (key === 'ref') {
192
- snapshot.__ref_set ??= new Set();
193
190
  const fakeSnapshot = {
194
191
  __values: {
195
192
  get [index]() {
@@ -202,9 +199,8 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
202
199
  },
203
200
  __id: snapshot.__id,
204
201
  __elements: snapshot.__elements,
205
- __ref_set: snapshot.__ref_set,
206
202
  } as SnapshotInstance;
207
- updateRef(fakeSnapshot, index, oldValue[key], elementIndex, key);
203
+ updateRef(fakeSnapshot, index, oldValue[key], elementIndex);
208
204
  } else if (key.endsWith(':ref')) {
209
205
  snapshot.__worklet_ref_set ??= new Set();
210
206
  const fakeSnapshot = {
@@ -284,8 +280,12 @@ function transformSpread(
284
280
  value ??= '';
285
281
  result['className'] = value;
286
282
  } else if (key === 'ref') {
287
- // @ts-ignore
288
- result[key] = transformRef(value)?.__ref;
283
+ if (__LEPUS__) {
284
+ result[key] = value ? 1 : undefined;
285
+ } else {
286
+ // @ts-ignore
287
+ result[key] = transformRef(value)?.__ref;
288
+ }
289
289
  } else if (typeof value === 'function') {
290
290
  result[key] = `${snapshot.__id}:${index}:${key}`;
291
291
  } else {
@@ -1,12 +1,34 @@
1
1
  // Copyright 2024 The Lynx Authors. All rights reserved.
2
2
  // Licensed under the Apache License Version 2.0 that can be found in the
3
3
  // LICENSE file in the root directory of this source tree.
4
- import { runWorkletCtx, updateWorkletRef as update } from '@lynx-js/react/worklet-runtime/bindings';
4
+
5
+ import { onWorkletCtxUpdate, runWorkletCtx, updateWorkletRef as update } from '@lynx-js/react/worklet-runtime/bindings';
5
6
  import type { Element, Worklet, WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings';
6
7
 
8
+ import { isMainThreadHydrationFinished } from '../lifecycle/patch/isMainThreadHydrationFinished.js';
7
9
  import type { SnapshotInstance } from '../snapshot.js';
8
10
 
9
- function workletUnRef(value: Worklet | WorkletRefImpl<Element>): void {
11
+ let mtRefQueue: (WorkletRefImpl<Element> | Worklet | Element)[] = [];
12
+
13
+ export function applyRefQueue(): void {
14
+ const queue = mtRefQueue;
15
+ mtRefQueue = [];
16
+ for (let i = 0; i < queue.length; i += 2) {
17
+ const worklet = queue[i] as Worklet | WorkletRefImpl<Element>;
18
+ const element = queue[i + 1] as Element;
19
+ if ('_wvid' in worklet) {
20
+ update(worklet as WorkletRefImpl<Element>, element);
21
+ } else if ('_wkltId' in worklet) {
22
+ worklet._unmount = runWorkletCtx(worklet, [{ elementRefptr: element }]) as () => void;
23
+ }
24
+ }
25
+ }
26
+
27
+ function addToRefQueue(worklet: Worklet | WorkletRefImpl<Element>, element: Element): void {
28
+ mtRefQueue.push(worklet, element);
29
+ }
30
+
31
+ export function workletUnRef(value: Worklet | WorkletRefImpl<Element>): void {
10
32
  if ('_wvid' in value) {
11
33
  update(value as WorkletRefImpl<Element>, null);
12
34
  } else if ('_wkltId' in value) {
@@ -18,7 +40,7 @@ function workletUnRef(value: Worklet | WorkletRefImpl<Element>): void {
18
40
  }
19
41
  }
20
42
 
21
- function updateWorkletRef(
43
+ export function updateWorkletRef(
22
44
  snapshot: SnapshotInstance,
23
45
  expIndex: number,
24
46
  oldValue: WorkletRefImpl<Element> | Worklet | undefined,
@@ -38,11 +60,17 @@ function updateWorkletRef(
38
60
  if (value === null || value === undefined) {
39
61
  // do nothing
40
62
  } else if (value._wvid) {
41
- update(value as WorkletRefImpl<Element>, snapshot.__elements[elementIndex]!);
63
+ const element = snapshot.__elements[elementIndex]! as Element;
64
+ addToRefQueue(value as Worklet, element);
42
65
  } else if ((value as Worklet)._wkltId) {
43
- (value as Worklet)._unmount = runWorkletCtx(value as Worklet, [{
44
- elementRefptr: (snapshot.__elements[elementIndex]!) as any,
45
- }]) as () => void;
66
+ const element = snapshot.__elements[elementIndex]! as Element;
67
+ onWorkletCtxUpdate(
68
+ value as Worklet,
69
+ oldValue as Worklet | undefined,
70
+ !isMainThreadHydrationFinished,
71
+ element,
72
+ );
73
+ addToRefQueue(value as Worklet, element);
46
74
  /* v8 ignore next 3 */
47
75
  } else if (value._type === '__LEPUS__' || (value as Worklet)._lepusWorkletHash) {
48
76
  // for pre-0.99 compatibility
@@ -58,5 +86,3 @@ function updateWorkletRef(
58
86
  // Add an arbitrary attribute to avoid this element being layout-only
59
87
  __SetAttribute(snapshot.__elements[elementIndex]!, 'has-react-ref', true);
60
88
  }
61
-
62
- export { updateWorkletRef, workletUnRef };
@@ -39,7 +39,7 @@ export const enum DynamicPartType {
39
39
  * A snapshot definition that contains all the information needed to create and update elements
40
40
  * This is generated at compile time through static analysis of the JSX
41
41
  */
42
- interface Snapshot {
42
+ export interface Snapshot {
43
43
  create: null | ((ctx: SnapshotInstance) => FiberElement[]);
44
44
  update: null | ((ctx: SnapshotInstance, index: number, oldValue: any) => void)[];
45
45
  slot: [DynamicPartType, number][];
@@ -47,6 +47,7 @@ interface Snapshot {
47
47
  isListHolder?: boolean;
48
48
  cssId?: number | undefined;
49
49
  entryName?: string | undefined;
50
+ refAndSpreadIndexes?: number[] | null;
50
51
  }
51
52
 
52
53
  export let __page: FiberElement;
@@ -166,7 +167,7 @@ export const backgroundSnapshotInstanceManager: {
166
167
  if (!res || (res.length != 2 && res.length != 3)) {
167
168
  throw new Error('Invalid ctx format: ' + str);
168
169
  }
169
- let id = Number(res[0]);
170
+ const id = Number(res[0]);
170
171
  const expIndex = Number(res[1]);
171
172
  const ctx = this.values.get(id);
172
173
  if (!ctx) {
@@ -190,8 +191,9 @@ export function createSnapshot(
190
191
  create: Snapshot['create'] | null,
191
192
  update: Snapshot['update'] | null,
192
193
  slot: Snapshot['slot'],
193
- cssId?: number,
194
- entryName?: string,
194
+ cssId: number | undefined,
195
+ entryName: string | undefined,
196
+ refAndSpreadIndexes: number[] | null,
195
197
  ): string {
196
198
  if (
197
199
  __DEV__ && __JS__
@@ -221,7 +223,7 @@ export function createSnapshot(
221
223
 
222
224
  uniqID = entryUniqID(uniqID, entryName);
223
225
 
224
- const s: Snapshot = { create, update, slot, cssId, entryName };
226
+ const s: Snapshot = { create, update, slot, cssId, entryName, refAndSpreadIndexes };
225
227
  snapshotManager.values.set(uniqID, s);
226
228
  if (slot && slot[0] && slot[0][0] === DynamicPartType.ListChildren) {
227
229
  s.isListHolder = true;
@@ -268,12 +270,15 @@ export class SnapshotInstance {
268
270
  __element_root?: FiberElement | undefined;
269
271
  __values?: any[] | undefined;
270
272
  __current_slot_index = 0;
271
- __ref_set?: Set<string>;
272
273
  __worklet_ref_set?: Set<WorkletRefImpl<any> | Worklet>;
273
274
  __listItemPlatformInfo?: any;
274
275
 
275
276
  constructor(public type: string, id?: number) {
276
277
  this.__snapshot_def = snapshotManager.values.get(type)!;
278
+ // Suspense uses 'div'
279
+ if (!this.__snapshot_def && type !== 'div') {
280
+ throw new Error('Snapshot not found: ' + type);
281
+ }
277
282
 
278
283
  id ||= snapshotInstanceManager.nextId -= 1;
279
284
  this.__id = id;
@@ -291,15 +296,15 @@ export class SnapshotInstance {
291
296
  // CSS Scope is removed(We only need to call `__SetCSSId` when there is `entryName`)
292
297
  // Or an old bundle(`__SetCSSId` is called in `create`), we skip calling `__SetCSSId`
293
298
  if (entryName !== DEFAULT_ENTRY_NAME && entryName !== undefined) {
294
- __SetCSSId(this.__elements!, DEFAULT_CSS_ID, entryName);
299
+ __SetCSSId(this.__elements, DEFAULT_CSS_ID, entryName);
295
300
  }
296
301
  } else {
297
302
  // cssId !== undefined
298
303
  if (entryName !== DEFAULT_ENTRY_NAME && entryName !== undefined) {
299
304
  // For lazy bundle, we need add `entryName` to the third params
300
- __SetCSSId(this.__elements!, cssId, entryName);
305
+ __SetCSSId(this.__elements, cssId, entryName);
301
306
  } else {
302
- __SetCSSId(this.__elements!, cssId);
307
+ __SetCSSId(this.__elements, cssId);
303
308
  }
304
309
  }
305
310
 
@@ -388,6 +393,14 @@ export class SnapshotInstance {
388
393
  return a;
389
394
  }
390
395
 
396
+ tearDown(): void {
397
+ traverseSnapshotInstance(this, v => {
398
+ v.__parent = null;
399
+ v.__previousSibling = null;
400
+ v.__nextSibling = null;
401
+ });
402
+ }
403
+
391
404
  // onCreate?: () => void;
392
405
  // onAttach?: () => void;
393
406
  // onDetach?: () => void;
@@ -539,26 +552,22 @@ export class SnapshotInstance {
539
552
  }
540
553
 
541
554
  removeChild(child: SnapshotInstance): void {
542
- const r = () => {
543
- this.__removeChild(child);
544
- traverseSnapshotInstance(child, v => {
545
- v.__parent = null;
546
- snapshotInstanceManager.values.delete(v.__id);
547
- });
548
- };
549
-
550
555
  const __snapshot_def = this.__snapshot_def;
551
556
  if (__snapshot_def.isListHolder) {
552
557
  (__pendingListUpdates.values[this.__id] ??= new ListUpdateInfoRecording(
553
558
  this,
554
559
  )).onRemoveChild(child);
555
- r();
560
+
561
+ this.__removeChild(child);
562
+ traverseSnapshotInstance(child, v => {
563
+ snapshotInstanceManager.values.delete(v.__id);
564
+ });
565
+ // mark this child as deleted
566
+ child.__id = 0;
556
567
  return;
557
568
  }
558
569
 
559
- // TODO: ref: can this be done on the background thread?
560
570
  unref(child, true);
561
- r();
562
571
  if (this.__elements) {
563
572
  const [, elementIndex] = __snapshot_def.slot[0]!;
564
573
  __RemoveElement(this.__elements[elementIndex]!, child.__element_root!);
@@ -567,6 +576,16 @@ export class SnapshotInstance {
567
576
  if (child.__snapshot_def.isListHolder) {
568
577
  snapshotDestroyList(child);
569
578
  }
579
+
580
+ this.__removeChild(child);
581
+ traverseSnapshotInstance(child, v => {
582
+ v.__parent = null;
583
+ v.__previousSibling = null;
584
+ v.__nextSibling = null;
585
+ delete v.__elements;
586
+ delete v.__element_root;
587
+ snapshotInstanceManager.values.delete(v.__id);
588
+ });
570
589
  }
571
590
 
572
591
  setAttribute(key: string | number, value: any): void {
@@ -0,0 +1,17 @@
1
+ // Copyright 2024 The Lynx Authors. All rights reserved.
2
+ // Licensed under the Apache License Version 2.0 that can be found in the
3
+ // LICENSE file in the root directory of this source tree.
4
+
5
+ /**
6
+ * A map to store hydration states between snapshot instances.
7
+ * K->V: main thread snapshotInstance IDs -> background snapshotInstance IDs.
8
+ *
9
+ * The map is used by the ref system to translate between snapshot instance IDs when
10
+ * operations need to cross the thread boundary during the commit phase.
11
+ */
12
+ const hydrationMap: Map<number, number> = new Map();
13
+
14
+ /**
15
+ * @internal
16
+ */
17
+ export { hydrationMap };
@@ -54,10 +54,9 @@ abstract class WorkletRef<T> {
54
54
  throw new Error('MainThreadRef: value of a MainThreadRef cannot be accessed in the background thread.');
55
55
  }
56
56
  if (__LEPUS__ && __DEV__) {
57
- /* v8 ignore next */
57
+ /* v8 ignore next 3 */
58
58
  throw new Error('MainThreadRef: value of a MainThreadRef cannot be accessed outside of main thread script.');
59
59
  }
60
- /* v8 ignore next */
61
60
  return undefined as T;
62
61
  }
63
62
 
@@ -149,7 +149,7 @@ const initElementTree = ()=>{
149
149
  e.id = id;
150
150
  }
151
151
  __SetInlineStyles(e, styles) {
152
- if ('string' == typeof styles) e.style.cssText = styles;
152
+ if ('string' == typeof styles) e.setAttributeNS(null, 'style', styles);
153
153
  else Object.assign(e.style, styles);
154
154
  }
155
155
  __AddDataset(e, key, value) {
@@ -357,7 +357,7 @@ function createPolyfills() {
357
357
  function injectMainThreadGlobals(target, polyfills) {
358
358
  var _globalThis_onInjectMainThreadGlobals, _globalThis;
359
359
  __injectElementApi(target);
360
- const { performance, JsContext, __LoadLepusChunk } = polyfills || {};
360
+ const { performance, CoreContext, JsContext, __LoadLepusChunk } = polyfills || {};
361
361
  if (void 0 === target) target = globalThis;
362
362
  target.__DEV__ = true;
363
363
  target.__PROFILE__ = true;
@@ -372,6 +372,7 @@ function injectMainThreadGlobals(target, polyfills) {
372
372
  target.globDynamicComponentEntry = '__Card__';
373
373
  target.lynx = {
374
374
  performance,
375
+ getCoreContext: ()=>CoreContext,
375
376
  getJSContext: ()=>JsContext,
376
377
  reportError: (e)=>{
377
378
  throw e;
@@ -410,7 +411,7 @@ class NodesRef {
410
411
  }
411
412
  function injectBackgroundThreadGlobals(target, polyfills) {
412
413
  var _globalThis_onInjectBackgroundThreadGlobals, _globalThis;
413
- const { app, performance, CoreContext, __LoadLepusChunk } = polyfills || {};
414
+ const { app, performance, CoreContext, JsContext, __LoadLepusChunk } = polyfills || {};
414
415
  if (void 0 === target) target = globalThis;
415
416
  target.__DEV__ = true;
416
417
  target.__PROFILE__ = true;
@@ -442,6 +443,7 @@ function injectBackgroundThreadGlobals(target, polyfills) {
442
443
  }
443
444
  }),
444
445
  getCoreContext: ()=>CoreContext,
446
+ getJSContext: ()=>JsContext,
445
447
  getJSModule: (moduleName)=>{
446
448
  if ('GlobalEventEmitter' === moduleName) return globalEventEmitter;
447
449
  throw new Error(`getJSModule(${moduleName}) not implemented`);
@@ -14413,9 +14413,10 @@ function __webpack_require__(moduleId) {
14413
14413
  };
14414
14414
  })();
14415
14415
  var dom_esm = __webpack_require__("../../../node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@testing-library/dom/dist/@testing-library/dom.esm.js");
14416
- let NodesRef = lynx.createSelectorQuery().selectUniqueID(-1).constructor;
14416
+ const NodesRef = lynx.createSelectorQuery().selectUniqueID(-1).constructor;
14417
14417
  function getElement(elemOrNodesRef) {
14418
14418
  if (elemOrNodesRef instanceof NodesRef) return __GetElementByUniqueId(Number(elemOrNodesRef._nodeSelectToken.identifier));
14419
+ if ('refAttr' in elemOrNodesRef) return document.querySelector(`[react-ref-${elemOrNodesRef.refAttr[0]}-${elemOrNodesRef.refAttr[1]}]`);
14419
14420
  if (elemOrNodesRef?.constructor?.name === 'HTMLUnknownElement') return elemOrNodesRef;
14420
14421
  throw new Error('Invalid element, got: ' + elemOrNodesRef.constructor?.name);
14421
14422
  }
@@ -14423,7 +14424,7 @@ const fireEvent = (elemOrNodesRef, ...args)=>{
14423
14424
  const isMainThread = __MAIN_THREAD__;
14424
14425
  lynxTestingEnv.switchToBackgroundThread();
14425
14426
  const elem = getElement(elemOrNodesRef);
14426
- let ans = (0, dom_esm.BX)(elem, ...args);
14427
+ const ans = (0, dom_esm.BX)(elem, ...args);
14427
14428
  if (isMainThread) lynxTestingEnv.switchToMainThread();
14428
14429
  return ans;
14429
14430
  };
@@ -14542,7 +14543,7 @@ Object.keys(eventMap).forEach((key)=>{
14542
14543
  const isMainThread = __MAIN_THREAD__;
14543
14544
  lynxTestingEnv.switchToBackgroundThread();
14544
14545
  const elem = getElement(elemOrNodesRef);
14545
- const eventType = init?.['eventType'] || 'bindEvent';
14546
+ const eventType = init?.eventType || 'bindEvent';
14546
14547
  init = {
14547
14548
  eventType,
14548
14549
  eventName: key,