@lynx-js/react 0.107.1 → 0.108.1

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 (135) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +4 -2
  3. package/package.json +4 -4
  4. package/refresh/.turbo/turbo-build.log +1 -1
  5. package/refresh/package.json +1 -1
  6. package/runtime/lazy/import.js +7 -1
  7. package/runtime/lepus/index.js +1 -1
  8. package/runtime/lepus/jsx-dev-runtime/index.js +1 -1
  9. package/runtime/lib/backgroundSnapshot.d.ts +1 -1
  10. package/runtime/lib/backgroundSnapshot.js +7 -4
  11. package/runtime/lib/backgroundSnapshot.js.map +1 -1
  12. package/runtime/lib/document.d.ts +33 -2
  13. package/runtime/lib/document.js +9 -1
  14. package/runtime/lib/document.js.map +1 -1
  15. package/runtime/lib/hydrate.js +2 -1
  16. package/runtime/lib/hydrate.js.map +1 -1
  17. package/runtime/lib/lifecycle/delayUnmount.d.ts +1 -1
  18. package/runtime/lib/lifecycle/delayUnmount.js +1 -4
  19. package/runtime/lib/lifecycle/delayUnmount.js.map +1 -1
  20. package/runtime/lib/lifecycle/destroy.js +2 -2
  21. package/runtime/lib/lifecycle/destroy.js.map +1 -1
  22. package/runtime/lib/lifecycle/patch/commit.d.ts +21 -3
  23. package/runtime/lib/lifecycle/patch/commit.js +44 -56
  24. package/runtime/lib/lifecycle/patch/commit.js.map +1 -1
  25. package/runtime/lib/lifecycle/patch/isMainThreadHydrationFinished.d.ts +2 -0
  26. package/runtime/lib/lifecycle/patch/isMainThreadHydrationFinished.js +9 -0
  27. package/runtime/lib/lifecycle/patch/isMainThreadHydrationFinished.js.map +1 -0
  28. package/runtime/lib/lifecycle/patch/snapshotPatch.d.ts +5 -0
  29. package/runtime/lib/lifecycle/patch/snapshotPatch.js +5 -0
  30. package/runtime/lib/lifecycle/patch/snapshotPatch.js.map +1 -1
  31. package/runtime/lib/lifecycle/patch/snapshotPatchApply.d.ts +5 -0
  32. package/runtime/lib/lifecycle/patch/snapshotPatchApply.js +21 -5
  33. package/runtime/lib/lifecycle/patch/snapshotPatchApply.js.map +1 -1
  34. package/runtime/lib/lifecycle/patch/updateMainThread.js +4 -3
  35. package/runtime/lib/lifecycle/patch/updateMainThread.js.map +1 -1
  36. package/runtime/lib/lifecycle/reload.js +11 -4
  37. package/runtime/lib/lifecycle/reload.js.map +1 -1
  38. package/runtime/lib/lifecycle/render.d.ts +1 -3
  39. package/runtime/lib/lifecycle/render.js +4 -6
  40. package/runtime/lib/lifecycle/render.js.map +1 -1
  41. package/runtime/lib/list.d.ts +1 -1
  42. package/runtime/lib/list.js +60 -23
  43. package/runtime/lib/list.js.map +1 -1
  44. package/runtime/lib/lynx/env.d.ts +1 -1
  45. package/runtime/lib/lynx/env.js +1 -1
  46. package/runtime/lib/lynx/env.js.map +1 -1
  47. package/runtime/lib/lynx/runWithForce.js +12 -4
  48. package/runtime/lib/lynx/runWithForce.js.map +1 -1
  49. package/runtime/lib/lynx/tt.js +6 -8
  50. package/runtime/lib/lynx/tt.js.map +1 -1
  51. package/runtime/lib/lynx-api.d.ts +4 -4
  52. package/runtime/lib/lynx-api.js +5 -5
  53. package/runtime/lib/lynx-api.js.map +1 -1
  54. package/runtime/lib/lynx.js +6 -5
  55. package/runtime/lib/lynx.js.map +1 -1
  56. package/runtime/lib/opcodes.js +2 -2
  57. package/runtime/lib/opcodes.js.map +1 -1
  58. package/runtime/lib/renderToOpcodes/index.js +5 -1
  59. package/runtime/lib/renderToOpcodes/index.js.map +1 -1
  60. package/runtime/lib/root.d.ts +4 -0
  61. package/runtime/lib/root.js +6 -2
  62. package/runtime/lib/root.js.map +1 -1
  63. package/runtime/lib/snapshot/ref.d.ts +1 -1
  64. package/runtime/lib/snapshot/ref.js +1 -1
  65. package/runtime/lib/snapshot/ref.js.map +1 -1
  66. package/runtime/lib/snapshot/spread.d.ts +2 -2
  67. package/runtime/lib/snapshot/spread.js +9 -9
  68. package/runtime/lib/snapshot/spread.js.map +1 -1
  69. package/runtime/lib/snapshot/workletEvent.d.ts +2 -1
  70. package/runtime/lib/snapshot/workletEvent.js +1 -1
  71. package/runtime/lib/snapshot/workletEvent.js.map +1 -1
  72. package/runtime/lib/snapshot/workletRef.d.ts +4 -4
  73. package/runtime/lib/snapshot/workletRef.js +8 -6
  74. package/runtime/lib/snapshot/workletRef.js.map +1 -1
  75. package/runtime/lib/snapshot.d.ts +31 -5
  76. package/runtime/lib/snapshot.js +15 -3
  77. package/runtime/lib/snapshot.js.map +1 -1
  78. package/runtime/lib/worklet/workletRef.d.ts +1 -0
  79. package/runtime/lib/worklet/workletRef.js +14 -12
  80. package/runtime/lib/worklet/workletRef.js.map +1 -1
  81. package/runtime/src/backgroundSnapshot.ts +20 -13
  82. package/runtime/src/document.ts +33 -2
  83. package/runtime/src/hydrate.ts +3 -1
  84. package/runtime/src/lifecycle/delayUnmount.ts +2 -2
  85. package/runtime/src/lifecycle/destroy.ts +3 -2
  86. package/runtime/src/lifecycle/patch/commit.ts +75 -72
  87. package/runtime/src/lifecycle/patch/isMainThreadHydrationFinished.ts +10 -0
  88. package/runtime/src/lifecycle/patch/snapshotPatch.ts +9 -0
  89. package/runtime/src/lifecycle/patch/snapshotPatchApply.ts +20 -5
  90. package/runtime/src/lifecycle/patch/updateMainThread.ts +4 -3
  91. package/runtime/src/lifecycle/reload.ts +14 -4
  92. package/runtime/src/lifecycle/render.ts +6 -8
  93. package/runtime/src/list.ts +71 -23
  94. package/runtime/src/lynx/env.ts +1 -1
  95. package/runtime/src/lynx/runWithForce.ts +17 -6
  96. package/runtime/src/lynx/tt.ts +10 -17
  97. package/runtime/src/lynx-api.ts +7 -7
  98. package/runtime/src/lynx.ts +7 -6
  99. package/runtime/src/opcodes.ts +2 -2
  100. package/runtime/src/renderToOpcodes/index.ts +6 -1
  101. package/runtime/src/root.ts +6 -2
  102. package/runtime/src/snapshot/ref.ts +7 -7
  103. package/runtime/src/snapshot/spread.ts +28 -10
  104. package/runtime/src/snapshot/workletEvent.ts +3 -2
  105. package/runtime/src/snapshot/workletRef.ts +18 -19
  106. package/runtime/src/snapshot.ts +35 -10
  107. package/runtime/src/worklet/workletRef.ts +15 -12
  108. package/testing-library/README.md +1 -1
  109. package/testing-library/dist/env/vitest.js +23 -23
  110. package/testing-library/dist/index.d.ts +24 -24
  111. package/testing-library/dist/index.js +2 -2
  112. package/testing-library/dist/pure.js +13 -15
  113. package/testing-library/dist/vitest-global-setup.js +13 -14
  114. package/testing-library/types/entry.d.ts +2 -2
  115. package/testing-library/types/index.d.ts +3 -3
  116. package/transform/dist/wasm.cjs +1 -1
  117. package/worklet-runtime/dist/dev.js +25 -13
  118. package/worklet-runtime/dist/dev.js.map +3 -3
  119. package/worklet-runtime/dist/main.js +25 -13
  120. package/worklet-runtime/dist/main.js.map +3 -3
  121. package/worklet-runtime/lib/bindings/bindings.d.ts +1 -1
  122. package/worklet-runtime/lib/bindings/bindings.js +2 -2
  123. package/worklet-runtime/lib/bindings/bindings.js.map +1 -1
  124. package/worklet-runtime/lib/bindings/index.d.ts +1 -1
  125. package/worklet-runtime/lib/bindings/index.js +1 -1
  126. package/worklet-runtime/lib/bindings/index.js.map +1 -1
  127. package/worklet-runtime/lib/bindings/types.d.ts +7 -3
  128. package/worklet-runtime/lib/bindings/types.js +3 -0
  129. package/worklet-runtime/lib/bindings/types.js.map +1 -1
  130. package/worklet-runtime/lib/ctxTrace.d.ts +8 -0
  131. package/worklet-runtime/lib/ctxTrace.js +13 -0
  132. package/worklet-runtime/lib/ctxTrace.js.map +1 -0
  133. package/worklet-runtime/lib/global.d.ts +3 -3
  134. package/worklet-runtime/lib/workletRuntime.js +11 -8
  135. package/worklet-runtime/lib/workletRuntime.js.map +1 -1
@@ -1,34 +1,30 @@
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 { render } from 'preact';
5
+
4
6
  import { LifecycleConstant, NativeUpdateDataType } from '../lifecycleConstant.js';
5
7
  import {
6
- PerformanceTimingKeys,
7
8
  PerformanceTimingFlags,
9
+ PerformanceTimingKeys,
8
10
  PipelineOrigins,
9
11
  beginPipeline,
10
12
  markTiming,
11
13
  } from './performance.js';
12
14
  import { BackgroundSnapshotInstance, hydrate } from '../backgroundSnapshot.js';
15
+ import { runWithForce } from './runWithForce.js';
13
16
  import { destroyBackground } from '../lifecycle/destroy.js';
14
17
  import { delayedEvents, delayedPublishEvent } from '../lifecycle/event/delayEvents.js';
15
18
  import { delayLifecycleEvent, delayedLifecycleEvents } from '../lifecycle/event/delayLifecycleEvents.js';
16
- import {
17
- clearPatchesToCommit,
18
- commitPatchUpdate,
19
- genCommitTaskId,
20
- globalCommitTaskMap,
21
- patchesToCommit,
22
- type PatchList,
23
- } from '../lifecycle/patch/commit.js';
19
+ import { commitPatchUpdate, genCommitTaskId, globalCommitTaskMap } from '../lifecycle/patch/commit.js';
20
+ import type { PatchList } from '../lifecycle/patch/commit.js';
24
21
  import { reloadBackground } from '../lifecycle/reload.js';
25
- import { renderBackground } from '../lifecycle/render.js';
26
22
  import { CHILDREN } from '../renderToOpcodes/constants.js';
27
23
  import { __root } from '../root.js';
28
24
  import { globalRefsToSet, updateBackgroundRefs } from '../snapshot/ref.js';
29
25
  import { backgroundSnapshotInstanceManager } from '../snapshot.js';
30
26
  import { destroyWorklet } from '../worklet/destroy.js';
31
- import { runWithForce } from './runWithForce.js';
27
+
32
28
  export { runWithForce };
33
29
 
34
30
  function injectTt(): void {
@@ -129,14 +125,11 @@ function onLifecycleEventImpl(type: string, data: any): void {
129
125
  console.profile('commitChanges');
130
126
  }
131
127
  const commitTaskId = genCommitTaskId();
132
- patchesToCommit.push(
133
- { snapshotPatch, id: commitTaskId },
134
- );
135
128
  const patchList: PatchList = {
136
- patchList: patchesToCommit,
129
+ patchList: [{ snapshotPatch, id: commitTaskId }],
137
130
  };
138
- clearPatchesToCommit();
139
131
  const obj = commitPatchUpdate(patchList, { isHydration: true });
132
+
140
133
  lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => {
141
134
  updateBackgroundRefs(commitTaskId);
142
135
  globalCommitTaskMap.forEach((commitTask, id) => {
@@ -212,7 +205,7 @@ function updateGlobalProps(newData: Record<string, any>): void {
212
205
  // This is already done because updateFromRoot will consume all dirty flags marked by
213
206
  // the setState, and setState's flush will be a noop. No extra diffs will be needed.
214
207
  Promise.resolve().then(() => {
215
- runWithForce(() => renderBackground(__root.__jsx, __root as any));
208
+ runWithForce(() => render(__root.__jsx, __root as any));
216
209
  });
217
210
  lynxCoreInject.tt.GlobalEventEmitter.emit('onGlobalPropsChanged');
218
211
  }
@@ -1,13 +1,13 @@
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 { render } from 'preact';
4
5
  import { createContext, createElement } from 'preact/compat';
5
6
  import { useState } from 'preact/hooks';
6
7
  import type { Consumer, FC, ReactNode } from 'react';
7
8
 
8
9
  import { factory, withInitDataInState } from './compat/initData.js';
9
10
  import { useLynxGlobalEventListener } from './hooks/useLynxGlobalEventListener.js';
10
- import { renderBackground } from './lifecycle/render.js';
11
11
  import { LifecycleConstant } from './lifecycleConstant.js';
12
12
  import { flushDelayedLifecycleEvents } from './lynx/tt.js';
13
13
  import { __root } from './root.js';
@@ -43,13 +43,13 @@ export interface Root {
43
43
  * return <view>...</view>
44
44
  * }
45
45
  *
46
- * if (__LEPUS__) {
46
+ * if (__MAIN_THREAD__) {
47
47
  * root.render(
48
48
  * <DataProvider data={DEFAULT_DATA}>
49
49
  * <App/>
50
50
  * </DataProvider>
51
51
  * );
52
- * } else if (__JS__) {
52
+ * } else if (__BACKGROUND__) {
53
53
  * fetchData().then((data) => {
54
54
  * root.render(
55
55
  * <DataProvider data={data}>
@@ -82,11 +82,11 @@ export interface Root {
82
82
  */
83
83
  export const root: Root = {
84
84
  render: (jsx: ReactNode): void => {
85
- if (__LEPUS__) {
85
+ if (__MAIN_THREAD__) {
86
86
  __root.__jsx = jsx;
87
87
  } else {
88
88
  __root.__jsx = jsx;
89
- renderBackground(jsx, __root as any);
89
+ render(jsx, __root as any);
90
90
  if (__FIRST_SCREEN_SYNC_TIMING__ === 'immediately') {
91
91
  // This is for cases where `root.render()` is called asynchronously,
92
92
  // `firstScreen` message might have been reached.
@@ -370,7 +370,7 @@ export interface Lynx {
370
370
  registerDataProcessors: (dataProcessorDefinition?: DataProcessorDefinition) => void;
371
371
  }
372
372
 
373
- export { runOnMainThread } from './worklet/runOnMainThread.js';
373
+ export { useLynxGlobalEventListener } from './hooks/useLynxGlobalEventListener.js';
374
374
  export { runOnBackground } from './worklet/runOnBackground.js';
375
+ export { runOnMainThread } from './worklet/runOnMainThread.js';
375
376
  export { MainThreadRef, useMainThreadRef } from './worklet/workletRef.js';
376
- export { useLynxGlobalEventListener } from './hooks/useLynxGlobalEventListener.js';
@@ -11,21 +11,21 @@ import { initDelayUnmount } from './lifecycle/delayUnmount.js';
11
11
  import { replaceCommitHook, replaceRequestAnimationFrame } from './lifecycle/patch/commit.js';
12
12
  import { injectUpdateMainThread } from './lifecycle/patch/updateMainThread.js';
13
13
  import { injectCalledByNative } from './lynx/calledByNative.js';
14
- import { setupLynxEnv } from './lynx/env.js';
14
+ import { setupLynxTestingEnv } from './lynx/env.js';
15
15
  import { injectLepusMethods } from './lynx/injectLepusMethods.js';
16
16
  import { initTimingAPI } from './lynx/performance.js';
17
17
  import { injectTt } from './lynx/tt.js';
18
18
  export { runWithForce } from './lynx/runWithForce.js';
19
19
 
20
20
  // @ts-expect-error Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature
21
- if (__LEPUS__ && typeof globalThis.processEvalResult === 'undefined') {
21
+ if (__MAIN_THREAD__ && typeof globalThis.processEvalResult === 'undefined') {
22
22
  // @ts-expect-error Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature
23
23
  globalThis.processEvalResult = <T>(result: ((schema: string) => T) | undefined, schema: string) => {
24
24
  return result?.(schema);
25
25
  };
26
26
  }
27
27
 
28
- if (__LEPUS__) {
28
+ if (__MAIN_THREAD__) {
29
29
  injectCalledByNative();
30
30
  injectUpdateMainThread();
31
31
  if (__DEV__) {
@@ -39,8 +39,9 @@ if (__PROFILE__) {
39
39
  initProfileHook();
40
40
  }
41
41
 
42
- if (__JS__) {
43
- options.document = document;
42
+ if (__BACKGROUND__) {
43
+ // Trick Preact and TypeScript to accept our custom document adapter.
44
+ options.document = document as any;
44
45
  setupBackgroundDocument();
45
46
  injectTt();
46
47
 
@@ -53,4 +54,4 @@ if (__JS__) {
53
54
  }
54
55
  }
55
56
 
56
- setupLynxEnv();
57
+ setupLynxTestingEnv();
@@ -52,7 +52,7 @@ export function ssrHydrateByOpcodes(
52
52
  const signMap = gSignMap[listElementUniqueID] = new Map();
53
53
  gRecycleMap[listElementUniqueID] = new Map();
54
54
  const enqueueFunc = enqueueComponentFactory();
55
- const componentAtIndex = componentAtIndexFactory(top.childNodes);
55
+ const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory(top.childNodes);
56
56
  for (const child of top.childNodes) {
57
57
  if (child.__element_root) {
58
58
  const childElementUniqueID = __GetElementUniqueID(child.__element_root);
@@ -64,7 +64,7 @@ export function ssrHydrateByOpcodes(
64
64
  );
65
65
  }
66
66
  }
67
- __UpdateListCallbacks(listElement, componentAtIndex, enqueueFunc);
67
+ __UpdateListCallbacks(listElement, componentAtIndex, enqueueFunc, componentAtIndexes);
68
68
  }
69
69
 
70
70
  stack.pop();
@@ -2,7 +2,12 @@
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
4
 
5
- // modified from preact-render-to-string@6.0.3
5
+ /**
6
+ * Implements rendering to opcodes.
7
+ * This module is modified from preact-render-to-string@6.0.3 to generate
8
+ * opcodes instead of HTML strings for Lynx.
9
+ */
10
+
6
11
  // @ts-nocheck
7
12
 
8
13
  import { Fragment, h, options } from 'preact';
@@ -4,15 +4,19 @@
4
4
  import { BackgroundSnapshotInstance } from './backgroundSnapshot.js';
5
5
  import { SnapshotInstance } from './snapshot.js';
6
6
 
7
+ /**
8
+ * The internal ReactLynx's root.
9
+ * {@link @lynx-js/react!Root | root}.
10
+ */
7
11
  let __root: (SnapshotInstance | BackgroundSnapshotInstance) & { __jsx?: React.ReactNode; __opcodes?: any[] };
8
12
 
9
13
  function setRoot(root: typeof __root): void {
10
14
  __root = root;
11
15
  }
12
16
 
13
- if (__LEPUS__) {
17
+ if (__MAIN_THREAD__) {
14
18
  setRoot(new SnapshotInstance('root'));
15
- } else if (__JS__) {
19
+ } else if (__BACKGROUND__) {
16
20
  setRoot(new BackgroundSnapshotInstance('root'));
17
21
  }
18
22
 
@@ -1,7 +1,7 @@
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 type { Worklet, WorkletRef } from '@lynx-js/react/worklet-runtime/bindings';
4
+ import type { Element, Worklet, WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings';
5
5
 
6
6
  import { nextCommitTaskId } from '../lifecycle/patch/commit.js';
7
7
  import { SnapshotInstance, backgroundSnapshotInstanceManager } from '../snapshot.js';
@@ -20,7 +20,7 @@ function unref(snapshot: SnapshotInstance, recursive: boolean): void {
20
20
 
21
21
  snapshot.__worklet_ref_set?.forEach(v => {
22
22
  if (v) {
23
- workletUnRef(v as Worklet | WorkletRef<unknown>);
23
+ workletUnRef(v as Worklet | WorkletRefImpl<Element>);
24
24
  }
25
25
  });
26
26
  snapshot.__worklet_ref_set?.clear();
@@ -137,12 +137,12 @@ function markRefToRemove(sign: string, ref: unknown): void {
137
137
  }
138
138
 
139
139
  export {
140
- updateRef,
141
- takeGlobalRefPatchMap,
142
- updateBackgroundRefs,
143
- unref,
144
- transformRef,
145
140
  globalRefsToRemove,
146
141
  globalRefsToSet,
147
142
  markRefToRemove,
143
+ takeGlobalRefPatchMap,
144
+ transformRef,
145
+ unref,
146
+ updateBackgroundRefs,
147
+ updateRef,
148
148
  };
@@ -1,16 +1,26 @@
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
+
5
+ /**
6
+ * Handles JSX spread operator in the snapshot system.
7
+ *
8
+ * Spread operators in JSX (e.g., <div {...props}>) are transformed into
9
+ * optimized attribute updates at compile time, avoiding runtime object spreads.
10
+ */
11
+
12
+ import type { Worklet } from '@lynx-js/react/worklet-runtime/bindings';
13
+
14
+ import { BackgroundSnapshotInstance } from '../backgroundSnapshot.js';
15
+ import { ListUpdateInfoRecording, __pendingListUpdates } from '../list.js';
4
16
  import { SnapshotInstance } from '../snapshot.js';
17
+ import { isDirectOrDeepEqual, isEmptyObject, pick } from '../utils.js';
5
18
  import { updateEvent } from './event.js';
6
- import { BackgroundSnapshotInstance } from '../backgroundSnapshot.js';
19
+ import { updateGesture } from './gesture.js';
20
+ import { platformInfoAttributes, updateListItemPlatformInfo } from './platformInfo.js';
7
21
  import { transformRef, updateRef } from './ref.js';
8
22
  import { updateWorkletEvent } from './workletEvent.js';
9
23
  import { updateWorkletRef } from './workletRef.js';
10
- import { updateGesture } from './gesture.js';
11
- import { platformInfoAttributes, updateListItemPlatformInfo } from './platformInfo.js';
12
- import { isDirectOrDeepEqual, isEmptyObject, pick } from '../utils.js';
13
- import { __pendingListUpdates, ListUpdateInfoRecording } from '../list.js';
14
24
 
15
25
  const eventRegExp = /^(([A-Za-z-]*):)?(bind|catch|capture-bind|capture-catch|global-bind)([A-Za-z]+)$/;
16
26
  const eventTypeMap: Record<string, string> = {
@@ -29,7 +39,7 @@ const noFlattenAttributes = /* @__PURE__ */ new Set<string>([
29
39
  ]);
30
40
 
31
41
  function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any, elementIndex: number): void {
32
- oldValue ||= {};
42
+ oldValue ??= {};
33
43
  let newValue: Record<string, any> = snapshot.__values![index]; // compiler guarantee this must be an object;
34
44
 
35
45
  // @ts-ignore
@@ -124,7 +134,7 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
124
134
  __elements: snapshot.__elements,
125
135
  } as SnapshotInstance;
126
136
  updateGesture(fakeSnapshot, index, oldValue[key], elementIndex, workletType);
127
- } else if ((match = key.match(eventRegExp))) {
137
+ } else if ((match = eventRegExp.exec(key))) {
128
138
  const workletType = match[2];
129
139
  const eventType = eventTypeMap[match[3]!]!;
130
140
  const eventName = match[4]!;
@@ -142,7 +152,15 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
142
152
  __elements: snapshot.__elements,
143
153
  } as SnapshotInstance;
144
154
  if (workletType) {
145
- updateWorkletEvent(fakeSnapshot, index, oldValue[key], elementIndex, workletType, eventType, eventName);
155
+ updateWorkletEvent(
156
+ fakeSnapshot,
157
+ index,
158
+ oldValue[key] as Worklet,
159
+ elementIndex,
160
+ workletType,
161
+ eventType,
162
+ eventName,
163
+ );
146
164
  } else {
147
165
  updateEvent(fakeSnapshot, index, oldValue[key], elementIndex, eventType, eventName, key);
148
166
  }
@@ -212,7 +230,7 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
212
230
  __elements: snapshot.__elements,
213
231
  } as SnapshotInstance;
214
232
  updateGesture(fakeSnapshot, index, oldValue[key], elementIndex, workletType);
215
- } else if ((match = key.match(eventRegExp))) {
233
+ } else if ((match = eventRegExp.exec(key))) {
216
234
  const workletType = match[2];
217
235
  const eventType = eventTypeMap[match[3]!]!;
218
236
  const eventName = match[4]!;
@@ -284,4 +302,4 @@ function transformSpread(
284
302
  return result;
285
303
  }
286
304
 
287
- export { updateSpread, transformSpread };
305
+ export { transformSpread, updateSpread };
@@ -2,13 +2,14 @@
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
4
  import { onWorkletCtxUpdate } from '@lynx-js/react/worklet-runtime/bindings';
5
+ import type { Worklet } from '@lynx-js/react/worklet-runtime/bindings';
5
6
 
6
7
  import { SnapshotInstance } from '../snapshot.js';
7
8
 
8
9
  function updateWorkletEvent(
9
10
  snapshot: SnapshotInstance,
10
11
  expIndex: number,
11
- _oldValue: any,
12
+ _oldValue: Worklet,
12
13
  elementIndex: number,
13
14
  workletType: string,
14
15
  eventType: string,
@@ -17,7 +18,7 @@ function updateWorkletEvent(
17
18
  if (!snapshot.__elements) {
18
19
  return;
19
20
  }
20
- const value = snapshot.__values![expIndex] || {};
21
+ const value = (snapshot.__values![expIndex] as Worklet || undefined) ?? {};
21
22
  value._workletType = workletType;
22
23
 
23
24
  if (workletType === 'main-thread') {
@@ -1,23 +1,19 @@
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 {
5
- type Worklet,
6
- type WorkletRef,
7
- runWorkletCtx,
8
- updateWorkletRef as update,
9
- } from '@lynx-js/react/worklet-runtime/bindings';
4
+ import { runWorkletCtx, updateWorkletRef as update } from '@lynx-js/react/worklet-runtime/bindings';
5
+ import type { Element, Worklet, WorkletRefId, WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings';
10
6
 
11
- import { SnapshotInstance } from '../snapshot.js';
7
+ import type { SnapshotInstance } from '../snapshot.js';
12
8
 
13
- function workletUnRef(value: Worklet | WorkletRef<unknown>): void {
14
- if ('_wvid' in value) {
15
- update(value as any, null);
9
+ function workletUnRef(value: Worklet | WorkletRefImpl<Element>): void {
10
+ if ('_wvid' in value && (value._wvid as WorkletRefId) > 0) {
11
+ update(value as WorkletRefImpl<Element>, null);
16
12
  } else if ('_wkltId' in value) {
17
13
  if (typeof value._unmount == 'function') {
18
- value._unmount();
14
+ (value._unmount as () => void)();
19
15
  } else {
20
- runWorkletCtx(value as any, [null]);
16
+ runWorkletCtx(value, [null]);
21
17
  }
22
18
  }
23
19
  }
@@ -25,7 +21,7 @@ function workletUnRef(value: Worklet | WorkletRef<unknown>): void {
25
21
  function updateWorkletRef(
26
22
  snapshot: SnapshotInstance,
27
23
  expIndex: number,
28
- oldValue: any,
24
+ oldValue: WorkletRefImpl<Element> | Worklet | undefined,
29
25
  elementIndex: number,
30
26
  _workletType: string,
31
27
  ): void {
@@ -38,15 +34,18 @@ function updateWorkletRef(
38
34
  snapshot.__worklet_ref_set?.delete(oldValue);
39
35
  }
40
36
 
41
- const value = snapshot.__values![expIndex];
37
+ const value = snapshot.__values![expIndex] as (WorkletRefImpl<Element> | Worklet | undefined);
42
38
  if (value === null || value === undefined) {
43
39
  // do nothing
44
40
  } else if (value._wvid) {
45
- update(value as any, snapshot.__elements[elementIndex]!);
46
- } else if (value._wkltId) {
47
- // @ts-ignore
48
- value._unmount = runWorkletCtx(value as any, [{ elementRefptr: snapshot.__elements[elementIndex]! }]);
49
- } else if (value._type === '__LEPUS__' || value._lepusWorkletHash) {
41
+ if ((value._wvid as WorkletRefId) > 0) {
42
+ update(value as WorkletRefImpl<Element>, snapshot.__elements[elementIndex]!);
43
+ }
44
+ } else if ((value as Worklet)._wkltId) {
45
+ (value as Worklet)._unmount = runWorkletCtx(value as Worklet, [{
46
+ elementRefptr: (snapshot.__elements[elementIndex]!) as any,
47
+ }]) as () => void;
48
+ } else if (value._type === '__LEPUS__' || (value as Worklet)._lepusWorkletHash) {
50
49
  // During the initial render, we will not update the WorkletRef because the background thread is not ready yet.
51
50
  } else {
52
51
  throw new Error('MainThreadRef: main-thread:ref must be of type MainThreadRef or main-thread function.');
@@ -1,26 +1,44 @@
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
+
5
+ /**
6
+ * Core snapshot system that implements a compiler-hinted virtual DOM.
7
+ *
8
+ * Key components:
9
+ * 1. {@link Snapshot}: Template definition generated at compile time
10
+ * 2. {@link SnapshotInstance}: Runtime instance in the main thread
11
+ * 3. {@link BackgroundSnapshotInstance}: Runtime instance in the background thread
12
+ *
13
+ * The system uses static analysis to identify dynamic parts and generate
14
+ * optimized update instructions, avoiding full virtual DOM diffing.
15
+ */
16
+
4
17
  import type { Worklet, WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings';
5
18
 
6
19
  import type { BackgroundSnapshotInstance } from './backgroundSnapshot.js';
20
+ import { SnapshotOperation, __globalSnapshotPatch } from './lifecycle/patch/snapshotPatch.js';
7
21
  import { ListUpdateInfoRecording, __pendingListUpdates, snapshotDestroyList } from './list.js';
8
22
  import { unref } from './snapshot/ref.js';
9
- import { SnapshotOperation, __globalSnapshotPatch } from './lifecycle/patch/snapshotPatch.js';
10
23
  import { isDirectOrDeepEqual } from './utils.js';
11
24
 
25
+ /**
26
+ * Types of dynamic parts that can be updated in a snapshot
27
+ * These are determined at compile time through static analysis
28
+ */
12
29
  export const enum DynamicPartType {
13
- Attr = 0,
14
- Spread,
15
- Slot,
16
- // Component,
17
- Children,
18
- ListChildren,
19
-
20
- // Used by compat layer
21
- MultiChildren,
30
+ Attr = 0, // Regular attribute updates
31
+ Spread, // Spread operator in JSX
32
+ Slot, // Slot for component children
33
+ Children, // Regular children updates
34
+ ListChildren, // List/array children updates
35
+ MultiChildren, // Multiple children updates (compat layer)
22
36
  }
23
37
 
38
+ /**
39
+ * A snapshot definition that contains all the information needed to create and update elements
40
+ * This is generated at compile time through static analysis of the JSX
41
+ */
24
42
  interface Snapshot {
25
43
  create: null | ((ctx: SnapshotInstance) => FiberElement[]);
26
44
  update: null | ((ctx: SnapshotInstance, index: number, oldValue: any) => void)[];
@@ -234,6 +252,13 @@ export interface SerializedSnapshotInstance {
234
252
  const DEFAULT_ENTRY_NAME = '__Card__';
235
253
  const DEFAULT_CSS_ID = 0;
236
254
 
255
+ /**
256
+ * The runtime instance of a {@link Snapshot} on the main thread that manages
257
+ * the actual elements and handles updates to dynamic parts.
258
+ *
259
+ * This class is designed to be compatible with Preact's {@link ContainerNode}
260
+ * interface for Preact's renderer to operate upon.
261
+ */
237
262
  export class SnapshotInstance {
238
263
  __id: number;
239
264
  __snapshot_def: Snapshot;
@@ -9,13 +9,19 @@ import { WorkletEvents } from '@lynx-js/react/worklet-runtime/bindings';
9
9
  import { addWorkletRefInitValue } from './workletRefPool.js';
10
10
  import { useMemo } from '../hooks/react.js';
11
11
 
12
- let lastId = 0;
12
+ // Split into two variables for testing purposes
13
+ let lastIdBG = 0;
14
+ let lastIdMT = 0;
15
+
16
+ export function clearWorkletRefLastIdForTesting(): void {
17
+ lastIdBG = lastIdMT = 0;
18
+ }
13
19
 
14
20
  abstract class WorkletRef<T> {
15
21
  /**
16
22
  * @internal
17
23
  */
18
- protected _id: number;
24
+ protected _wvid: number;
19
25
  /**
20
26
  * @internal
21
27
  */
@@ -33,16 +39,13 @@ abstract class WorkletRef<T> {
33
39
  * @internal
34
40
  */
35
41
  protected constructor(initValue: T, type: string) {
42
+ this._initValue = initValue;
43
+ this._type = type;
36
44
  if (__JS__) {
37
- this._id = ++lastId;
38
- this._initValue = initValue;
39
- this._type = type;
40
- addWorkletRefInitValue(this._id, initValue);
45
+ this._wvid = ++lastIdBG;
46
+ addWorkletRefInitValue(this._wvid, initValue);
41
47
  } else {
42
- // Out of the js thread, the `WorkletRef` class here is just a placeholder and should not be accessed directly.
43
- // The real WorkletRef will be generated by the worklet runtime.
44
- this._id = -1;
45
- this._type = '__LEPUS__';
48
+ this._wvid = --lastIdMT;
46
49
  }
47
50
  }
48
51
 
@@ -72,7 +75,7 @@ abstract class WorkletRef<T> {
72
75
  */
73
76
  toJSON(): { _wvid: WorkletRefImpl<T>['_wvid'] } {
74
77
  return {
75
- _wvid: this._id,
78
+ _wvid: this._wvid,
76
79
  };
77
80
  }
78
81
  }
@@ -88,7 +91,7 @@ export class MainThreadRef<T> extends WorkletRef<T> {
88
91
  constructor(initValue: T) {
89
92
  super(initValue, 'main-thread');
90
93
  if (__JS__) {
91
- const id = this._id;
94
+ const id = this._wvid;
92
95
  this._lifecycleObserver = lynx.getNativeApp().createJSObjectDestructionObserver?.(() => {
93
96
  lynx.getCoreContext?.().dispatchEvent({
94
97
  type: WorkletEvents.releaseWorkletRef,
@@ -58,7 +58,7 @@ test('renders options.wrapper around node', async () => {
58
58
  });
59
59
  ```
60
60
 
61
- 💡 Since our testing environment (`@lynx-js/test-environment`) is based on jsdom, You may also be interested in installing `@testing-library/jest-dom` so you can use
61
+ 💡 Since our testing environment (`@lynx-js/testing-environment`) is based on jsdom, You may also be interested in installing `@testing-library/jest-dom` so you can use
62
62
  [the custom jest matchers](https://github.com/testing-library/jest-dom).
63
63
 
64
64
  ## Examples