@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.
- package/CHANGELOG.md +46 -0
- package/README.md +4 -2
- package/package.json +4 -4
- package/refresh/.turbo/turbo-build.log +1 -1
- package/refresh/package.json +1 -1
- package/runtime/lazy/import.js +7 -1
- package/runtime/lepus/index.js +1 -1
- package/runtime/lepus/jsx-dev-runtime/index.js +1 -1
- package/runtime/lib/backgroundSnapshot.d.ts +1 -1
- package/runtime/lib/backgroundSnapshot.js +7 -4
- package/runtime/lib/backgroundSnapshot.js.map +1 -1
- package/runtime/lib/document.d.ts +33 -2
- package/runtime/lib/document.js +9 -1
- package/runtime/lib/document.js.map +1 -1
- package/runtime/lib/hydrate.js +2 -1
- package/runtime/lib/hydrate.js.map +1 -1
- package/runtime/lib/lifecycle/delayUnmount.d.ts +1 -1
- package/runtime/lib/lifecycle/delayUnmount.js +1 -4
- package/runtime/lib/lifecycle/delayUnmount.js.map +1 -1
- package/runtime/lib/lifecycle/destroy.js +2 -2
- package/runtime/lib/lifecycle/destroy.js.map +1 -1
- package/runtime/lib/lifecycle/patch/commit.d.ts +21 -3
- package/runtime/lib/lifecycle/patch/commit.js +44 -56
- package/runtime/lib/lifecycle/patch/commit.js.map +1 -1
- package/runtime/lib/lifecycle/patch/isMainThreadHydrationFinished.d.ts +2 -0
- package/runtime/lib/lifecycle/patch/isMainThreadHydrationFinished.js +9 -0
- package/runtime/lib/lifecycle/patch/isMainThreadHydrationFinished.js.map +1 -0
- package/runtime/lib/lifecycle/patch/snapshotPatch.d.ts +5 -0
- package/runtime/lib/lifecycle/patch/snapshotPatch.js +5 -0
- package/runtime/lib/lifecycle/patch/snapshotPatch.js.map +1 -1
- package/runtime/lib/lifecycle/patch/snapshotPatchApply.d.ts +5 -0
- package/runtime/lib/lifecycle/patch/snapshotPatchApply.js +21 -5
- package/runtime/lib/lifecycle/patch/snapshotPatchApply.js.map +1 -1
- package/runtime/lib/lifecycle/patch/updateMainThread.js +4 -3
- package/runtime/lib/lifecycle/patch/updateMainThread.js.map +1 -1
- package/runtime/lib/lifecycle/reload.js +11 -4
- package/runtime/lib/lifecycle/reload.js.map +1 -1
- package/runtime/lib/lifecycle/render.d.ts +1 -3
- package/runtime/lib/lifecycle/render.js +4 -6
- package/runtime/lib/lifecycle/render.js.map +1 -1
- package/runtime/lib/list.d.ts +1 -1
- package/runtime/lib/list.js +60 -23
- package/runtime/lib/list.js.map +1 -1
- package/runtime/lib/lynx/env.d.ts +1 -1
- package/runtime/lib/lynx/env.js +1 -1
- package/runtime/lib/lynx/env.js.map +1 -1
- package/runtime/lib/lynx/runWithForce.js +12 -4
- package/runtime/lib/lynx/runWithForce.js.map +1 -1
- package/runtime/lib/lynx/tt.js +6 -8
- package/runtime/lib/lynx/tt.js.map +1 -1
- package/runtime/lib/lynx-api.d.ts +4 -4
- package/runtime/lib/lynx-api.js +5 -5
- package/runtime/lib/lynx-api.js.map +1 -1
- package/runtime/lib/lynx.js +6 -5
- package/runtime/lib/lynx.js.map +1 -1
- package/runtime/lib/opcodes.js +2 -2
- package/runtime/lib/opcodes.js.map +1 -1
- package/runtime/lib/renderToOpcodes/index.js +5 -1
- package/runtime/lib/renderToOpcodes/index.js.map +1 -1
- package/runtime/lib/root.d.ts +4 -0
- package/runtime/lib/root.js +6 -2
- package/runtime/lib/root.js.map +1 -1
- package/runtime/lib/snapshot/ref.d.ts +1 -1
- package/runtime/lib/snapshot/ref.js +1 -1
- package/runtime/lib/snapshot/ref.js.map +1 -1
- package/runtime/lib/snapshot/spread.d.ts +2 -2
- package/runtime/lib/snapshot/spread.js +9 -9
- package/runtime/lib/snapshot/spread.js.map +1 -1
- package/runtime/lib/snapshot/workletEvent.d.ts +2 -1
- package/runtime/lib/snapshot/workletEvent.js +1 -1
- package/runtime/lib/snapshot/workletEvent.js.map +1 -1
- package/runtime/lib/snapshot/workletRef.d.ts +4 -4
- package/runtime/lib/snapshot/workletRef.js +8 -6
- package/runtime/lib/snapshot/workletRef.js.map +1 -1
- package/runtime/lib/snapshot.d.ts +31 -5
- package/runtime/lib/snapshot.js +15 -3
- package/runtime/lib/snapshot.js.map +1 -1
- package/runtime/lib/worklet/workletRef.d.ts +1 -0
- package/runtime/lib/worklet/workletRef.js +14 -12
- package/runtime/lib/worklet/workletRef.js.map +1 -1
- package/runtime/src/backgroundSnapshot.ts +20 -13
- package/runtime/src/document.ts +33 -2
- package/runtime/src/hydrate.ts +3 -1
- package/runtime/src/lifecycle/delayUnmount.ts +2 -2
- package/runtime/src/lifecycle/destroy.ts +3 -2
- package/runtime/src/lifecycle/patch/commit.ts +75 -72
- package/runtime/src/lifecycle/patch/isMainThreadHydrationFinished.ts +10 -0
- package/runtime/src/lifecycle/patch/snapshotPatch.ts +9 -0
- package/runtime/src/lifecycle/patch/snapshotPatchApply.ts +20 -5
- package/runtime/src/lifecycle/patch/updateMainThread.ts +4 -3
- package/runtime/src/lifecycle/reload.ts +14 -4
- package/runtime/src/lifecycle/render.ts +6 -8
- package/runtime/src/list.ts +71 -23
- package/runtime/src/lynx/env.ts +1 -1
- package/runtime/src/lynx/runWithForce.ts +17 -6
- package/runtime/src/lynx/tt.ts +10 -17
- package/runtime/src/lynx-api.ts +7 -7
- package/runtime/src/lynx.ts +7 -6
- package/runtime/src/opcodes.ts +2 -2
- package/runtime/src/renderToOpcodes/index.ts +6 -1
- package/runtime/src/root.ts +6 -2
- package/runtime/src/snapshot/ref.ts +7 -7
- package/runtime/src/snapshot/spread.ts +28 -10
- package/runtime/src/snapshot/workletEvent.ts +3 -2
- package/runtime/src/snapshot/workletRef.ts +18 -19
- package/runtime/src/snapshot.ts +35 -10
- package/runtime/src/worklet/workletRef.ts +15 -12
- package/testing-library/README.md +1 -1
- package/testing-library/dist/env/vitest.js +23 -23
- package/testing-library/dist/index.d.ts +24 -24
- package/testing-library/dist/index.js +2 -2
- package/testing-library/dist/pure.js +13 -15
- package/testing-library/dist/vitest-global-setup.js +13 -14
- package/testing-library/types/entry.d.ts +2 -2
- package/testing-library/types/index.d.ts +3 -3
- package/transform/dist/wasm.cjs +1 -1
- package/worklet-runtime/dist/dev.js +25 -13
- package/worklet-runtime/dist/dev.js.map +3 -3
- package/worklet-runtime/dist/main.js +25 -13
- package/worklet-runtime/dist/main.js.map +3 -3
- package/worklet-runtime/lib/bindings/bindings.d.ts +1 -1
- package/worklet-runtime/lib/bindings/bindings.js +2 -2
- package/worklet-runtime/lib/bindings/bindings.js.map +1 -1
- package/worklet-runtime/lib/bindings/index.d.ts +1 -1
- package/worklet-runtime/lib/bindings/index.js +1 -1
- package/worklet-runtime/lib/bindings/index.js.map +1 -1
- package/worklet-runtime/lib/bindings/types.d.ts +7 -3
- package/worklet-runtime/lib/bindings/types.js +3 -0
- package/worklet-runtime/lib/bindings/types.js.map +1 -1
- package/worklet-runtime/lib/ctxTrace.d.ts +8 -0
- package/worklet-runtime/lib/ctxTrace.js +13 -0
- package/worklet-runtime/lib/ctxTrace.js.map +1 -0
- package/worklet-runtime/lib/global.d.ts +3 -3
- package/worklet-runtime/lib/workletRuntime.js +11 -8
- package/worklet-runtime/lib/workletRuntime.js.map +1 -1
package/runtime/src/lynx/tt.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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:
|
|
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(() =>
|
|
208
|
+
runWithForce(() => render(__root.__jsx, __root as any));
|
|
216
209
|
});
|
|
217
210
|
lynxCoreInject.tt.GlobalEventEmitter.emit('onGlobalPropsChanged');
|
|
218
211
|
}
|
package/runtime/src/lynx-api.ts
CHANGED
|
@@ -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 (
|
|
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 (
|
|
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 (
|
|
85
|
+
if (__MAIN_THREAD__) {
|
|
86
86
|
__root.__jsx = jsx;
|
|
87
87
|
} else {
|
|
88
88
|
__root.__jsx = jsx;
|
|
89
|
-
|
|
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 {
|
|
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';
|
package/runtime/src/lynx.ts
CHANGED
|
@@ -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 {
|
|
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 (
|
|
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 (
|
|
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 (
|
|
43
|
-
|
|
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
|
-
|
|
57
|
+
setupLynxTestingEnv();
|
package/runtime/src/opcodes.ts
CHANGED
|
@@ -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
|
-
|
|
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';
|
package/runtime/src/root.ts
CHANGED
|
@@ -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 (
|
|
17
|
+
if (__MAIN_THREAD__) {
|
|
14
18
|
setRoot(new SnapshotInstance('root'));
|
|
15
|
-
} else if (
|
|
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,
|
|
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 |
|
|
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 {
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 {
|
|
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:
|
|
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
|
-
|
|
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 |
|
|
14
|
-
if ('_wvid' in value) {
|
|
15
|
-
update(value as
|
|
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
|
|
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:
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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.');
|
package/runtime/src/snapshot.ts
CHANGED
|
@@ -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
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
38
|
-
this.
|
|
39
|
-
this._type = type;
|
|
40
|
-
addWorkletRefInitValue(this._id, initValue);
|
|
45
|
+
this._wvid = ++lastIdBG;
|
|
46
|
+
addWorkletRefInitValue(this._wvid, initValue);
|
|
41
47
|
} else {
|
|
42
|
-
|
|
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.
|
|
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.
|
|
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/
|
|
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
|