@lynx-js/react 0.110.0 → 0.111.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/components/lib/DeferredListItem.d.ts +7 -0
- package/components/lib/DeferredListItem.jsx +40 -0
- package/components/lib/DeferredListItem.jsx.map +1 -0
- package/components/lib/Page.js.map +1 -1
- package/components/lib/index.d.ts +1 -0
- package/components/lib/index.js +1 -0
- package/components/lib/index.js.map +1 -1
- package/components/src/DeferredListItem.tsx +56 -0
- package/components/src/Page.ts +1 -1
- package/components/src/index.ts +1 -0
- package/package.json +1 -1
- package/refresh/.turbo/turbo-build.log +1 -1
- package/runtime/lazy/react-lepus.js +1 -0
- package/runtime/lazy/react.js +1 -0
- package/runtime/lepus/index.d.ts +1 -1
- package/runtime/lepus/index.js +44 -0
- package/runtime/lib/backgroundSnapshot.d.ts +2 -1
- package/runtime/lib/backgroundSnapshot.js +64 -41
- package/runtime/lib/backgroundSnapshot.js.map +1 -1
- package/runtime/lib/compat/initData.d.ts +7 -5
- package/runtime/lib/compat/initData.js +11 -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/index.d.ts +2 -2
- package/runtime/lib/index.js +2 -2
- package/runtime/lib/index.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/commit.js +5 -5
- package/runtime/lib/lifecycle/patch/commit.js.map +1 -1
- package/runtime/lib/lifecycle/patch/snapshotPatch.d.ts +9 -9
- package/runtime/lib/lifecycle/patch/snapshotPatch.js +9 -10
- package/runtime/lib/lifecycle/patch/snapshotPatch.js.map +1 -1
- package/runtime/lib/lifecycle/patch/snapshotPatchApply.js.map +1 -1
- package/runtime/lib/lifecycle/patch/updateMainThread.js +11 -12
- 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/lifecycleConstant.d.ts +2 -1
- package/runtime/lib/lifecycleConstant.js +1 -0
- package/runtime/lib/lifecycleConstant.js.map +1 -1
- package/runtime/lib/list.d.ts +2 -46
- package/runtime/lib/list.js +124 -211
- 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 +8 -11
- package/runtime/lib/lynx/calledByNative.js.map +1 -1
- package/runtime/lib/lynx/component.js +11 -14
- package/runtime/lib/lynx/component.js.map +1 -1
- package/runtime/lib/lynx/env.js +1 -2
- package/runtime/lib/lynx/env.js.map +1 -1
- package/runtime/lib/lynx/lazy-bundle.js +48 -21
- package/runtime/lib/lynx/lazy-bundle.js.map +1 -1
- package/runtime/lib/lynx/performance.d.ts +3 -19
- package/runtime/lib/lynx/performance.js +25 -26
- package/runtime/lib/lynx/performance.js.map +1 -1
- package/runtime/lib/lynx/tt.js +10 -5
- package/runtime/lib/lynx/tt.js.map +1 -1
- package/runtime/lib/lynx-api.d.ts +78 -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 +7 -14
- package/runtime/lib/snapshot.js +36 -31
- package/runtime/lib/snapshot.js.map +1 -1
- package/runtime/lib/snapshotInstanceHydrationMap.js.map +1 -1
- package/runtime/lib/utils.d.ts +1 -0
- package/runtime/lib/utils.js +7 -1
- package/runtime/lib/utils.js.map +1 -1
- package/runtime/src/backgroundSnapshot.ts +78 -63
- package/runtime/src/compat/initData.ts +20 -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/index.ts +2 -0
- package/runtime/src/internal.ts +3 -2
- package/runtime/src/legacy-react-runtime/index.ts +1 -1
- package/runtime/src/lifecycle/patch/commit.ts +5 -11
- package/runtime/src/lifecycle/patch/snapshotPatch.ts +9 -9
- package/runtime/src/lifecycle/patch/snapshotPatchApply.ts +1 -1
- package/runtime/src/lifecycle/patch/updateMainThread.ts +11 -12
- package/runtime/src/lifecycle/reload.ts +1 -1
- package/runtime/src/lifecycleConstant.ts +1 -0
- package/runtime/src/list.ts +143 -278
- package/runtime/src/listUpdateInfo.ts +221 -0
- package/runtime/src/lynx/calledByNative.ts +8 -10
- package/runtime/src/lynx/component.ts +17 -29
- package/runtime/src/lynx/env.ts +1 -2
- package/runtime/src/lynx/lazy-bundle.ts +55 -20
- package/runtime/src/lynx/performance.ts +26 -27
- package/runtime/src/lynx/tt.ts +10 -11
- package/runtime/src/lynx-api.ts +81 -2
- 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 +41 -34
- package/runtime/src/snapshotInstanceHydrationMap.ts +1 -1
- package/runtime/src/utils.ts +12 -3
- package/testing-library/dist/env/vitest.js +3 -3
- package/testing-library/dist/index.d.ts +4 -1
- package/testing-library/dist/pure.js +1 -1
- package/testing-library/dist/vitest.config.js +45 -8
- package/testing-library/types/entry.d.ts +3 -2
- package/transform/cjs/main.cjs +4 -0
- package/transform/dist/wasm.cjs +1 -1
- package/types/react.d.ts +21 -1
- package/types/react.docs.d.ts +1 -1
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`.
|
|
@@ -215,6 +218,82 @@ export interface InitData {}
|
|
|
215
218
|
|
|
216
219
|
export { withInitDataInState };
|
|
217
220
|
|
|
221
|
+
/**
|
|
222
|
+
* The data processors that registered with {@link Lynx.registerDataProcessors}.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
*
|
|
226
|
+
* Extending `dataProcessors` interface
|
|
227
|
+
*
|
|
228
|
+
* ```ts
|
|
229
|
+
* import type { DataProcessors as WellKnownDataProcessors } from '@lynx-js/react';
|
|
230
|
+
*
|
|
231
|
+
* declare module '@lynx-js/react' {
|
|
232
|
+
* interface DataProcessors extends WellKnownDataProcessors {
|
|
233
|
+
* foo(bar: string): number;
|
|
234
|
+
* }
|
|
235
|
+
* }
|
|
236
|
+
* ```
|
|
237
|
+
*
|
|
238
|
+
* Then you can use `lynx.registerDataProcessors` with types.
|
|
239
|
+
*
|
|
240
|
+
* ```js
|
|
241
|
+
* lynx.registerDataProcessors({
|
|
242
|
+
* dataProcessors: {
|
|
243
|
+
* foo(bar) {
|
|
244
|
+
* return 1;
|
|
245
|
+
* }
|
|
246
|
+
* }
|
|
247
|
+
* })
|
|
248
|
+
* ```
|
|
249
|
+
*
|
|
250
|
+
* @public
|
|
251
|
+
*/
|
|
252
|
+
export interface DataProcessors {
|
|
253
|
+
/**
|
|
254
|
+
* Optional processor to override screen metrics used by the app
|
|
255
|
+
*
|
|
256
|
+
* @param metrics - The physical screen dimensions in pixels
|
|
257
|
+
*
|
|
258
|
+
* @returns New screen dimensions to be used by the app
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
*
|
|
262
|
+
* ```ts
|
|
263
|
+
* lynx.registerDataProcessors({
|
|
264
|
+
* dataProcessors: {
|
|
265
|
+
* getScreenMetricsOverride: (metrics) => {
|
|
266
|
+
* // Force a specific aspect ratio
|
|
267
|
+
* return {
|
|
268
|
+
* width: metrics.width,
|
|
269
|
+
* height: metrics.width * (16/9)
|
|
270
|
+
* };
|
|
271
|
+
* }
|
|
272
|
+
* }
|
|
273
|
+
* });
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
getScreenMetricsOverride?(metrics: {
|
|
277
|
+
/**
|
|
278
|
+
* The physical pixel width of the screen
|
|
279
|
+
*/
|
|
280
|
+
width: number;
|
|
281
|
+
/**
|
|
282
|
+
* The physical pixel height of the screen
|
|
283
|
+
*/
|
|
284
|
+
height: number;
|
|
285
|
+
}): { width: number; height: number };
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Custom unknown data processors.
|
|
289
|
+
*
|
|
290
|
+
* @remarks
|
|
291
|
+
*
|
|
292
|
+
* You may extends the `DataProcessors` interface for better TypeScript types. See {@link DataProcessors}.
|
|
293
|
+
*/
|
|
294
|
+
[processorName: string]: (...args: any[]) => any;
|
|
295
|
+
}
|
|
296
|
+
|
|
218
297
|
/**
|
|
219
298
|
* Definition of DataProcessor(s)
|
|
220
299
|
* @public
|
|
@@ -235,7 +314,7 @@ export interface DataProcessorDefinition {
|
|
|
235
314
|
*
|
|
236
315
|
* @public
|
|
237
316
|
*/
|
|
238
|
-
dataProcessors?:
|
|
317
|
+
dataProcessors?: DataProcessors;
|
|
239
318
|
}
|
|
240
319
|
|
|
241
320
|
/**
|
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 {
|
package/runtime/src/snapshot.ts
CHANGED
|
@@ -18,23 +18,14 @@ import type { Worklet, WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bin
|
|
|
18
18
|
|
|
19
19
|
import type { BackgroundSnapshotInstance } from './backgroundSnapshot.js';
|
|
20
20
|
import { SnapshotOperation, __globalSnapshotPatch } from './lifecycle/patch/snapshotPatch.js';
|
|
21
|
-
import { ListUpdateInfoRecording
|
|
21
|
+
import { ListUpdateInfoRecording } from './listUpdateInfo.js';
|
|
22
|
+
import { __pendingListUpdates } from './pendingListUpdates.js';
|
|
23
|
+
import { DynamicPartType } from './snapshot/dynamicPartType.js';
|
|
24
|
+
import { snapshotDestroyList } from './snapshot/list.js';
|
|
25
|
+
import type { PlatformInfo } from './snapshot/platformInfo.js';
|
|
22
26
|
import { unref } from './snapshot/ref.js';
|
|
23
27
|
import { isDirectOrDeepEqual } from './utils.js';
|
|
24
28
|
|
|
25
|
-
/**
|
|
26
|
-
* Types of dynamic parts that can be updated in a snapshot
|
|
27
|
-
* These are determined at compile time through static analysis
|
|
28
|
-
*/
|
|
29
|
-
export const enum DynamicPartType {
|
|
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)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
29
|
/**
|
|
39
30
|
* A snapshot definition that contains all the information needed to create and update elements
|
|
40
31
|
* This is generated at compile time through static analysis of the JSX
|
|
@@ -174,10 +165,17 @@ export const backgroundSnapshotInstanceManager: {
|
|
|
174
165
|
return null;
|
|
175
166
|
}
|
|
176
167
|
const spreadKey = res[2];
|
|
177
|
-
if (
|
|
178
|
-
|
|
168
|
+
if (res[1] === '__extraProps') {
|
|
169
|
+
if (spreadKey) {
|
|
170
|
+
return ctx.__extraProps![spreadKey];
|
|
171
|
+
}
|
|
172
|
+
throw new Error('unreachable');
|
|
179
173
|
} else {
|
|
180
|
-
|
|
174
|
+
if (spreadKey) {
|
|
175
|
+
return (ctx.__values![expIndex] as { [spreadKey]: unknown })[spreadKey];
|
|
176
|
+
} else {
|
|
177
|
+
return ctx.__values![expIndex];
|
|
178
|
+
}
|
|
181
179
|
}
|
|
182
180
|
},
|
|
183
181
|
};
|
|
@@ -250,6 +248,7 @@ export interface SerializedSnapshotInstance {
|
|
|
250
248
|
id: number;
|
|
251
249
|
type: string;
|
|
252
250
|
values?: any[] | undefined;
|
|
251
|
+
extraProps?: Record<string, unknown> | undefined;
|
|
253
252
|
children?: SerializedSnapshotInstance[] | undefined;
|
|
254
253
|
}
|
|
255
254
|
|
|
@@ -268,10 +267,11 @@ export class SnapshotInstance {
|
|
|
268
267
|
__snapshot_def: Snapshot;
|
|
269
268
|
__elements?: FiberElement[] | undefined;
|
|
270
269
|
__element_root?: FiberElement | undefined;
|
|
271
|
-
__values?:
|
|
270
|
+
__values?: unknown[] | undefined;
|
|
272
271
|
__current_slot_index = 0;
|
|
273
272
|
__worklet_ref_set?: Set<WorkletRefImpl<any> | Worklet>;
|
|
274
|
-
__listItemPlatformInfo?:
|
|
273
|
+
__listItemPlatformInfo?: PlatformInfo;
|
|
274
|
+
__extraProps?: Record<string, unknown> | undefined;
|
|
275
275
|
|
|
276
276
|
constructor(public type: string, id?: number) {
|
|
277
277
|
this.__snapshot_def = snapshotManager.values.get(type)!;
|
|
@@ -280,7 +280,7 @@ export class SnapshotInstance {
|
|
|
280
280
|
throw new Error('Snapshot not found: ' + type);
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
-
id
|
|
283
|
+
id ??= snapshotInstanceManager.nextId -= 1;
|
|
284
284
|
this.__id = id;
|
|
285
285
|
snapshotInstanceManager.values.set(id, this);
|
|
286
286
|
}
|
|
@@ -589,31 +589,30 @@ export class SnapshotInstance {
|
|
|
589
589
|
}
|
|
590
590
|
|
|
591
591
|
setAttribute(key: string | number, value: any): void {
|
|
592
|
-
const helper = (index: number, oldValue: any, newValue: any) => {
|
|
593
|
-
if (isDirectOrDeepEqual(oldValue, newValue)) {}
|
|
594
|
-
else {
|
|
595
|
-
this.__snapshot_def.update![index]!(this, index, oldValue);
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
|
-
|
|
599
592
|
if (key === 'values') {
|
|
600
593
|
const oldValues = this.__values;
|
|
601
|
-
|
|
594
|
+
const values = value as unknown[];
|
|
595
|
+
this.__values = values;
|
|
602
596
|
if (oldValues) {
|
|
603
|
-
for (let index = 0; index <
|
|
604
|
-
|
|
597
|
+
for (let index = 0; index < values.length; index++) {
|
|
598
|
+
this.callUpdateIfNotDirectOrDeepEqual(index, oldValues[index], values[index]);
|
|
605
599
|
}
|
|
606
600
|
} else {
|
|
607
|
-
for (let index = 0; index <
|
|
608
|
-
|
|
601
|
+
for (let index = 0; index < values.length; index++) {
|
|
602
|
+
this.callUpdateIfNotDirectOrDeepEqual(index, undefined, values[index]);
|
|
609
603
|
}
|
|
610
604
|
}
|
|
611
605
|
return;
|
|
612
606
|
}
|
|
613
607
|
|
|
614
|
-
|
|
608
|
+
if (typeof key === 'string') {
|
|
609
|
+
// for more flexible usage, we allow setting non-indexed attributes
|
|
610
|
+
(this.__extraProps ??= {})[key] = value;
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
|
|
615
614
|
this.__values ??= [];
|
|
616
|
-
|
|
615
|
+
this.callUpdateIfNotDirectOrDeepEqual(key, this.__values[key], this.__values[key] = value);
|
|
617
616
|
}
|
|
618
617
|
|
|
619
618
|
toJSON(): Omit<SerializedSnapshotInstance, 'children'> & { children: SnapshotInstance[] | undefined } {
|
|
@@ -621,7 +620,15 @@ export class SnapshotInstance {
|
|
|
621
620
|
id: this.__id,
|
|
622
621
|
type: this.type,
|
|
623
622
|
values: this.__values,
|
|
623
|
+
extraProps: this.__extraProps,
|
|
624
624
|
children: this.__firstChild ? this.childNodes : undefined,
|
|
625
625
|
};
|
|
626
626
|
}
|
|
627
|
+
|
|
628
|
+
callUpdateIfNotDirectOrDeepEqual(index: number, oldValue: any, newValue: any): void {
|
|
629
|
+
if (isDirectOrDeepEqual(oldValue, newValue)) {}
|
|
630
|
+
else {
|
|
631
|
+
this.__snapshot_def.update![index]!(this, index, oldValue);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
627
634
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* The map is used by the ref system to translate between snapshot instance IDs when
|
|
10
10
|
* operations need to cross the thread boundary during the commit phase.
|
|
11
11
|
*/
|
|
12
|
-
const hydrationMap: Map<number, number> = new Map();
|
|
12
|
+
const hydrationMap: Map<number, number> = new Map<number, number>();
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @internal
|