@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
|
@@ -1,12 +1,28 @@
|
|
|
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
|
+
* Implements the commit phase of the rendering lifecycle.
|
|
7
|
+
* This module patches Preact's commit phase to integrate with the snapshot system,
|
|
8
|
+
* handling the collection and transmission of patches between threads.
|
|
9
|
+
*
|
|
10
|
+
* The commit phase is responsible for:
|
|
11
|
+
* - Collecting patches from the snapshot system
|
|
12
|
+
* - Managing commit tasks and their execution
|
|
13
|
+
* - Coordinating with the native layer for updates
|
|
14
|
+
* - Handling performance timing and pipeline options
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* This module patches Preact's commit phase by hacking into the internal of
|
|
19
|
+
* its [options](https://preactjs.com/guide/v10/options/) API
|
|
20
|
+
*/
|
|
21
|
+
|
|
4
22
|
import type { VNode } from 'preact';
|
|
5
23
|
import { options } from 'preact';
|
|
6
24
|
import type { Component } from 'preact/compat';
|
|
7
25
|
|
|
8
|
-
import type { SnapshotPatch } from './snapshotPatch.js';
|
|
9
|
-
import { takeGlobalSnapshotPatch } from './snapshotPatch.js';
|
|
10
26
|
import { LifecycleConstant } from '../../lifecycleConstant.js';
|
|
11
27
|
import {
|
|
12
28
|
PerformanceTimingKeys,
|
|
@@ -16,12 +32,14 @@ import {
|
|
|
16
32
|
setPipeline,
|
|
17
33
|
} from '../../lynx/performance.js';
|
|
18
34
|
import { CATCH_ERROR, COMMIT, RENDER_CALLBACKS, VNODE } from '../../renderToOpcodes/constants.js';
|
|
19
|
-
import { updateBackgroundRefs } from '../../snapshot/ref.js';
|
|
20
35
|
import { backgroundSnapshotInstanceManager } from '../../snapshot.js';
|
|
36
|
+
import { updateBackgroundRefs } from '../../snapshot/ref.js';
|
|
21
37
|
import { isEmptyObject } from '../../utils.js';
|
|
22
38
|
import { takeWorkletRefInitValuePatch } from '../../worklet/workletRefPool.js';
|
|
23
39
|
import { runDelayedUnmounts, takeDelayedUnmounts } from '../delayUnmount.js';
|
|
24
40
|
import { getReloadVersion } from '../pass.js';
|
|
41
|
+
import type { SnapshotPatch } from './snapshotPatch.js';
|
|
42
|
+
import { takeGlobalSnapshotPatch } from './snapshotPatch.js';
|
|
25
43
|
|
|
26
44
|
let globalFlushOptions: FlushOptions = {};
|
|
27
45
|
|
|
@@ -30,58 +48,50 @@ let nextCommitTaskId = 1;
|
|
|
30
48
|
|
|
31
49
|
let globalBackgroundSnapshotInstancesToRemove: number[] = [];
|
|
32
50
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
51
|
+
/**
|
|
52
|
+
* A single patch operation.
|
|
53
|
+
*/
|
|
38
54
|
interface Patch {
|
|
39
55
|
id: number;
|
|
40
56
|
snapshotPatch?: SnapshotPatch;
|
|
41
57
|
workletRefInitValuePatch?: [id: number, value: unknown][];
|
|
42
58
|
}
|
|
43
59
|
|
|
60
|
+
/**
|
|
61
|
+
* List of patches to be applied in a single update cycle with flush options.
|
|
62
|
+
*/
|
|
44
63
|
interface PatchList {
|
|
45
64
|
patchList: Patch[];
|
|
46
65
|
flushOptions?: FlushOptions;
|
|
47
66
|
}
|
|
48
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Configuration options for patch operations
|
|
70
|
+
*/
|
|
49
71
|
interface PatchOptions {
|
|
50
72
|
pipelineOptions?: PipelineOptions;
|
|
51
73
|
reloadVersion: number;
|
|
52
74
|
isHydration?: boolean;
|
|
53
75
|
}
|
|
54
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Replaces Preact's default commit hook with our custom implementation
|
|
79
|
+
*/
|
|
55
80
|
function replaceCommitHook(): void {
|
|
56
|
-
|
|
57
|
-
type DebounceRendering = (f: () => void) => void;
|
|
58
|
-
const injectDebounceRendering = (debounceRendering: DebounceRendering): DebounceRendering => {
|
|
59
|
-
return (f: () => void) => {
|
|
60
|
-
debounceRendering(() => {
|
|
61
|
-
f();
|
|
62
|
-
void commitToMainThread();
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
|
-
const defaultDebounceRendering = options.debounceRendering?.bind(options)
|
|
67
|
-
?? (Promise.prototype.then.bind(Promise.resolve()) as DebounceRendering);
|
|
68
|
-
let _debounceRendering = injectDebounceRendering(defaultDebounceRendering);
|
|
69
|
-
Object.defineProperty(options, 'debounceRendering', {
|
|
70
|
-
get() {
|
|
71
|
-
return _debounceRendering;
|
|
72
|
-
},
|
|
73
|
-
set(debounceRendering: DebounceRendering) {
|
|
74
|
-
_debounceRendering = injectDebounceRendering(debounceRendering);
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const oldCommit = options[COMMIT];
|
|
81
|
+
const originalPreactCommit = options[COMMIT];
|
|
79
82
|
const commit = async (vnode: VNode, commitQueue: any[]) => {
|
|
80
|
-
|
|
83
|
+
// Skip commit phase for MT runtime
|
|
84
|
+
if (__MAIN_THREAD__) {
|
|
81
85
|
// for testing only
|
|
82
86
|
commitQueue.length = 0;
|
|
83
87
|
return;
|
|
84
88
|
}
|
|
89
|
+
|
|
90
|
+
// Mark the end of virtual DOM diffing phase for performance tracking
|
|
91
|
+
markTimingLegacy(PerformanceTimingKeys.updateDiffVdomEnd);
|
|
92
|
+
markTiming(PerformanceTimingKeys.diffVdomEnd);
|
|
93
|
+
|
|
94
|
+
// The callback functions to be called after components are rendered.
|
|
85
95
|
const renderCallbacks = commitQueue.map((component: Component<any>) => {
|
|
86
96
|
const ret = {
|
|
87
97
|
component,
|
|
@@ -92,16 +102,19 @@ function replaceCommitHook(): void {
|
|
|
92
102
|
return ret;
|
|
93
103
|
});
|
|
94
104
|
commitQueue.length = 0;
|
|
105
|
+
|
|
95
106
|
const delayedUnmounts = takeDelayedUnmounts();
|
|
96
107
|
|
|
97
108
|
const backgroundSnapshotInstancesToRemove = globalBackgroundSnapshotInstancesToRemove;
|
|
98
109
|
globalBackgroundSnapshotInstancesToRemove = [];
|
|
99
110
|
|
|
100
111
|
const commitTaskId = genCommitTaskId();
|
|
112
|
+
|
|
113
|
+
// Register the commit task
|
|
101
114
|
globalCommitTaskMap.set(commitTaskId, () => {
|
|
102
115
|
updateBackgroundRefs(commitTaskId);
|
|
103
116
|
runDelayedUnmounts(delayedUnmounts);
|
|
104
|
-
|
|
117
|
+
originalPreactCommit?.(vnode, renderCallbacks);
|
|
105
118
|
renderCallbacks.some(wrapper => {
|
|
106
119
|
try {
|
|
107
120
|
wrapper[RENDER_CALLBACKS].some((cb: (this: Component) => void) => {
|
|
@@ -120,8 +133,11 @@ function replaceCommitHook(): void {
|
|
|
120
133
|
}
|
|
121
134
|
});
|
|
122
135
|
|
|
136
|
+
// Collect patches for this update
|
|
123
137
|
const snapshotPatch = takeGlobalSnapshotPatch();
|
|
138
|
+
const flushOptions = globalFlushOptions;
|
|
124
139
|
const workletRefInitValuePatch = takeWorkletRefInitValuePatch();
|
|
140
|
+
globalFlushOptions = {};
|
|
125
141
|
if (!snapshotPatch && workletRefInitValuePatch.length === 0) {
|
|
126
142
|
// before hydration, skip patch
|
|
127
143
|
return;
|
|
@@ -137,46 +153,29 @@ function replaceCommitHook(): void {
|
|
|
137
153
|
if (workletRefInitValuePatch.length) {
|
|
138
154
|
patch.workletRefInitValuePatch = workletRefInitValuePatch;
|
|
139
155
|
}
|
|
156
|
+
const patchList: PatchList = {
|
|
157
|
+
patchList: [patch],
|
|
158
|
+
};
|
|
159
|
+
if (!isEmptyObject(flushOptions)) {
|
|
160
|
+
patchList.flushOptions = flushOptions;
|
|
161
|
+
}
|
|
162
|
+
const obj = commitPatchUpdate(patchList, {});
|
|
140
163
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function commitToMainThread(): Promise<void> {
|
|
147
|
-
if (patchesToCommit.length === 0) {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
markTimingLegacy(PerformanceTimingKeys.updateDiffVdomEnd);
|
|
152
|
-
markTiming(PerformanceTimingKeys.diffVdomEnd);
|
|
153
|
-
|
|
154
|
-
const flushOptions = globalFlushOptions;
|
|
155
|
-
globalFlushOptions = {};
|
|
156
|
-
|
|
157
|
-
const patchList: PatchList = {
|
|
158
|
-
patchList: patchesToCommit,
|
|
159
|
-
};
|
|
160
|
-
patchesToCommit = [];
|
|
161
|
-
|
|
162
|
-
if (!isEmptyObject(flushOptions)) {
|
|
163
|
-
patchList.flushOptions = flushOptions;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const obj = commitPatchUpdate(patchList, {});
|
|
167
|
-
|
|
168
|
-
lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => {
|
|
169
|
-
for (let i = 0; i < patchList.patchList.length; i++) {
|
|
170
|
-
const patch = patchList.patchList[i]!;
|
|
171
|
-
const commitTask = globalCommitTaskMap.get(patch.id);
|
|
164
|
+
// Send the update to the native layer
|
|
165
|
+
lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => {
|
|
166
|
+
const commitTask = globalCommitTaskMap.get(commitTaskId);
|
|
172
167
|
if (commitTask) {
|
|
173
168
|
commitTask();
|
|
174
|
-
globalCommitTaskMap.delete(
|
|
169
|
+
globalCommitTaskMap.delete(commitTaskId);
|
|
175
170
|
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
options[COMMIT] = commit as ((...args: Parameters<typeof commit>) => void);
|
|
178
174
|
}
|
|
179
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Prepares the patch update for transmission to the native layer
|
|
178
|
+
*/
|
|
180
179
|
function commitPatchUpdate(patchList: PatchList, patchOptions: Omit<PatchOptions, 'reloadVersion'>): {
|
|
181
180
|
data: string;
|
|
182
181
|
patchOptions: PatchOptions;
|
|
@@ -212,9 +211,16 @@ function commitPatchUpdate(patchList: PatchList, patchOptions: Omit<PatchOptions
|
|
|
212
211
|
return obj;
|
|
213
212
|
}
|
|
214
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Generates a unique ID for commit tasks
|
|
216
|
+
*/
|
|
215
217
|
function genCommitTaskId(): number {
|
|
216
218
|
return nextCommitTaskId++;
|
|
217
219
|
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Resets the commit task ID counter
|
|
223
|
+
*/
|
|
218
224
|
function clearCommitTaskId(): void {
|
|
219
225
|
nextCommitTaskId = 1;
|
|
220
226
|
}
|
|
@@ -231,18 +237,15 @@ function replaceRequestAnimationFrame(): void {
|
|
|
231
237
|
* @internal
|
|
232
238
|
*/
|
|
233
239
|
export {
|
|
240
|
+
clearCommitTaskId,
|
|
234
241
|
commitPatchUpdate,
|
|
235
|
-
commitToMainThread,
|
|
236
242
|
genCommitTaskId,
|
|
237
|
-
clearCommitTaskId,
|
|
238
243
|
globalBackgroundSnapshotInstancesToRemove,
|
|
239
244
|
globalCommitTaskMap,
|
|
240
245
|
globalFlushOptions,
|
|
241
246
|
nextCommitTaskId,
|
|
242
|
-
patchesToCommit,
|
|
243
|
-
clearPatchesToCommit,
|
|
244
247
|
replaceCommitHook,
|
|
245
248
|
replaceRequestAnimationFrame,
|
|
246
|
-
type PatchOptions,
|
|
247
249
|
type PatchList,
|
|
250
|
+
type PatchOptions,
|
|
248
251
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { onHydrationFinished } from '@lynx-js/react/worklet-runtime/bindings';
|
|
2
|
+
|
|
3
|
+
export let isMainThreadHydrationFinished = false;
|
|
4
|
+
|
|
5
|
+
export function setMainThreadHydrationFinished(isFinished: boolean): void {
|
|
6
|
+
if (isFinished && !isMainThreadHydrationFinished) {
|
|
7
|
+
onHydrationFinished();
|
|
8
|
+
}
|
|
9
|
+
isMainThreadHydrationFinished = isFinished;
|
|
10
|
+
}
|
|
@@ -1,6 +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
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Defines the core patch operations for the snapshot system.
|
|
7
|
+
* The patch operations are designed to be serializable and minimal, allowing
|
|
8
|
+
* efficient transmission between threads and application to element tree.
|
|
9
|
+
*/
|
|
10
|
+
|
|
4
11
|
export const enum SnapshotOperation {
|
|
5
12
|
CreateElement,
|
|
6
13
|
InsertBefore,
|
|
@@ -12,6 +19,8 @@ export const enum SnapshotOperation {
|
|
|
12
19
|
DEV_ONLY_RegisterWorklet = 101,
|
|
13
20
|
}
|
|
14
21
|
|
|
22
|
+
// Operation format definitions:
|
|
23
|
+
//
|
|
15
24
|
// [opcode: SnapshotOperation.CreateElement, type: string, id: number]
|
|
16
25
|
// [opcode: SnapshotOperation.InsertBefore, parentId: number, id: number, beforeId: number | undefined]
|
|
17
26
|
// [opcode: SnapshotOperation.RemoveChild, parentId: number, childId: number]
|
|
@@ -1,14 +1,31 @@
|
|
|
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
|
+
* Implements the patch application logic for the snapshot system.
|
|
7
|
+
* This module is responsible for interpreting and executing patch operations
|
|
8
|
+
* that were generated in the background thread, applying them to the DOM
|
|
9
|
+
* in the main thread.
|
|
10
|
+
*
|
|
11
|
+
* The module handles various operations like element creation, insertion,
|
|
12
|
+
* removal, and attribute updates, ensuring they are applied in the correct
|
|
13
|
+
* order and with proper error handling.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { SnapshotInstance, createSnapshot, snapshotInstanceManager, snapshotManager } from '../../snapshot.js';
|
|
4
17
|
import type { SnapshotPatch } from './snapshotPatch.js';
|
|
5
18
|
import { SnapshotOperation } from './snapshotPatch.js';
|
|
6
|
-
import { SnapshotInstance, createSnapshot, snapshotInstanceManager, snapshotManager } from '../../snapshot.js';
|
|
7
19
|
|
|
8
20
|
function reportCtxNotFound(): void {
|
|
9
21
|
lynx.reportError(new Error(`snapshotPatchApply failed: ctx not found`));
|
|
10
22
|
}
|
|
11
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Applies a patch of snapshot operations to the main thread.
|
|
26
|
+
* This is the counterpart to the patch generation in the background thread.
|
|
27
|
+
* Each operation in the patch is processed sequentially to update the DOM.
|
|
28
|
+
*/
|
|
12
29
|
export function snapshotPatchApply(snapshotPatch: SnapshotPatch): void {
|
|
13
30
|
const length = snapshotPatch.length;
|
|
14
31
|
for (let i = 0; i < length; ++i) {
|
|
@@ -107,10 +124,8 @@ export function snapshotPatchApply(snapshotPatch: SnapshotPatch): void {
|
|
|
107
124
|
}
|
|
108
125
|
|
|
109
126
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* @param code - The code to be evaluated
|
|
113
|
-
* @returns the evaluated expression
|
|
127
|
+
* Evaluates a string as code with ReactLynx runtime injected.
|
|
128
|
+
* Used for HMR (Hot Module Replacement) to update snapshot definitions.
|
|
114
129
|
*/
|
|
115
130
|
function evaluate<T>(code: string): T {
|
|
116
131
|
return new Function(`return ${code}`)();
|
|
@@ -2,17 +2,18 @@
|
|
|
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
|
-
import {
|
|
5
|
+
import { updateWorkletRefInitValueChanges } from '@lynx-js/react/worklet-runtime/bindings';
|
|
6
6
|
|
|
7
7
|
import type { PatchList, PatchOptions } from './commit.js';
|
|
8
8
|
import { snapshotPatchApply } from './snapshotPatchApply.js';
|
|
9
9
|
import { LifecycleConstant } from '../../lifecycleConstant.js';
|
|
10
10
|
import { __pendingListUpdates } from '../../list.js';
|
|
11
|
-
import {
|
|
11
|
+
import { markTiming, PerformanceTimingKeys, setPipeline } from '../../lynx/performance.js';
|
|
12
12
|
import { takeGlobalRefPatchMap } from '../../snapshot/ref.js';
|
|
13
13
|
import { __page } from '../../snapshot.js';
|
|
14
14
|
import { isEmptyObject } from '../../utils.js';
|
|
15
15
|
import { getReloadVersion } from '../pass.js';
|
|
16
|
+
import { setMainThreadHydrationFinished } from './isMainThreadHydrationFinished.js';
|
|
16
17
|
|
|
17
18
|
function updateMainThread(
|
|
18
19
|
{ data, patchOptions }: {
|
|
@@ -47,7 +48,7 @@ function updateMainThread(
|
|
|
47
48
|
markTiming(PerformanceTimingKeys.patchChangesEnd);
|
|
48
49
|
markTiming(PerformanceTimingKeys.mtsRenderEnd);
|
|
49
50
|
if (patchOptions.isHydration) {
|
|
50
|
-
|
|
51
|
+
setMainThreadHydrationFinished(true);
|
|
51
52
|
}
|
|
52
53
|
if (patchOptions.pipelineOptions) {
|
|
53
54
|
flushOptions.pipelineOptions = patchOptions.pipelineOptions;
|
|
@@ -1,19 +1,28 @@
|
|
|
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
|
+
* Implements the reload (thinking of "refresh" in browser) for both main thread
|
|
7
|
+
* and background thread.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { render } from 'preact';
|
|
11
|
+
|
|
4
12
|
import { hydrate } from '../hydrate.js';
|
|
5
13
|
import { LifecycleConstant } from '../lifecycleConstant.js';
|
|
6
14
|
import { __pendingListUpdates } from '../list.js';
|
|
7
15
|
import { __root, setRoot } from '../root.js';
|
|
8
|
-
import { takeGlobalRefPatchMap } from '../snapshot/ref.js';
|
|
9
16
|
import { SnapshotInstance, __page, snapshotInstanceManager } from '../snapshot.js';
|
|
17
|
+
import { takeGlobalRefPatchMap } from '../snapshot/ref.js';
|
|
10
18
|
import { isEmptyObject } from '../utils.js';
|
|
11
|
-
import { destroyBackground } from './destroy.js';
|
|
12
19
|
import { destroyWorklet } from '../worklet/destroy.js';
|
|
20
|
+
import { destroyBackground } from './destroy.js';
|
|
13
21
|
import { clearJSReadyEventIdSwap, isJSReady } from './event/jsReady.js';
|
|
14
22
|
import { increaseReloadVersion } from './pass.js';
|
|
15
23
|
import { deinitGlobalSnapshotPatch } from './patch/snapshotPatch.js';
|
|
16
|
-
import {
|
|
24
|
+
import { renderMainThread } from './render.js';
|
|
25
|
+
import { setMainThreadHydrationFinished } from './patch/isMainThreadHydrationFinished.js';
|
|
17
26
|
|
|
18
27
|
function reloadMainThread(data: any, options: UpdatePageOption): void {
|
|
19
28
|
if (__PROFILE__) {
|
|
@@ -30,6 +39,7 @@ function reloadMainThread(data: any, options: UpdatePageOption): void {
|
|
|
30
39
|
snapshotInstanceManager.clear();
|
|
31
40
|
__pendingListUpdates.clear();
|
|
32
41
|
clearJSReadyEventIdSwap();
|
|
42
|
+
setMainThreadHydrationFinished(false);
|
|
33
43
|
|
|
34
44
|
const oldRoot = __root;
|
|
35
45
|
setRoot(new SnapshotInstance('root'));
|
|
@@ -74,7 +84,7 @@ function reloadBackground(updateData: Record<string, any>): void {
|
|
|
74
84
|
// COW when modify `lynx.__initData` to make sure Provider & Consumer works
|
|
75
85
|
lynx.__initData = Object.assign({}, lynx.__initData, updateData);
|
|
76
86
|
|
|
77
|
-
|
|
87
|
+
render(__root.__jsx, __root as any);
|
|
78
88
|
|
|
79
89
|
if (__PROFILE__) {
|
|
80
90
|
console.profileEnd();
|
|
@@ -1,13 +1,16 @@
|
|
|
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
|
+
* Implements the IFR (Instant First-Frame Rendering) on main thread.
|
|
7
|
+
*/
|
|
8
|
+
|
|
4
9
|
import { render } from 'preact';
|
|
5
|
-
import type { ComponentChild, ContainerNode } from 'preact';
|
|
6
10
|
|
|
7
11
|
import { renderOpcodesInto } from '../opcodes.js';
|
|
8
12
|
import { render as renderToString } from '../renderToOpcodes/index.js';
|
|
9
13
|
import { __root } from '../root.js';
|
|
10
|
-
import { commitToMainThread } from './patch/commit.js';
|
|
11
14
|
|
|
12
15
|
function renderMainThread(): void {
|
|
13
16
|
/* v8 ignore start */
|
|
@@ -47,9 +50,4 @@ function renderMainThread(): void {
|
|
|
47
50
|
/* v8 ignore stop */
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
render(vnode, parent);
|
|
52
|
-
void commitToMainThread();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export { renderMainThread, renderBackground };
|
|
53
|
+
export { renderMainThread };
|
package/runtime/src/list.ts
CHANGED
|
@@ -78,10 +78,12 @@ export class ListUpdateInfoRecording implements ListUpdateInfo {
|
|
|
78
78
|
// __FlushElementTree(listElement);
|
|
79
79
|
// });
|
|
80
80
|
__SetAttribute(listElement, 'update-list-info', this.__toAttribute());
|
|
81
|
+
const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory(this.list.childNodes);
|
|
81
82
|
__UpdateListCallbacks(
|
|
82
83
|
listElement,
|
|
83
|
-
|
|
84
|
+
componentAtIndex,
|
|
84
85
|
enqueueComponentFactory(),
|
|
86
|
+
componentAtIndexes,
|
|
85
87
|
);
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -243,13 +245,17 @@ export function clearListGlobal(): void {
|
|
|
243
245
|
}
|
|
244
246
|
}
|
|
245
247
|
|
|
246
|
-
export function componentAtIndexFactory(
|
|
248
|
+
export function componentAtIndexFactory(
|
|
249
|
+
ctx: SnapshotInstance[],
|
|
250
|
+
): [ComponentAtIndexCallback, ComponentAtIndexesCallback] {
|
|
247
251
|
const componentAtIndex = (
|
|
248
252
|
list: FiberElement,
|
|
249
253
|
listID: number,
|
|
250
254
|
cellIndex: number,
|
|
251
255
|
operationID: number,
|
|
252
256
|
enableReuseNotification: boolean,
|
|
257
|
+
enableBatchRender: boolean = false,
|
|
258
|
+
asyncFlush: boolean = false,
|
|
253
259
|
) => {
|
|
254
260
|
const signMap = gSignMap[listID];
|
|
255
261
|
const recycleMap = gRecycleMap[listID];
|
|
@@ -284,7 +290,14 @@ export function componentAtIndexFactory(ctx: SnapshotInstance[]): ComponentAtInd
|
|
|
284
290
|
if (recycleSignMap?.has(sign)) {
|
|
285
291
|
signMap.set(sign, childCtx);
|
|
286
292
|
recycleSignMap.delete(sign);
|
|
287
|
-
|
|
293
|
+
if (!enableBatchRender) {
|
|
294
|
+
__FlushElementTree(root, { triggerLayout: true, operationID, elementID: sign, listID });
|
|
295
|
+
} else if (enableBatchRender && asyncFlush) {
|
|
296
|
+
__FlushElementTree(root, { asyncFlush: true });
|
|
297
|
+
} else {
|
|
298
|
+
// enableBatchRender == true && asyncFlush == false
|
|
299
|
+
// in this case, no need to invoke __FlushElementTree because in the end of componentAtIndexes(), the list will invoke __FlushElementTree.
|
|
300
|
+
}
|
|
288
301
|
return sign;
|
|
289
302
|
} else {
|
|
290
303
|
const newCtx = childCtx.takeElements();
|
|
@@ -299,24 +312,31 @@ export function componentAtIndexFactory(ctx: SnapshotInstance[]): ComponentAtInd
|
|
|
299
312
|
hydrate(oldCtx, childCtx);
|
|
300
313
|
oldCtx.unRenderElements();
|
|
301
314
|
const root = childCtx.__element_root!;
|
|
302
|
-
if (
|
|
303
|
-
|
|
315
|
+
if (!enableBatchRender) {
|
|
316
|
+
const flushOptions: FlushOptions = {
|
|
304
317
|
triggerLayout: true,
|
|
305
318
|
operationID,
|
|
306
319
|
elementID: sign,
|
|
307
320
|
listID,
|
|
308
|
-
|
|
321
|
+
};
|
|
322
|
+
if (enableReuseNotification) {
|
|
323
|
+
flushOptions.listReuseNotification = {
|
|
309
324
|
listElement: list,
|
|
310
325
|
itemKey: platformInfo['item-key'],
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
__FlushElementTree(root, flushOptions);
|
|
329
|
+
} else if (enableBatchRender && asyncFlush) {
|
|
330
|
+
const flushOptions: FlushOptions = {
|
|
331
|
+
asyncFlush: true,
|
|
332
|
+
};
|
|
333
|
+
if (enableReuseNotification) {
|
|
334
|
+
flushOptions.listReuseNotification = {
|
|
335
|
+
listElement: list,
|
|
336
|
+
itemKey: platformInfo['item-key'],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
__FlushElementTree(root, flushOptions);
|
|
320
340
|
}
|
|
321
341
|
signMap.set(sign, childCtx);
|
|
322
342
|
commitMainThreadPatchUpdate(undefined);
|
|
@@ -327,17 +347,43 @@ export function componentAtIndexFactory(ctx: SnapshotInstance[]): ComponentAtInd
|
|
|
327
347
|
const root = childCtx.__element_root!;
|
|
328
348
|
__AppendElement(list, root);
|
|
329
349
|
const sign = __GetElementUniqueID(root);
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
350
|
+
if (!enableBatchRender) {
|
|
351
|
+
__FlushElementTree(root, {
|
|
352
|
+
triggerLayout: true,
|
|
353
|
+
operationID,
|
|
354
|
+
elementID: sign,
|
|
355
|
+
listID,
|
|
356
|
+
});
|
|
357
|
+
} else if (enableBatchRender && asyncFlush) {
|
|
358
|
+
__FlushElementTree(root, {
|
|
359
|
+
asyncFlush: true,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
336
362
|
signMap.set(sign, childCtx);
|
|
337
363
|
commitMainThreadPatchUpdate(undefined);
|
|
338
364
|
return sign;
|
|
339
365
|
};
|
|
340
|
-
|
|
366
|
+
|
|
367
|
+
const componentAtIndexes = (
|
|
368
|
+
list: FiberElement,
|
|
369
|
+
listID: number,
|
|
370
|
+
cellIndexes: number[],
|
|
371
|
+
operationIDs: number[],
|
|
372
|
+
enableReuseNotification: boolean,
|
|
373
|
+
asyncFlush: boolean,
|
|
374
|
+
) => {
|
|
375
|
+
const uiSigns = cellIndexes.map((cellIndex, index) => {
|
|
376
|
+
const operationID = operationIDs[index] ?? 0;
|
|
377
|
+
return componentAtIndex(list, listID, cellIndex, operationID, enableReuseNotification, true, asyncFlush);
|
|
378
|
+
});
|
|
379
|
+
__FlushElementTree(list, {
|
|
380
|
+
triggerLayout: true,
|
|
381
|
+
operationIDs: operationIDs,
|
|
382
|
+
elementIDs: uiSigns,
|
|
383
|
+
listID,
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
return [componentAtIndex, componentAtIndexes] as const;
|
|
341
387
|
}
|
|
342
388
|
|
|
343
389
|
export function enqueueComponentFactory(): EnqueueComponentCallback {
|
|
@@ -371,11 +417,13 @@ export function snapshotCreateList(
|
|
|
371
417
|
): FiberElement {
|
|
372
418
|
const signMap = new Map<number, SnapshotInstance>();
|
|
373
419
|
const recycleMap = new Map<string, Map<number, SnapshotInstance>>();
|
|
420
|
+
const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory([]);
|
|
374
421
|
const list = __CreateList(
|
|
375
422
|
pageId,
|
|
376
|
-
|
|
423
|
+
componentAtIndex,
|
|
377
424
|
enqueueComponentFactory(),
|
|
378
425
|
{},
|
|
426
|
+
componentAtIndexes,
|
|
379
427
|
);
|
|
380
428
|
const listID = __GetElementUniqueID(list);
|
|
381
429
|
gSignMap[listID] = signMap;
|
package/runtime/src/lynx/env.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// LICENSE file in the root directory of this source tree.
|
|
4
4
|
import type { DataProcessorDefinition } from '../lynx-api.js';
|
|
5
5
|
|
|
6
|
-
export function
|
|
6
|
+
export function setupLynxTestingEnv(): void {
|
|
7
7
|
if (!__LEPUS__) {
|
|
8
8
|
const { initData, updateData } = lynxCoreInject.tt._params;
|
|
9
9
|
// @ts-ignore
|
|
@@ -2,13 +2,17 @@ import { options } from 'preact';
|
|
|
2
2
|
import type { VNode } from 'preact';
|
|
3
3
|
import { COMPONENT, DIFF, DIFFED, FORCE } from '../renderToOpcodes/constants.js';
|
|
4
4
|
|
|
5
|
+
const sForcedVNode = Symbol('FORCE');
|
|
6
|
+
|
|
7
|
+
type PatchedVNode = VNode & { [sForcedVNode]?: true };
|
|
8
|
+
|
|
5
9
|
export function runWithForce(cb: () => void): void {
|
|
6
10
|
// save vnode and its `_component` in WeakMap
|
|
7
11
|
const m = new WeakMap<VNode, any>();
|
|
8
12
|
|
|
9
13
|
const oldDiff = options[DIFF];
|
|
10
14
|
|
|
11
|
-
options[DIFF] = (vnode:
|
|
15
|
+
options[DIFF] = (vnode: PatchedVNode) => {
|
|
12
16
|
if (oldDiff) {
|
|
13
17
|
oldDiff(vnode);
|
|
14
18
|
}
|
|
@@ -28,19 +32,26 @@ export function runWithForce(cb: () => void): void {
|
|
|
28
32
|
return m.get(vnode);
|
|
29
33
|
},
|
|
30
34
|
});
|
|
35
|
+
vnode[sForcedVNode] = true;
|
|
31
36
|
};
|
|
32
37
|
|
|
33
38
|
const oldDiffed = options[DIFFED];
|
|
34
39
|
|
|
35
|
-
options[DIFFED] = (vnode:
|
|
40
|
+
options[DIFFED] = (vnode: PatchedVNode) => {
|
|
36
41
|
if (oldDiffed) {
|
|
37
42
|
oldDiffed(vnode);
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
//
|
|
43
|
-
vnode[
|
|
45
|
+
// There would be cases when `options[DIFF]` has been reset while options[DIFFED] is not,
|
|
46
|
+
// so we need to check if `vnode` is patched by `options[DIFF]`.
|
|
47
|
+
// We only want to change the patched vnode
|
|
48
|
+
if (vnode[sForcedVNode]) {
|
|
49
|
+
// delete is a reverse operation of previous `Object.defineProperty`
|
|
50
|
+
delete vnode[COMPONENT];
|
|
51
|
+
delete vnode[sForcedVNode];
|
|
52
|
+
// restore
|
|
53
|
+
vnode[COMPONENT] = m.get(vnode);
|
|
54
|
+
}
|
|
44
55
|
};
|
|
45
56
|
|
|
46
57
|
try {
|