@lynx-js/react 0.110.0 → 0.110.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 +6 -0
- package/components/lib/Page.js.map +1 -1
- package/components/src/Page.ts +1 -1
- package/package.json +1 -1
- package/refresh/.turbo/turbo-build.log +1 -1
- package/runtime/lib/backgroundSnapshot.js +2 -1
- package/runtime/lib/backgroundSnapshot.js.map +1 -1
- package/runtime/lib/compat/initData.d.ts +7 -5
- package/runtime/lib/compat/initData.js +1 -2
- package/runtime/lib/compat/initData.js.map +1 -1
- package/runtime/lib/compat/lynxComponent.js +10 -12
- package/runtime/lib/compat/lynxComponent.js.map +1 -1
- package/runtime/lib/debug/profile.js +1 -0
- package/runtime/lib/debug/profile.js.map +1 -1
- package/runtime/lib/gesture/processGestureBagkround.d.ts +1 -1
- package/runtime/lib/gesture/processGestureBagkround.js +4 -1
- package/runtime/lib/gesture/processGestureBagkround.js.map +1 -1
- package/runtime/lib/gesture/types.js.map +1 -1
- package/runtime/lib/hooks/useLynxGlobalEventListener.d.ts +1 -1
- package/runtime/lib/hydrate.d.ts +1 -1
- package/runtime/lib/hydrate.js +5 -4
- package/runtime/lib/hydrate.js.map +1 -1
- package/runtime/lib/internal.d.ts +3 -2
- package/runtime/lib/internal.js +3 -2
- package/runtime/lib/internal.js.map +1 -1
- package/runtime/lib/legacy-react-runtime/index.js +1 -1
- package/runtime/lib/legacy-react-runtime/index.js.map +1 -1
- package/runtime/lib/lifecycle/patch/snapshotPatchApply.js.map +1 -1
- package/runtime/lib/lifecycle/patch/updateMainThread.js +4 -4
- package/runtime/lib/lifecycle/patch/updateMainThread.js.map +1 -1
- package/runtime/lib/lifecycle/reload.js +1 -1
- package/runtime/lib/lifecycle/reload.js.map +1 -1
- package/runtime/lib/list.d.ts +2 -46
- package/runtime/lib/list.js +23 -200
- package/runtime/lib/list.js.map +1 -1
- package/runtime/lib/listUpdateInfo.d.ts +38 -0
- package/runtime/lib/listUpdateInfo.js +152 -0
- package/runtime/lib/listUpdateInfo.js.map +1 -0
- package/runtime/lib/lynx/calledByNative.js +2 -2
- package/runtime/lib/lynx/calledByNative.js.map +1 -1
- package/runtime/lib/lynx-api.js +3 -0
- package/runtime/lib/lynx-api.js.map +1 -1
- package/runtime/lib/lynx.js +1 -0
- package/runtime/lib/lynx.js.map +1 -1
- package/runtime/lib/opcodes.js +2 -1
- package/runtime/lib/opcodes.js.map +1 -1
- package/runtime/lib/pendingListUpdates.d.ts +6 -0
- package/runtime/lib/pendingListUpdates.js +16 -0
- package/runtime/lib/pendingListUpdates.js.map +1 -0
- package/runtime/lib/renderToOpcodes/index.js +7 -7
- package/runtime/lib/renderToOpcodes/index.js.map +1 -1
- package/runtime/lib/snapshot/dynamicPartType.d.ts +12 -0
- package/runtime/lib/snapshot/dynamicPartType.js +17 -0
- package/runtime/lib/snapshot/dynamicPartType.js.map +1 -0
- package/runtime/lib/snapshot/gesture.js +3 -0
- package/runtime/lib/snapshot/gesture.js.map +1 -1
- package/runtime/lib/snapshot/list.d.ts +3 -0
- package/runtime/lib/snapshot/list.js +23 -0
- package/runtime/lib/snapshot/list.js.map +1 -0
- package/runtime/lib/snapshot/platformInfo.d.ts +10 -0
- package/runtime/lib/snapshot/platformInfo.js +6 -3
- package/runtime/lib/snapshot/platformInfo.js.map +1 -1
- package/runtime/lib/snapshot/ref.d.ts +3 -0
- package/runtime/lib/snapshot/ref.js.map +1 -1
- package/runtime/lib/snapshot/spread.d.ts +2 -2
- package/runtime/lib/snapshot/spread.js +4 -5
- package/runtime/lib/snapshot/spread.js.map +1 -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 +1 -1
- package/runtime/lib/snapshot/workletRef.js.map +1 -1
- package/runtime/lib/snapshot.d.ts +5 -14
- package/runtime/lib/snapshot.js +18 -27
- package/runtime/lib/snapshot.js.map +1 -1
- package/runtime/lib/snapshotInstanceHydrationMap.js.map +1 -1
- package/runtime/lib/utils.js +1 -1
- package/runtime/lib/utils.js.map +1 -1
- package/runtime/src/backgroundSnapshot.ts +4 -8
- package/runtime/src/compat/initData.ts +10 -9
- package/runtime/src/compat/lynxComponent.ts +12 -13
- package/runtime/src/debug/profile.ts +1 -0
- package/runtime/src/gesture/processGestureBagkround.ts +5 -1
- package/runtime/src/gesture/types.ts +3 -0
- package/runtime/src/hooks/useLynxGlobalEventListener.ts +1 -1
- package/runtime/src/hydrate.ts +6 -4
- package/runtime/src/internal.ts +3 -2
- package/runtime/src/legacy-react-runtime/index.ts +1 -1
- package/runtime/src/lifecycle/patch/snapshotPatchApply.ts +1 -1
- package/runtime/src/lifecycle/patch/updateMainThread.ts +5 -5
- package/runtime/src/lifecycle/reload.ts +1 -1
- package/runtime/src/list.ts +26 -264
- package/runtime/src/listUpdateInfo.ts +221 -0
- package/runtime/src/lynx/calledByNative.ts +2 -2
- package/runtime/src/lynx-api.ts +4 -1
- package/runtime/src/lynx.ts +1 -0
- package/runtime/src/opcodes.ts +2 -1
- package/runtime/src/pendingListUpdates.ts +18 -0
- package/runtime/src/renderToOpcodes/index.ts +7 -7
- package/runtime/src/snapshot/dynamicPartType.ts +16 -0
- package/runtime/src/snapshot/gesture.ts +3 -0
- package/runtime/src/snapshot/list.ts +36 -0
- package/runtime/src/snapshot/platformInfo.ts +19 -5
- package/runtime/src/snapshot/ref.ts +1 -0
- package/runtime/src/snapshot/spread.ts +42 -17
- package/runtime/src/snapshot/workletEvent.ts +1 -1
- package/runtime/src/snapshot/workletRef.ts +1 -1
- package/runtime/src/snapshot.ts +23 -31
- package/runtime/src/snapshotInstanceHydrationMap.ts +1 -1
- package/runtime/src/utils.ts +3 -3
- package/testing-library/dist/env/vitest.js +3 -3
- package/testing-library/dist/vitest.config.js +7 -7
- package/transform/cjs/main.cjs +4 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// Copyright 2025 The Lynx Authors. All rights reserved.
|
|
2
|
+
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
|
+
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
|
|
5
|
+
import { hydrate } from './hydrate.js';
|
|
6
|
+
import { componentAtIndexFactory, enqueueComponentFactory } from './list.js';
|
|
7
|
+
import type { SnapshotInstance } from './snapshot.js';
|
|
8
|
+
|
|
9
|
+
export interface ListUpdateInfo {
|
|
10
|
+
flush(): void;
|
|
11
|
+
onInsertBefore(
|
|
12
|
+
newNode: SnapshotInstance,
|
|
13
|
+
existingNode?: SnapshotInstance,
|
|
14
|
+
): void;
|
|
15
|
+
onRemoveChild(child: SnapshotInstance): void;
|
|
16
|
+
onSetAttribute(child: SnapshotInstance, attr: any, oldAttr: any): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// class ListUpdateInfoDiffing implements ListUpdateInfo {
|
|
20
|
+
// private oldChildNodes: SnapshotInstance[];
|
|
21
|
+
// constructor(private list: SnapshotInstance) {
|
|
22
|
+
// this.oldChildNodes = list.childNodes;
|
|
23
|
+
// }
|
|
24
|
+
// flush(): void {
|
|
25
|
+
// Object.defineProperty(SnapshotInstance.prototype, "key", {
|
|
26
|
+
// get: function () {
|
|
27
|
+
// return this.values[0]["item-key"];
|
|
28
|
+
// },
|
|
29
|
+
// });
|
|
30
|
+
|
|
31
|
+
// }
|
|
32
|
+
// onInsertBefore(newNode: SnapshotInstance, existingNode?: SnapshotInstance | undefined): void {}
|
|
33
|
+
// onRemoveChild(child: SnapshotInstance): void {}
|
|
34
|
+
// onSetAttribute(child: SnapshotInstance, attr: any): void {
|
|
35
|
+
// throw new Error("Method not implemented.");
|
|
36
|
+
// }
|
|
37
|
+
// }
|
|
38
|
+
|
|
39
|
+
interface InsertAction {
|
|
40
|
+
position: number;
|
|
41
|
+
type: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface UpdateAction {
|
|
45
|
+
from: number;
|
|
46
|
+
to: number;
|
|
47
|
+
type: string;
|
|
48
|
+
flush: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface ListOperations {
|
|
52
|
+
insertAction: InsertAction[];
|
|
53
|
+
removeAction: number[];
|
|
54
|
+
updateAction: UpdateAction[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class ListUpdateInfoRecording implements ListUpdateInfo {
|
|
58
|
+
constructor(private list: SnapshotInstance) {
|
|
59
|
+
this.oldChildNodes = list.childNodes;
|
|
60
|
+
// this.oldChildNodesSet = new Set(this.oldChildNodes);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// private __commitAndReset() {
|
|
64
|
+
// (this.__pendingAttributes ??= []).push(this.__toAttribute());
|
|
65
|
+
// this.oldChildNodes = this.list.childNodes;
|
|
66
|
+
// this.oldChildNodesSet = new Set(this.oldChildNodes);
|
|
67
|
+
// this.removeChild1.clear();
|
|
68
|
+
// this.removeChild2.clear();
|
|
69
|
+
// this.insertBefore.clear();
|
|
70
|
+
// this.appendChild.length = 0;
|
|
71
|
+
// this.platformInfoUpdate.clear();
|
|
72
|
+
// }
|
|
73
|
+
|
|
74
|
+
flush(): void {
|
|
75
|
+
const elementIndex = this.list.__snapshot_def.slot[0]![1];
|
|
76
|
+
const listElement = this.list.__elements![elementIndex]!;
|
|
77
|
+
// this.__pendingAttributes?.forEach(pendingAttribute => {
|
|
78
|
+
// __SetAttribute(listElement, "update-list-info", pendingAttribute);
|
|
79
|
+
// __FlushElementTree(listElement);
|
|
80
|
+
// });
|
|
81
|
+
__SetAttribute(listElement, 'update-list-info', this.__toAttribute());
|
|
82
|
+
const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory(this.list.childNodes, hydrate);
|
|
83
|
+
__UpdateListCallbacks(
|
|
84
|
+
listElement,
|
|
85
|
+
componentAtIndex,
|
|
86
|
+
enqueueComponentFactory(),
|
|
87
|
+
componentAtIndexes,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private oldChildNodes: SnapshotInstance[];
|
|
92
|
+
// private oldChildNodesSet: Set<SnapshotInstance>;
|
|
93
|
+
private removeChild = new Set<SnapshotInstance>();
|
|
94
|
+
private insertBefore = new Map<SnapshotInstance, SnapshotInstance[]>(); // insert V before K
|
|
95
|
+
private appendChild = [] as SnapshotInstance[];
|
|
96
|
+
private platformInfoUpdate = new Map<SnapshotInstance, any>();
|
|
97
|
+
|
|
98
|
+
onInsertBefore(newNode: SnapshotInstance, existingNode?: SnapshotInstance): void {
|
|
99
|
+
if (newNode.parentNode) {
|
|
100
|
+
// if (!this.oldChildNodesSet.has(newNode)) {
|
|
101
|
+
// this.__commitAndReset();
|
|
102
|
+
// }
|
|
103
|
+
this.removeChild.add(newNode);
|
|
104
|
+
}
|
|
105
|
+
if (existingNode) {
|
|
106
|
+
// if (!this.oldChildNodesSet.has(existingNode)) {
|
|
107
|
+
// this.__commitAndReset();
|
|
108
|
+
// }
|
|
109
|
+
const newChildren = this.insertBefore.get(existingNode) ?? [];
|
|
110
|
+
newChildren.push(newNode);
|
|
111
|
+
this.insertBefore.set(existingNode, newChildren);
|
|
112
|
+
} else {
|
|
113
|
+
this.appendChild.push(newNode);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
onRemoveChild(child: SnapshotInstance): void {
|
|
118
|
+
// if (!this.oldChildNodesSet.has(child)) {
|
|
119
|
+
// this.__commitAndReset();
|
|
120
|
+
// }
|
|
121
|
+
this.removeChild.add(child);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
onSetAttribute(child: SnapshotInstance, attr: any, _oldAttr: any): void {
|
|
125
|
+
this.platformInfoUpdate.set(child, attr);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private __toAttribute(): ListOperations {
|
|
129
|
+
const { removeChild, insertBefore, appendChild, platformInfoUpdate } = this;
|
|
130
|
+
|
|
131
|
+
const removals: number[] = [];
|
|
132
|
+
const insertions: InsertAction[] = [];
|
|
133
|
+
const updates: UpdateAction[] = [];
|
|
134
|
+
|
|
135
|
+
let j = 0;
|
|
136
|
+
for (let i = 0; i < this.oldChildNodes.length; i++, j++) {
|
|
137
|
+
const child = this.oldChildNodes[i]!;
|
|
138
|
+
if (platformInfoUpdate.has(child)) {
|
|
139
|
+
updates.push({
|
|
140
|
+
...platformInfoUpdate.get(child),
|
|
141
|
+
from: +j,
|
|
142
|
+
to: +j,
|
|
143
|
+
// no flush
|
|
144
|
+
flush: false,
|
|
145
|
+
type: child.type,
|
|
146
|
+
} as UpdateAction);
|
|
147
|
+
}
|
|
148
|
+
if (insertBefore.has(child)) {
|
|
149
|
+
const children = insertBefore.get(child)!;
|
|
150
|
+
children.forEach(c => {
|
|
151
|
+
insertions.push({
|
|
152
|
+
position: j,
|
|
153
|
+
type: c.type,
|
|
154
|
+
...c.__listItemPlatformInfo,
|
|
155
|
+
} as InsertAction);
|
|
156
|
+
j++;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (removeChild.has(child)) {
|
|
160
|
+
removals.push(i);
|
|
161
|
+
removeChild.delete(child);
|
|
162
|
+
j--;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
for (let i = 0; i < appendChild.length; i++) {
|
|
166
|
+
const child = appendChild[i]!;
|
|
167
|
+
insertions.push({
|
|
168
|
+
position: j + i,
|
|
169
|
+
type: child.type,
|
|
170
|
+
...child.__listItemPlatformInfo,
|
|
171
|
+
} as InsertAction);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
insertions.sort((a, b) => a.position - b.position);
|
|
175
|
+
removals.sort((a, b) => a - b);
|
|
176
|
+
|
|
177
|
+
if (
|
|
178
|
+
SystemInfo.lynxSdkVersion === '2.14'
|
|
179
|
+
|| SystemInfo.lynxSdkVersion === '2.15'
|
|
180
|
+
|| SystemInfo.lynxSdkVersion === '2.16'
|
|
181
|
+
|| SystemInfo.lynxSdkVersion === '2.17'
|
|
182
|
+
|| SystemInfo.lynxSdkVersion === '2.18'
|
|
183
|
+
) {
|
|
184
|
+
const elementIndex = this.list.__snapshot_def.slot[0]![1];
|
|
185
|
+
const listElement = this.list.__elements![elementIndex]!;
|
|
186
|
+
|
|
187
|
+
// `__GetAttributeByName` is available since Lynx 2.14
|
|
188
|
+
if (__GetAttributeByName(listElement, 'custom-list-name') === 'list-container') {
|
|
189
|
+
// `updateAction` must be full (not incremental) when Lynx version <= 2.18 and
|
|
190
|
+
// when `custom-list-name` is `list-container` (available when Lynx version >= 2.14) is true,
|
|
191
|
+
updates.length = 0;
|
|
192
|
+
this.list.childNodes.forEach((child, index) => {
|
|
193
|
+
updates.push({
|
|
194
|
+
...child.__listItemPlatformInfo,
|
|
195
|
+
from: index,
|
|
196
|
+
to: index,
|
|
197
|
+
// no flush
|
|
198
|
+
flush: false,
|
|
199
|
+
type: child.type,
|
|
200
|
+
} as UpdateAction);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
insertAction: insertions,
|
|
207
|
+
removeAction: removals,
|
|
208
|
+
updateAction: updates,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
toJSON(): [ListOperations] {
|
|
213
|
+
// if (this.__pendingAttributes) {
|
|
214
|
+
// return [...this.__pendingAttributes, this.__toAttribute()];
|
|
215
|
+
// } else {
|
|
216
|
+
// return [this.__toAttribute()];
|
|
217
|
+
// }
|
|
218
|
+
|
|
219
|
+
return [this.__toAttribute()] as const;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -6,13 +6,13 @@ import { isJSReady, jsReady, jsReadyEventIdSwap, resetJSReady } from '../lifecyc
|
|
|
6
6
|
import { reloadMainThread } from '../lifecycle/reload.js';
|
|
7
7
|
import { renderMainThread } from '../lifecycle/render.js';
|
|
8
8
|
import { LifecycleConstant } from '../lifecycleConstant.js';
|
|
9
|
-
import { __pendingListUpdates } from '../list.js';
|
|
10
9
|
import { ssrHydrateByOpcodes } from '../opcodes.js';
|
|
10
|
+
import { __pendingListUpdates } from '../pendingListUpdates.js';
|
|
11
11
|
import { __root, setRoot } from '../root.js';
|
|
12
|
+
import { applyRefQueue } from '../snapshot/workletRef.js';
|
|
12
13
|
import { SnapshotInstance, __page, setupPage } from '../snapshot.js';
|
|
13
14
|
import { isEmptyObject } from '../utils.js';
|
|
14
15
|
import { PerformanceTimingKeys, markTiming, setPipeline } from './performance.js';
|
|
15
|
-
import { applyRefQueue } from '../snapshot/workletRef.js';
|
|
16
16
|
|
|
17
17
|
function ssrEncode() {
|
|
18
18
|
const { __opcodes } = __root;
|
package/runtime/src/lynx-api.ts
CHANGED
|
@@ -86,6 +86,7 @@ export const root: Root = {
|
|
|
86
86
|
__root.__jsx = jsx;
|
|
87
87
|
} else {
|
|
88
88
|
__root.__jsx = jsx;
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
89
90
|
render(jsx, __root as any);
|
|
90
91
|
if (__FIRST_SCREEN_SYNC_TIMING__ === 'immediately') {
|
|
91
92
|
// This is for cases where `root.render()` is called asynchronously,
|
|
@@ -107,7 +108,7 @@ const _InitData = /* @__PURE__ */ factory<InitData>(
|
|
|
107
108
|
useState,
|
|
108
109
|
createElement,
|
|
109
110
|
useLynxGlobalEventListener,
|
|
110
|
-
}
|
|
111
|
+
},
|
|
111
112
|
'__initData',
|
|
112
113
|
'onDataChanged',
|
|
113
114
|
);
|
|
@@ -137,6 +138,7 @@ const _InitData = /* @__PURE__ */ factory<InitData>(
|
|
|
137
138
|
*
|
|
138
139
|
* @public
|
|
139
140
|
*/
|
|
141
|
+
// @ts-expect-error make preact and react types work
|
|
140
142
|
export const InitDataProvider: FC<{ children?: ReactNode | undefined }> = /* @__PURE__ */ _InitData.Provider();
|
|
141
143
|
/**
|
|
142
144
|
* The {@link https://react.dev/reference/react/createContext#consumer | Consumer} Component that provide `initData`.
|
|
@@ -144,6 +146,7 @@ export const InitDataProvider: FC<{ children?: ReactNode | undefined }> = /* @__
|
|
|
144
146
|
* @group Components
|
|
145
147
|
* @public
|
|
146
148
|
*/
|
|
149
|
+
// @ts-expect-error make preact and react types work
|
|
147
150
|
export const InitDataConsumer: Consumer<InitData> = /* @__PURE__ */ _InitData.Consumer();
|
|
148
151
|
/**
|
|
149
152
|
* A React Hooks for you to get `initData`.
|
package/runtime/src/lynx.ts
CHANGED
|
@@ -42,6 +42,7 @@ if (__PROFILE__) {
|
|
|
42
42
|
|
|
43
43
|
if (__BACKGROUND__) {
|
|
44
44
|
// Trick Preact and TypeScript to accept our custom document adapter.
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
45
46
|
options.document = document as any;
|
|
46
47
|
setupBackgroundDocument();
|
|
47
48
|
injectTt();
|
package/runtime/src/opcodes.ts
CHANGED
|
@@ -1,6 +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 { hydrate } from './hydrate.js';
|
|
4
5
|
import { componentAtIndexFactory, enqueueComponentFactory, gRecycleMap, gSignMap } from './list.js';
|
|
5
6
|
import { CHILDREN } from './renderToOpcodes/constants.js';
|
|
6
7
|
import { SnapshotInstance } from './snapshot.js';
|
|
@@ -52,7 +53,7 @@ export function ssrHydrateByOpcodes(
|
|
|
52
53
|
const signMap = gSignMap[listElementUniqueID] = new Map();
|
|
53
54
|
gRecycleMap[listElementUniqueID] = new Map();
|
|
54
55
|
const enqueueFunc = enqueueComponentFactory();
|
|
55
|
-
const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory(top.childNodes);
|
|
56
|
+
const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory(top.childNodes, hydrate);
|
|
56
57
|
for (const child of top.childNodes) {
|
|
57
58
|
if (child.__element_root) {
|
|
58
59
|
const childElementUniqueID = __GetElementUniqueID(child.__element_root);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Copyright 2025 The Lynx Authors. All rights reserved.
|
|
2
|
+
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
|
+
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
|
|
5
|
+
import type { ListUpdateInfo } from './listUpdateInfo.js';
|
|
6
|
+
|
|
7
|
+
export const __pendingListUpdates = {
|
|
8
|
+
values: {} as Record<number, ListUpdateInfo>,
|
|
9
|
+
clear(): void {
|
|
10
|
+
this.values = {};
|
|
11
|
+
},
|
|
12
|
+
flush(): void {
|
|
13
|
+
Object.values(this.values).forEach(update => {
|
|
14
|
+
update.flush();
|
|
15
|
+
});
|
|
16
|
+
this.clear();
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -97,9 +97,9 @@ export const __OpText = 3;
|
|
|
97
97
|
* @param {Record<string, unknown>} context
|
|
98
98
|
*/
|
|
99
99
|
function renderClassComponent(vnode, context) {
|
|
100
|
-
|
|
100
|
+
const type = /** @type {import("preact").ComponentClass<typeof vnode.props>} */ (vnode.type);
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
const c = new type(vnode.props, context);
|
|
103
103
|
|
|
104
104
|
vnode[COMPONENT] = c;
|
|
105
105
|
c[VNODE] = vnode;
|
|
@@ -162,7 +162,7 @@ function _renderToString(
|
|
|
162
162
|
if (isArray(vnode)) {
|
|
163
163
|
parent[CHILDREN] = vnode;
|
|
164
164
|
for (let i = 0; i < vnode.length; i++) {
|
|
165
|
-
|
|
165
|
+
const child = vnode[i];
|
|
166
166
|
if (child == null || typeof child === 'boolean') continue;
|
|
167
167
|
|
|
168
168
|
_renderToString(child, context, isSvgMode, selectValue, parent, opcodes);
|
|
@@ -190,7 +190,7 @@ function _renderToString(
|
|
|
190
190
|
} else {
|
|
191
191
|
contextType = type.contextType;
|
|
192
192
|
if (contextType != null) {
|
|
193
|
-
|
|
193
|
+
const provider = context[contextType.__c];
|
|
194
194
|
cctx = provider ? provider.props.value : contextType.__;
|
|
195
195
|
}
|
|
196
196
|
|
|
@@ -236,7 +236,7 @@ function _renderToString(
|
|
|
236
236
|
|
|
237
237
|
// When a component returns a Fragment node we flatten it in core, so we
|
|
238
238
|
// need to mirror that logic here too
|
|
239
|
-
|
|
239
|
+
const isTopLevelFragment = rendered != null && rendered.type === Fragment
|
|
240
240
|
&& rendered.key == null;
|
|
241
241
|
rendered = isTopLevelFragment ? rendered.props.children : rendered;
|
|
242
242
|
|
|
@@ -254,8 +254,8 @@ function _renderToString(
|
|
|
254
254
|
|
|
255
255
|
opcodes.push(__OpBegin, vnode);
|
|
256
256
|
|
|
257
|
-
for (
|
|
258
|
-
|
|
257
|
+
for (const name in props) {
|
|
258
|
+
const v = props[name];
|
|
259
259
|
|
|
260
260
|
switch (name) {
|
|
261
261
|
case 'children':
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Copyright 2025 The Lynx Authors. All rights reserved.
|
|
2
|
+
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
|
+
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Types of dynamic parts that can be updated in a snapshot
|
|
7
|
+
* These are determined at compile time through static analysis
|
|
8
|
+
*/
|
|
9
|
+
export const enum DynamicPartType {
|
|
10
|
+
Attr = 0, // Regular attribute updates
|
|
11
|
+
Spread, // Spread operator in JSX
|
|
12
|
+
Slot, // Slot for component children
|
|
13
|
+
Children, // Regular children updates
|
|
14
|
+
ListChildren, // List/array children updates
|
|
15
|
+
MultiChildren, // Multiple children updates (compat layer)
|
|
16
|
+
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Copyright 2025 The Lynx Authors. All rights reserved.
|
|
2
|
+
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
|
+
// LICENSE file in the root directory of this source tree.
|
|
1
4
|
import { processGesture } from '../gesture/processGesture.js';
|
|
2
5
|
import type { GestureKind } from '../gesture/types.js';
|
|
3
6
|
import { isMainThreadHydrationFinished } from '../lifecycle/patch/isMainThreadHydrationFinished.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright 2025 The Lynx Authors. All rights reserved.
|
|
2
|
+
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
|
+
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
|
|
5
|
+
import { hydrate } from '../hydrate.js';
|
|
6
|
+
import { componentAtIndexFactory, enqueueComponentFactory, gRecycleMap, gSignMap } from '../list.js';
|
|
7
|
+
import type { SnapshotInstance } from '../snapshot.js';
|
|
8
|
+
|
|
9
|
+
export function snapshotCreateList(
|
|
10
|
+
pageId: number,
|
|
11
|
+
_ctx: SnapshotInstance,
|
|
12
|
+
_expIndex: number,
|
|
13
|
+
): FiberElement {
|
|
14
|
+
const signMap = new Map<number, SnapshotInstance>();
|
|
15
|
+
const recycleMap = new Map<string, Map<number, SnapshotInstance>>();
|
|
16
|
+
const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory([], hydrate);
|
|
17
|
+
const list = __CreateList(
|
|
18
|
+
pageId,
|
|
19
|
+
componentAtIndex,
|
|
20
|
+
enqueueComponentFactory(),
|
|
21
|
+
{},
|
|
22
|
+
componentAtIndexes,
|
|
23
|
+
);
|
|
24
|
+
const listID = __GetElementUniqueID(list);
|
|
25
|
+
gSignMap[listID] = signMap;
|
|
26
|
+
gRecycleMap[listID] = recycleMap;
|
|
27
|
+
return list;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function snapshotDestroyList(si: SnapshotInstance): void {
|
|
31
|
+
const [, elementIndex] = si.__snapshot_def.slot[0]!;
|
|
32
|
+
const list = si.__elements![elementIndex]!;
|
|
33
|
+
const listID = __GetElementUniqueID(list);
|
|
34
|
+
delete gSignMap[listID];
|
|
35
|
+
delete gRecycleMap[listID];
|
|
36
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
// Copyright 2025 The Lynx Authors. All rights reserved.
|
|
2
|
+
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
|
+
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
import { ListUpdateInfoRecording } from '../listUpdateInfo.js';
|
|
5
|
+
import { __pendingListUpdates } from '../pendingListUpdates.js';
|
|
2
6
|
import { SnapshotInstance } from '../snapshot.js';
|
|
3
7
|
|
|
4
8
|
const platformInfoVirtualAttributes: Set<string> = /* @__PURE__ */ new Set<string>(['reuse-identifier']);
|
|
@@ -13,16 +17,26 @@ const platformInfoAttributes: Set<string> = /* @__PURE__ */ new Set<string>([
|
|
|
13
17
|
'estimated-main-axis-size-px',
|
|
14
18
|
]);
|
|
15
19
|
|
|
20
|
+
export interface PlatformInfo {
|
|
21
|
+
'reuse-identifier'?: string;
|
|
22
|
+
'full-span'?: boolean;
|
|
23
|
+
'item-key'?: string;
|
|
24
|
+
'sticky-top'?: boolean;
|
|
25
|
+
'sticky-bottom'?: boolean;
|
|
26
|
+
'estimated-height'?: number;
|
|
27
|
+
'estimated-height-px'?: number;
|
|
28
|
+
'estimated-main-axis-size-px'?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
function updateListItemPlatformInfo(
|
|
17
32
|
ctx: SnapshotInstance,
|
|
18
33
|
index: number,
|
|
19
34
|
oldValue: any,
|
|
20
35
|
elementIndex: number,
|
|
21
36
|
): void {
|
|
22
|
-
const newValue = ctx.__listItemPlatformInfo = ctx.__values![index];
|
|
37
|
+
const newValue = ctx.__listItemPlatformInfo = ctx.__values![index] as PlatformInfo;
|
|
23
38
|
|
|
24
|
-
|
|
25
|
-
const list = ctx.__parent;
|
|
39
|
+
const list = ctx.parentNode;
|
|
26
40
|
if (list?.__snapshot_def.isListHolder) {
|
|
27
41
|
(__pendingListUpdates.values[list.__id] ??= new ListUpdateInfoRecording(list)).onSetAttribute(
|
|
28
42
|
ctx,
|
|
@@ -35,7 +49,7 @@ function updateListItemPlatformInfo(
|
|
|
35
49
|
// No adding / removing keys.
|
|
36
50
|
if (ctx.__elements) {
|
|
37
51
|
const e = ctx.__elements[elementIndex]!;
|
|
38
|
-
const value = ctx.__values![index]
|
|
52
|
+
const value = ctx.__values![index] as Record<string, unknown>;
|
|
39
53
|
for (const k in value) {
|
|
40
54
|
if (platformInfoVirtualAttributes.has(k)) {
|
|
41
55
|
continue;
|
|
@@ -12,6 +12,7 @@ const refsToApply: (Ref | [snapshotInstanceId: number, expIndex: number])[] = []
|
|
|
12
12
|
|
|
13
13
|
type Ref = (((ref: RefProxy) => () => void) | { current: RefProxy | null }) & {
|
|
14
14
|
_unmount?: () => void;
|
|
15
|
+
__ref?: { value: number };
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
function unref(snapshot: SnapshotInstance, recursive: boolean): void {
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
* optimized attribute updates at compile time, avoiding runtime object spreads.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { Worklet } from '@lynx-js/react/worklet-runtime/bindings';
|
|
12
|
+
import type { Element, Worklet, WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings';
|
|
13
13
|
|
|
14
|
-
import { BackgroundSnapshotInstance } from '../backgroundSnapshot.js';
|
|
15
|
-
import { ListUpdateInfoRecording
|
|
14
|
+
import type { BackgroundSnapshotInstance } from '../backgroundSnapshot.js';
|
|
15
|
+
import { ListUpdateInfoRecording } from '../listUpdateInfo.js';
|
|
16
|
+
import { __pendingListUpdates } from '../pendingListUpdates.js';
|
|
16
17
|
import { SnapshotInstance } from '../snapshot.js';
|
|
17
18
|
import { isDirectOrDeepEqual, isEmptyObject, pick } from '../utils.js';
|
|
18
19
|
import { updateEvent } from './event.js';
|
|
@@ -22,6 +23,7 @@ import { transformRef, updateRef } from './ref.js';
|
|
|
22
23
|
import { updateWorkletEvent } from './workletEvent.js';
|
|
23
24
|
import { updateWorkletRef } from './workletRef.js';
|
|
24
25
|
|
|
26
|
+
// eslint-disable-next-line regexp/no-unused-capturing-group
|
|
25
27
|
const eventRegExp = /^(([A-Za-z-]*):)?(bind|catch|capture-bind|capture-catch|global-bind)([A-Za-z]+)$/;
|
|
26
28
|
const eventTypeMap: Record<string, string> = {
|
|
27
29
|
bind: 'bindEvent',
|
|
@@ -38,12 +40,16 @@ const noFlattenAttributes = /* @__PURE__ */ new Set<string>([
|
|
|
38
40
|
'exposure-id',
|
|
39
41
|
]);
|
|
40
42
|
|
|
41
|
-
function updateSpread(
|
|
43
|
+
function updateSpread(
|
|
44
|
+
snapshot: SnapshotInstance,
|
|
45
|
+
index: number,
|
|
46
|
+
oldValue: Record<string, unknown> | undefined | null,
|
|
47
|
+
elementIndex: number,
|
|
48
|
+
): void {
|
|
42
49
|
oldValue ??= {};
|
|
43
|
-
let newValue: Record<string,
|
|
50
|
+
let newValue: Record<string, unknown> = snapshot.__values![index] as Record<string, unknown>; // compiler guarantee this must be an object;
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
const list = snapshot.__parent;
|
|
52
|
+
const list = snapshot.parentNode;
|
|
47
53
|
if (list?.__snapshot_def.isListHolder) {
|
|
48
54
|
const oldPlatformInfo = pick(oldValue, platformInfoAttributes);
|
|
49
55
|
const platformInfo = pick(newValue, platformInfoAttributes);
|
|
@@ -79,17 +85,17 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
|
|
|
79
85
|
snapshot.__values![index] = newValue;
|
|
80
86
|
}
|
|
81
87
|
|
|
82
|
-
const dataset: Record<string,
|
|
88
|
+
const dataset: Record<string, unknown> = {};
|
|
83
89
|
let match: RegExpMatchArray | null = null;
|
|
84
90
|
for (const key in newValue) {
|
|
85
91
|
const v = newValue[key];
|
|
86
92
|
if (v !== oldValue[key]) {
|
|
87
93
|
if (key === 'className') {
|
|
88
|
-
__SetClasses(snapshot.__elements[elementIndex]!, v);
|
|
94
|
+
__SetClasses(snapshot.__elements[elementIndex]!, v as string);
|
|
89
95
|
} else if (key === 'style') {
|
|
90
|
-
__SetInlineStyles(snapshot.__elements[elementIndex]!, v);
|
|
96
|
+
__SetInlineStyles(snapshot.__elements[elementIndex]!, v as string);
|
|
91
97
|
} else if (key === 'id') {
|
|
92
|
-
__SetID(snapshot.__elements[elementIndex]!, v);
|
|
98
|
+
__SetID(snapshot.__elements[elementIndex]!, v as string);
|
|
93
99
|
} else if (key.startsWith('data-')) {
|
|
94
100
|
// collected below
|
|
95
101
|
} else if (key === 'ref') {
|
|
@@ -106,7 +112,7 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
|
|
|
106
112
|
__id: snapshot.__id,
|
|
107
113
|
__elements: snapshot.__elements,
|
|
108
114
|
} as SnapshotInstance;
|
|
109
|
-
updateRef(fakeSnapshot, index, oldValue[key], elementIndex);
|
|
115
|
+
updateRef(fakeSnapshot, index, oldValue[key] as string | null, elementIndex);
|
|
110
116
|
} else if (key.endsWith(':ref')) {
|
|
111
117
|
snapshot.__worklet_ref_set ??= new Set();
|
|
112
118
|
const fakeSnapshot = {
|
|
@@ -119,7 +125,13 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
|
|
|
119
125
|
__elements: snapshot.__elements,
|
|
120
126
|
__worklet_ref_set: snapshot.__worklet_ref_set,
|
|
121
127
|
} as SnapshotInstance;
|
|
122
|
-
updateWorkletRef(
|
|
128
|
+
updateWorkletRef(
|
|
129
|
+
fakeSnapshot,
|
|
130
|
+
index,
|
|
131
|
+
oldValue[key] as WorkletRefImpl<Element> | Worklet | null | undefined,
|
|
132
|
+
elementIndex,
|
|
133
|
+
key.slice(0, -4),
|
|
134
|
+
);
|
|
123
135
|
} else if (key.endsWith(':gesture')) {
|
|
124
136
|
const workletType = key.slice(0, -8);
|
|
125
137
|
const fakeSnapshot = {
|
|
@@ -200,7 +212,7 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
|
|
|
200
212
|
__id: snapshot.__id,
|
|
201
213
|
__elements: snapshot.__elements,
|
|
202
214
|
} as SnapshotInstance;
|
|
203
|
-
updateRef(fakeSnapshot, index, oldValue[key], elementIndex);
|
|
215
|
+
updateRef(fakeSnapshot, index, oldValue[key] as string | null, elementIndex);
|
|
204
216
|
} else if (key.endsWith(':ref')) {
|
|
205
217
|
snapshot.__worklet_ref_set ??= new Set();
|
|
206
218
|
const fakeSnapshot = {
|
|
@@ -213,7 +225,13 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
|
|
|
213
225
|
__elements: snapshot.__elements,
|
|
214
226
|
__worklet_ref_set: snapshot.__worklet_ref_set,
|
|
215
227
|
} as SnapshotInstance;
|
|
216
|
-
updateWorkletRef(
|
|
228
|
+
updateWorkletRef(
|
|
229
|
+
fakeSnapshot,
|
|
230
|
+
index,
|
|
231
|
+
oldValue[key] as WorkletRefImpl<Element> | Worklet | null | undefined,
|
|
232
|
+
elementIndex,
|
|
233
|
+
key.slice(0, -4),
|
|
234
|
+
);
|
|
217
235
|
} else if (key.endsWith(':gesture')) {
|
|
218
236
|
const workletType = key.slice(0, -8);
|
|
219
237
|
const fakeSnapshot = {
|
|
@@ -243,7 +261,15 @@ function updateSpread(snapshot: SnapshotInstance, index: number, oldValue: any,
|
|
|
243
261
|
__elements: snapshot.__elements,
|
|
244
262
|
} as SnapshotInstance;
|
|
245
263
|
if (workletType) {
|
|
246
|
-
updateWorkletEvent(
|
|
264
|
+
updateWorkletEvent(
|
|
265
|
+
fakeSnapshot,
|
|
266
|
+
index,
|
|
267
|
+
oldValue[key] as Worklet,
|
|
268
|
+
elementIndex,
|
|
269
|
+
workletType,
|
|
270
|
+
eventType,
|
|
271
|
+
eventName,
|
|
272
|
+
);
|
|
247
273
|
} else {
|
|
248
274
|
updateEvent(fakeSnapshot, index, oldValue[key], elementIndex, eventType, eventName, key);
|
|
249
275
|
}
|
|
@@ -283,7 +309,6 @@ function transformSpread(
|
|
|
283
309
|
if (__LEPUS__) {
|
|
284
310
|
result[key] = value ? 1 : undefined;
|
|
285
311
|
} else {
|
|
286
|
-
// @ts-ignore
|
|
287
312
|
result[key] = transformRef(value)?.__ref;
|
|
288
313
|
}
|
|
289
314
|
} else if (typeof value === 'function') {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { onWorkletCtxUpdate } from '@lynx-js/react/worklet-runtime/bindings';
|
|
5
5
|
import type { Worklet } from '@lynx-js/react/worklet-runtime/bindings';
|
|
6
6
|
|
|
7
|
-
import { SnapshotInstance } from '../snapshot.js';
|
|
8
7
|
import { isMainThreadHydrationFinished } from '../lifecycle/patch/isMainThreadHydrationFinished.js';
|
|
8
|
+
import { SnapshotInstance } from '../snapshot.js';
|
|
9
9
|
|
|
10
10
|
function updateWorkletEvent(
|
|
11
11
|
snapshot: SnapshotInstance,
|
|
@@ -43,7 +43,7 @@ export function workletUnRef(value: Worklet | WorkletRefImpl<Element>): void {
|
|
|
43
43
|
export function updateWorkletRef(
|
|
44
44
|
snapshot: SnapshotInstance,
|
|
45
45
|
expIndex: number,
|
|
46
|
-
oldValue: WorkletRefImpl<Element> | Worklet | undefined,
|
|
46
|
+
oldValue: WorkletRefImpl<Element> | Worklet | null | undefined,
|
|
47
47
|
elementIndex: number,
|
|
48
48
|
_workletType: string,
|
|
49
49
|
): void {
|