@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.
Files changed (112) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/components/lib/Page.js.map +1 -1
  3. package/components/src/Page.ts +1 -1
  4. package/package.json +1 -1
  5. package/refresh/.turbo/turbo-build.log +1 -1
  6. package/runtime/lib/backgroundSnapshot.js +2 -1
  7. package/runtime/lib/backgroundSnapshot.js.map +1 -1
  8. package/runtime/lib/compat/initData.d.ts +7 -5
  9. package/runtime/lib/compat/initData.js +1 -2
  10. package/runtime/lib/compat/initData.js.map +1 -1
  11. package/runtime/lib/compat/lynxComponent.js +10 -12
  12. package/runtime/lib/compat/lynxComponent.js.map +1 -1
  13. package/runtime/lib/debug/profile.js +1 -0
  14. package/runtime/lib/debug/profile.js.map +1 -1
  15. package/runtime/lib/gesture/processGestureBagkround.d.ts +1 -1
  16. package/runtime/lib/gesture/processGestureBagkround.js +4 -1
  17. package/runtime/lib/gesture/processGestureBagkround.js.map +1 -1
  18. package/runtime/lib/gesture/types.js.map +1 -1
  19. package/runtime/lib/hooks/useLynxGlobalEventListener.d.ts +1 -1
  20. package/runtime/lib/hydrate.d.ts +1 -1
  21. package/runtime/lib/hydrate.js +5 -4
  22. package/runtime/lib/hydrate.js.map +1 -1
  23. package/runtime/lib/internal.d.ts +3 -2
  24. package/runtime/lib/internal.js +3 -2
  25. package/runtime/lib/internal.js.map +1 -1
  26. package/runtime/lib/legacy-react-runtime/index.js +1 -1
  27. package/runtime/lib/legacy-react-runtime/index.js.map +1 -1
  28. package/runtime/lib/lifecycle/patch/snapshotPatchApply.js.map +1 -1
  29. package/runtime/lib/lifecycle/patch/updateMainThread.js +4 -4
  30. package/runtime/lib/lifecycle/patch/updateMainThread.js.map +1 -1
  31. package/runtime/lib/lifecycle/reload.js +1 -1
  32. package/runtime/lib/lifecycle/reload.js.map +1 -1
  33. package/runtime/lib/list.d.ts +2 -46
  34. package/runtime/lib/list.js +23 -200
  35. package/runtime/lib/list.js.map +1 -1
  36. package/runtime/lib/listUpdateInfo.d.ts +38 -0
  37. package/runtime/lib/listUpdateInfo.js +152 -0
  38. package/runtime/lib/listUpdateInfo.js.map +1 -0
  39. package/runtime/lib/lynx/calledByNative.js +2 -2
  40. package/runtime/lib/lynx/calledByNative.js.map +1 -1
  41. package/runtime/lib/lynx-api.js +3 -0
  42. package/runtime/lib/lynx-api.js.map +1 -1
  43. package/runtime/lib/lynx.js +1 -0
  44. package/runtime/lib/lynx.js.map +1 -1
  45. package/runtime/lib/opcodes.js +2 -1
  46. package/runtime/lib/opcodes.js.map +1 -1
  47. package/runtime/lib/pendingListUpdates.d.ts +6 -0
  48. package/runtime/lib/pendingListUpdates.js +16 -0
  49. package/runtime/lib/pendingListUpdates.js.map +1 -0
  50. package/runtime/lib/renderToOpcodes/index.js +7 -7
  51. package/runtime/lib/renderToOpcodes/index.js.map +1 -1
  52. package/runtime/lib/snapshot/dynamicPartType.d.ts +12 -0
  53. package/runtime/lib/snapshot/dynamicPartType.js +17 -0
  54. package/runtime/lib/snapshot/dynamicPartType.js.map +1 -0
  55. package/runtime/lib/snapshot/gesture.js +3 -0
  56. package/runtime/lib/snapshot/gesture.js.map +1 -1
  57. package/runtime/lib/snapshot/list.d.ts +3 -0
  58. package/runtime/lib/snapshot/list.js +23 -0
  59. package/runtime/lib/snapshot/list.js.map +1 -0
  60. package/runtime/lib/snapshot/platformInfo.d.ts +10 -0
  61. package/runtime/lib/snapshot/platformInfo.js +6 -3
  62. package/runtime/lib/snapshot/platformInfo.js.map +1 -1
  63. package/runtime/lib/snapshot/ref.d.ts +3 -0
  64. package/runtime/lib/snapshot/ref.js.map +1 -1
  65. package/runtime/lib/snapshot/spread.d.ts +2 -2
  66. package/runtime/lib/snapshot/spread.js +4 -5
  67. package/runtime/lib/snapshot/spread.js.map +1 -1
  68. package/runtime/lib/snapshot/workletEvent.js +1 -1
  69. package/runtime/lib/snapshot/workletEvent.js.map +1 -1
  70. package/runtime/lib/snapshot/workletRef.d.ts +1 -1
  71. package/runtime/lib/snapshot/workletRef.js.map +1 -1
  72. package/runtime/lib/snapshot.d.ts +5 -14
  73. package/runtime/lib/snapshot.js +18 -27
  74. package/runtime/lib/snapshot.js.map +1 -1
  75. package/runtime/lib/snapshotInstanceHydrationMap.js.map +1 -1
  76. package/runtime/lib/utils.js +1 -1
  77. package/runtime/lib/utils.js.map +1 -1
  78. package/runtime/src/backgroundSnapshot.ts +4 -8
  79. package/runtime/src/compat/initData.ts +10 -9
  80. package/runtime/src/compat/lynxComponent.ts +12 -13
  81. package/runtime/src/debug/profile.ts +1 -0
  82. package/runtime/src/gesture/processGestureBagkround.ts +5 -1
  83. package/runtime/src/gesture/types.ts +3 -0
  84. package/runtime/src/hooks/useLynxGlobalEventListener.ts +1 -1
  85. package/runtime/src/hydrate.ts +6 -4
  86. package/runtime/src/internal.ts +3 -2
  87. package/runtime/src/legacy-react-runtime/index.ts +1 -1
  88. package/runtime/src/lifecycle/patch/snapshotPatchApply.ts +1 -1
  89. package/runtime/src/lifecycle/patch/updateMainThread.ts +5 -5
  90. package/runtime/src/lifecycle/reload.ts +1 -1
  91. package/runtime/src/list.ts +26 -264
  92. package/runtime/src/listUpdateInfo.ts +221 -0
  93. package/runtime/src/lynx/calledByNative.ts +2 -2
  94. package/runtime/src/lynx-api.ts +4 -1
  95. package/runtime/src/lynx.ts +1 -0
  96. package/runtime/src/opcodes.ts +2 -1
  97. package/runtime/src/pendingListUpdates.ts +18 -0
  98. package/runtime/src/renderToOpcodes/index.ts +7 -7
  99. package/runtime/src/snapshot/dynamicPartType.ts +16 -0
  100. package/runtime/src/snapshot/gesture.ts +3 -0
  101. package/runtime/src/snapshot/list.ts +36 -0
  102. package/runtime/src/snapshot/platformInfo.ts +19 -5
  103. package/runtime/src/snapshot/ref.ts +1 -0
  104. package/runtime/src/snapshot/spread.ts +42 -17
  105. package/runtime/src/snapshot/workletEvent.ts +1 -1
  106. package/runtime/src/snapshot/workletRef.ts +1 -1
  107. package/runtime/src/snapshot.ts +23 -31
  108. package/runtime/src/snapshotInstanceHydrationMap.ts +1 -1
  109. package/runtime/src/utils.ts +3 -3
  110. package/testing-library/dist/env/vitest.js +3 -3
  111. package/testing-library/dist/vitest.config.js +7 -7
  112. package/transform/cjs/main.cjs +4 -0
@@ -11,7 +11,7 @@ export function isDirectOrDeepEqual(a, b) {
11
11
  return false;
12
12
  }
13
13
  export function isEmptyObject(obj) {
14
- for (var _ in obj)
14
+ for (const _ in obj)
15
15
  return false;
16
16
  return true;
17
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,yEAAyE;AACzE,0DAA0D;AAC1D,MAAM,UAAU,mBAAmB,CAAC,CAAM,EAAE,CAAM;IAChD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,OAAO,CAAC,IAAI,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EACnH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,KAAK,IAAI,CAAC,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC;IAChC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,KAAa;IACzD,MAAM,cAAc,GAAW,UAAU,CAAC,cAAc,IAAI,KAAK,CAAC;IAClE,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;AACnG,CAAC;AAED,MAAM,UAAU,IAAI,CAAsC,GAAM,EAAE,IAAiB;IACjF,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YACf,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,yEAAyE;AACzE,0DAA0D;AAC1D,MAAM,UAAU,mBAAmB,CAAC,CAAM,EAAE,CAAM;IAChD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,OAAO,CAAC,IAAI,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EACnH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,KAAK,MAAM,CAAC,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC;IAClC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,KAAa;IACzD,MAAM,cAAc,GAAW,UAAU,CAAC,cAAc,IAAI,KAAK,CAAC;IAClE,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;AACnG,CAAC;AAED,MAAM,UAAU,IAAI,CAAsC,GAAM,EAAE,IAAiB;IACjF,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YACf,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,MAAoB,CAAC;AAC9B,CAAC"}
@@ -22,16 +22,12 @@ import {
22
22
  takeGlobalSnapshotPatch,
23
23
  } from './lifecycle/patch/snapshotPatch.js';
24
24
  import { globalPipelineOptions } from './lynx/performance.js';
25
+ import { DynamicPartType } from './snapshot/dynamicPartType.js';
25
26
  import { clearQueuedRefs, queueRefAttrUpdate } from './snapshot/ref.js';
26
27
  import type { Ref } from './snapshot/ref.js';
27
28
  import { transformSpread } from './snapshot/spread.js';
28
29
  import type { SerializedSnapshotInstance, Snapshot } from './snapshot.js';
29
- import {
30
- DynamicPartType,
31
- backgroundSnapshotInstanceManager,
32
- snapshotManager,
33
- traverseSnapshotInstance,
34
- } from './snapshot.js';
30
+ import { backgroundSnapshotInstanceManager, snapshotManager, traverseSnapshotInstance } from './snapshot.js';
35
31
  import { hydrationMap } from './snapshotInstanceHydrationMap.js';
36
32
  import { isDirectOrDeepEqual } from './utils.js';
37
33
  import { onPostWorkletCtx } from './worklet/ctx.js';
@@ -206,7 +202,7 @@ export class BackgroundSnapshotInstance {
206
202
  for (let index = 0; index < value.length; index++) {
207
203
  const { needUpdate, valueToCommit } = this.setAttributeImpl(value[index], oldValues[index], index);
208
204
  if (needUpdate) {
209
- __globalSnapshotPatch!.push(
205
+ __globalSnapshotPatch.push(
210
206
  SnapshotOperation.SetAttribute,
211
207
  this.__id,
212
208
  index,
@@ -221,7 +217,7 @@ export class BackgroundSnapshotInstance {
221
217
  const { valueToCommit } = this.setAttributeImpl(value[index], null, index);
222
218
  patch[index] = valueToCommit;
223
219
  }
224
- __globalSnapshotPatch!.push(
220
+ __globalSnapshotPatch.push(
225
221
  SnapshotOperation.SetAttributes,
226
222
  this.__id,
227
223
  patch,
@@ -1,7 +1,8 @@
1
1
  // Copyright 2024 The Lynx Authors. All rights reserved.
2
2
  // Licensed under the Apache License Version 2.0 that can be found in the
3
3
  // LICENSE file in the root directory of this source tree.
4
- import type { ComponentClass, Consumer, Context, FC, PropsWithChildren, ReactNode } from 'react';
4
+ import type { ComponentChildren, Consumer, Context, Provider } from 'preact';
5
+ import type { ComponentClass } from 'react';
5
6
 
6
7
  import { useLynxGlobalEventListener } from '../hooks/useLynxGlobalEventListener.js';
7
8
 
@@ -11,23 +12,24 @@ type Getter<T> = {
11
12
 
12
13
  // for better reuse if runtime is changed
13
14
  export function factory<Data>(
14
- { createContext, useState, createElement, useLynxGlobalEventListener: useListener }: typeof import('react') & {
15
+ { createContext, useState, createElement, useLynxGlobalEventListener: useListener }: {
16
+ createContext: typeof import('preact').createContext;
17
+ useState: typeof import('preact/hooks').useState;
18
+ createElement: typeof import('preact').createElement;
15
19
  useLynxGlobalEventListener: typeof useLynxGlobalEventListener;
16
20
  },
17
21
  prop: '__globalProps' | '__initData',
18
22
  eventName: string,
19
23
  ): Getter<{
20
24
  Context: Context<Data>;
21
- Provider: FC<{
22
- children?: ReactNode | undefined;
23
- }>;
25
+ Provider: Provider<Data>;
24
26
  Consumer: Consumer<Data>;
25
27
  use: () => Data;
26
28
  useChanged: (callback: (data: Data) => void) => void;
27
29
  }> {
28
30
  const Context = createContext({} as Data);
29
31
 
30
- const Provider: FC<PropsWithChildren> = ({ children }) => {
32
+ const Provider = ({ children }: { children?: ComponentChildren }) => {
31
33
  const [__, set] = useState<Data>(lynx[prop] as Data);
32
34
 
33
35
  const handleChange = () => {
@@ -58,8 +60,7 @@ export function factory<Data>(
58
60
 
59
61
  const useChanged = (callback: (__: Data) => void) => {
60
62
  if (!__LEPUS__) {
61
- // @ts-ignore
62
- useListener<(__: unknown) => void>(eventName, callback);
63
+ useListener(eventName, callback);
63
64
  }
64
65
  };
65
66
 
@@ -98,7 +99,7 @@ export function factory<Data>(
98
99
  * @public
99
100
  */
100
101
  export function withInitDataInState<P, S>(App: ComponentClass<P, S>): ComponentClass<P, S> {
101
- const isClassComponent = 'prototype' in App && App.prototype.render;
102
+ const isClassComponent = 'prototype' in App && 'render' in App.prototype;
102
103
  if (!isClassComponent) {
103
104
  // return as-is when not class component
104
105
  return App;
@@ -9,15 +9,15 @@ export function wrapWithLynxComponent(
9
9
  jsxComponent: any,
10
10
  ): ReactNode {
11
11
  const C = jsxComponent.type;
12
- if (typeof C === 'function') {
13
- if (C === ComponentFromReactRuntime || C.prototype instanceof ComponentFromReactRuntime) {
14
- if (jsxSnapshot.length === 1) {
15
- return jsxSnapshot(jsxComponent);
16
- } else {
17
- // spread
18
- if (!jsxComponent.props.removeComponentElement) {
19
- return jsxSnapshot(jsxComponent, takeComponentAttributes(jsxComponent));
20
- }
12
+ if (
13
+ typeof C === 'function' && (C === ComponentFromReactRuntime || C.prototype instanceof ComponentFromReactRuntime)
14
+ ) {
15
+ if (jsxSnapshot.length === 1) {
16
+ return jsxSnapshot(jsxComponent);
17
+ } else {
18
+ // spread
19
+ if (!jsxComponent.props.removeComponentElement) {
20
+ return jsxSnapshot(jsxComponent, takeComponentAttributes(jsxComponent));
21
21
  }
22
22
  }
23
23
  }
@@ -25,8 +25,7 @@ export function wrapWithLynxComponent(
25
25
  }
26
26
 
27
27
  // @ts-expect-error
28
- export class ComponentFromReactRuntime extends Component {
29
- }
28
+ export class ComponentFromReactRuntime extends Component {}
30
29
 
31
30
  const __COMPONENT_ATTRIBUTES__ = /* @__PURE__ */ new Set([
32
31
  'name',
@@ -68,8 +67,8 @@ function takeComponentAttributes(jsxComponent: any): Record<string, any> {
68
67
  || k === 'dataSet'
69
68
  || k === 'data-set'
70
69
  || k === 'removeComponentElement'
71
- || k.match(/^(global-bind|bind|catch|capture-bind|capture-catch)([A-Za-z]+)$/)
72
- || k.match(/^data-([A-Za-z]+)$/)
70
+ || (/^(global-bind|bind|catch|capture-bind|capture-catch)([A-Za-z]+)$/.exec(k))
71
+ || (/^data-([A-Za-z]+)$/.exec(k))
73
72
  ) {
74
73
  attributes[k] = jsxComponent.props[k];
75
74
  delete jsxComponent.props[k];
@@ -29,6 +29,7 @@ export function initProfileHook(): void {
29
29
  const oldRender = options[RENDER];
30
30
  options[RENDER] = function(vnode: VNode & { [COMPONENT]: Component }) {
31
31
  const displayName = getDisplayName(vnode.type as ComponentClass);
32
+ // eslint-disable-next-line @typescript-eslint/unbound-method
32
33
  const originalRender = vnode[COMPONENT].render;
33
34
  vnode[COMPONENT].render = function render(this, props, state, context) {
34
35
  // This __PROFILE__ is used for DCE testing
@@ -1,5 +1,9 @@
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 { GestureTypeInner } from './types.js';
5
+ import type { BaseGesture, ComposedGesture, GestureKind } from './types.js';
1
6
  import { onPostWorkletCtx } from '../worklet/ctx.js';
2
- import { type GestureKind, GestureTypeInner, type ComposedGesture, type BaseGesture } from './types.js';
3
7
 
4
8
  export function processGestureBackground(gesture: GestureKind): void {
5
9
  if (gesture.type === GestureTypeInner.COMPOSED) {
@@ -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 type { Worklet } from '@lynx-js/react/worklet-runtime/bindings';
2
5
 
3
6
  export enum GestureTypeInner {
@@ -31,7 +31,7 @@ import { useEffect, useMemo, useRef } from 'preact/hooks';
31
31
  * @param listener - Event handler
32
32
  * @public
33
33
  */
34
- export function useLynxGlobalEventListener<T extends (...args: unknown[]) => void>(
34
+ export function useLynxGlobalEventListener<T extends (...args: any[]) => void>(
35
35
  eventName: string,
36
36
  listener: T,
37
37
  ): void {
@@ -2,9 +2,11 @@
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 { __pendingListUpdates, componentAtIndexFactory, enqueueComponentFactory } from './list.js';
5
+ import { componentAtIndexFactory, enqueueComponentFactory } from './list.js';
6
+ import { __pendingListUpdates } from './pendingListUpdates.js';
7
+ import { DynamicPartType } from './snapshot/dynamicPartType.js';
6
8
  import { unref } from './snapshot/ref.js';
7
- import { DynamicPartType, SnapshotInstance } from './snapshot.js';
9
+ import type { SnapshotInstance } from './snapshot.js';
8
10
  import { isEmptyObject } from './utils.js';
9
11
 
10
12
  export interface DiffResult<K> {
@@ -215,7 +217,7 @@ export function hydrate(before: SnapshotInstance, after: SnapshotInstance, optio
215
217
  }
216
218
 
217
219
  let swap;
218
- if (swap = options?.swap) {
220
+ if ((swap = options?.swap)) {
219
221
  swap[before.__id] = after.__id;
220
222
  }
221
223
 
@@ -349,7 +351,7 @@ export function hydrate(before: SnapshotInstance, after: SnapshotInstance, optio
349
351
 
350
352
  const listElement = before.__elements![elementIndex]!;
351
353
  __SetAttribute(listElement, 'update-list-info', info);
352
- const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory(afterChildNodes);
354
+ const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory(afterChildNodes, hydrate);
353
355
  __UpdateListCallbacks(
354
356
  listElement,
355
357
  componentAtIndex,
@@ -9,10 +9,11 @@ import './lynx.js';
9
9
 
10
10
  import { factory as factory2 } from './compat/componentIs.js';
11
11
  import { useMemo } from './hooks/react.js';
12
- import { snapshotCreateList } from './list.js';
13
12
  import { loadLazyBundle } from './lynx/lazy-bundle.js';
14
13
  import { __root } from './root.js';
15
- import { DynamicPartType, SnapshotInstance, __page, __pageId, createSnapshot, snapshotManager } from './snapshot.js';
14
+ import { DynamicPartType } from './snapshot/dynamicPartType.js';
15
+ import { snapshotCreateList } from './snapshot/list.js';
16
+ import { SnapshotInstance, __page, __pageId, createSnapshot, snapshotManager } from './snapshot.js';
16
17
 
17
18
  export { __page, __pageId, __root };
18
19
 
@@ -8,7 +8,7 @@ import {
8
8
  ComponentFromReactRuntime as Component,
9
9
  ComponentFromReactRuntime as PureComponent,
10
10
  } from '../compat/lynxComponent.js';
11
- import { useCallback, useEffect, useMemo, useReducer, useState, useRef } from '../hooks/react.js';
11
+ import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from '../hooks/react.js';
12
12
 
13
13
  /* v8 ignore next 3 */
14
14
  function __runInJS<T>(value: T): T | undefined | null {
@@ -16,6 +16,7 @@
16
16
  import { sendCtxNotFoundEventToBackground } from './error.js';
17
17
  import type { SnapshotPatch } from './snapshotPatch.js';
18
18
  import { SnapshotOperation } from './snapshotPatch.js';
19
+ import type { DynamicPartType } from '../../snapshot/dynamicPartType.js';
19
20
  import {
20
21
  SnapshotInstance,
21
22
  createSnapshot,
@@ -23,7 +24,6 @@ import {
23
24
  snapshotInstanceManager,
24
25
  snapshotManager,
25
26
  } from '../../snapshot.js';
26
- import type { DynamicPartType } from '../../snapshot.js';
27
27
 
28
28
  /**
29
29
  * Applies a patch of snapshot operations to the main thread.
@@ -4,15 +4,15 @@
4
4
 
5
5
  import { updateWorkletRefInitValueChanges } from '@lynx-js/react/worklet-runtime/bindings';
6
6
 
7
- import { LifecycleConstant } from '../../lifecycleConstant.js';
8
- import { __pendingListUpdates } from '../../list.js';
9
- import { PerformanceTimingKeys, markTiming, setPipeline } from '../../lynx/performance.js';
10
- import { __page } from '../../snapshot.js';
11
- import { getReloadVersion } from '../pass.js';
12
7
  import type { PatchList, PatchOptions } from './commit.js';
13
8
  import { setMainThreadHydrationFinished } from './isMainThreadHydrationFinished.js';
14
9
  import { snapshotPatchApply } from './snapshotPatchApply.js';
10
+ import { LifecycleConstant } from '../../lifecycleConstant.js';
11
+ import { PerformanceTimingKeys, markTiming, setPipeline } from '../../lynx/performance.js';
12
+ import { __pendingListUpdates } from '../../pendingListUpdates.js';
15
13
  import { applyRefQueue } from '../../snapshot/workletRef.js';
14
+ import { __page } from '../../snapshot.js';
15
+ import { getReloadVersion } from '../pass.js';
16
16
 
17
17
  function updateMainThread(
18
18
  { data, patchOptions }: {
@@ -11,7 +11,7 @@ import { render } from 'preact';
11
11
 
12
12
  import { hydrate } from '../hydrate.js';
13
13
  import { LifecycleConstant } from '../lifecycleConstant.js';
14
- import { __pendingListUpdates } from '../list.js';
14
+ import { __pendingListUpdates } from '../pendingListUpdates.js';
15
15
  import { __root, setRoot } from '../root.js';
16
16
  import { destroyBackground } from './destroy.js';
17
17
  import { applyRefQueue } from '../snapshot/workletRef.js';
@@ -1,240 +1,12 @@
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';
5
4
  import { applyRefQueue } from './snapshot/workletRef.js';
6
- import { type SnapshotInstance } from './snapshot.js';
7
-
8
- export interface ListUpdateInfo {
9
- flush(): void;
10
- onInsertBefore(
11
- newNode: SnapshotInstance,
12
- existingNode?: SnapshotInstance,
13
- ): void;
14
- onRemoveChild(child: SnapshotInstance): void;
15
- onSetAttribute(child: SnapshotInstance, attr: any, oldAttr: any): void;
16
- }
17
-
18
- interface InsertAction {
19
- position: number;
20
- type: string;
21
- }
22
-
23
- interface UpdateAction {
24
- from: number;
25
- to: number;
26
- type: string;
27
- flush: boolean;
28
- }
29
-
30
- interface ListOperations {
31
- insertAction: InsertAction[];
32
- removeAction: number[];
33
- updateAction: UpdateAction[];
34
- }
35
-
36
- // class ListUpdateInfoDiffing implements ListUpdateInfo {
37
- // private oldChildNodes: SnapshotInstance[];
38
- // constructor(private list: SnapshotInstance) {
39
- // this.oldChildNodes = list.childNodes;
40
- // }
41
- // flush(): void {
42
- // Object.defineProperty(SnapshotInstance.prototype, "key", {
43
- // get: function () {
44
- // return this.values[0]["item-key"];
45
- // },
46
- // });
47
-
48
- // }
49
- // onInsertBefore(newNode: SnapshotInstance, existingNode?: SnapshotInstance | undefined): void {}
50
- // onRemoveChild(child: SnapshotInstance): void {}
51
- // onSetAttribute(child: SnapshotInstance, attr: any): void {
52
- // throw new Error("Method not implemented.");
53
- // }
54
- // }
55
-
56
- export class ListUpdateInfoRecording implements ListUpdateInfo {
57
- constructor(private list: SnapshotInstance) {
58
- this.oldChildNodes = list.childNodes;
59
- // this.oldChildNodesSet = new Set(this.oldChildNodes);
60
- }
61
-
62
- // private __commitAndReset() {
63
- // (this.__pendingAttributes ??= []).push(this.__toAttribute());
64
- // this.oldChildNodes = this.list.childNodes;
65
- // this.oldChildNodesSet = new Set(this.oldChildNodes);
66
- // this.removeChild1.clear();
67
- // this.removeChild2.clear();
68
- // this.insertBefore.clear();
69
- // this.appendChild.length = 0;
70
- // this.platformInfoUpdate.clear();
71
- // }
72
-
73
- flush(): void {
74
- const elementIndex = this.list.__snapshot_def.slot[0]![1];
75
- const listElement = this.list.__elements![elementIndex]!;
76
- // this.__pendingAttributes?.forEach(pendingAttribute => {
77
- // __SetAttribute(listElement, "update-list-info", pendingAttribute);
78
- // __FlushElementTree(listElement);
79
- // });
80
- __SetAttribute(listElement, 'update-list-info', this.__toAttribute());
81
- const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory(this.list.childNodes);
82
- __UpdateListCallbacks(
83
- listElement,
84
- componentAtIndex,
85
- enqueueComponentFactory(),
86
- componentAtIndexes,
87
- );
88
- }
89
-
90
- private oldChildNodes: SnapshotInstance[];
91
- // private oldChildNodesSet: Set<SnapshotInstance>;
92
- private removeChild = new Set<SnapshotInstance>();
93
- private insertBefore = new Map<SnapshotInstance, SnapshotInstance[]>(); // insert V before K
94
- private appendChild = [] as SnapshotInstance[];
95
- private platformInfoUpdate = new Map<SnapshotInstance, any>();
96
-
97
- onInsertBefore(newNode: SnapshotInstance, existingNode?: SnapshotInstance): void {
98
- // @ts-ignore
99
- if (newNode.__parent) {
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
- });
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
- });
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
- });
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
- });
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
- }
222
-
223
- export const __pendingListUpdates = {
224
- values: {} as Record<number, ListUpdateInfo>,
225
- clear(): void {
226
- this.values = {};
227
- },
228
- flush(): void {
229
- Object.values(this.values).forEach(update => {
230
- update.flush();
231
- });
232
- this.clear();
233
- },
234
- };
5
+ import type { SnapshotInstance } from './snapshot.js';
235
6
 
236
7
  export const gSignMap: Record<number, Map<number, SnapshotInstance>> = {};
237
8
  export const gRecycleMap: Record<number, Map<string, Map<number, SnapshotInstance>>> = {};
9
+ const gParentWeakMap: WeakMap<SnapshotInstance, unknown> = new WeakMap();
238
10
 
239
11
  export function clearListGlobal(): void {
240
12
  for (const key in gSignMap) {
@@ -247,7 +19,25 @@ export function clearListGlobal(): void {
247
19
 
248
20
  export function componentAtIndexFactory(
249
21
  ctx: SnapshotInstance[],
22
+ hydrateFunction: (before: SnapshotInstance, after: SnapshotInstance) => void,
250
23
  ): [ComponentAtIndexCallback, ComponentAtIndexesCallback] {
24
+ // A hack workaround to ensure childCtx has no direct reference through `__parent` to list,
25
+ // to avoid memory leak.
26
+ // TODO(hzy): make `__parent` a WeakRef or `#__parent` in the future.
27
+ ctx.forEach((childCtx) => {
28
+ if (gParentWeakMap.has(childCtx)) {
29
+ // do it only once
30
+ } else {
31
+ gParentWeakMap.set(childCtx, childCtx.parentNode!);
32
+ Object.defineProperty(childCtx, '__parent', {
33
+ get: () => gParentWeakMap.get(childCtx)!,
34
+ set: (value: unknown) => {
35
+ gParentWeakMap.set(childCtx, value);
36
+ },
37
+ });
38
+ }
39
+ });
40
+
251
41
  const componentAtIndex = (
252
42
  list: FiberElement,
253
43
  listID: number,
@@ -268,7 +58,7 @@ export function componentAtIndexFactory(
268
58
  throw new Error('childCtx not found');
269
59
  }
270
60
 
271
- const platformInfo = childCtx.__listItemPlatformInfo || {};
61
+ const platformInfo = childCtx.__listItemPlatformInfo ?? {};
272
62
 
273
63
  const uniqID = childCtx.type + (platformInfo['reuse-identifier'] ?? '');
274
64
  const recycleSignMap = recycleMap.get(uniqID);
@@ -308,7 +98,7 @@ export function componentAtIndexFactory(
308
98
  const [first] = recycleSignMap;
309
99
  const [sign, oldCtx] = first!;
310
100
  recycleSignMap.delete(sign);
311
- hydrate(oldCtx, childCtx);
101
+ hydrateFunction(oldCtx, childCtx);
312
102
  oldCtx.unRenderElements();
313
103
  if (!oldCtx.__id) {
314
104
  oldCtx.tearDown();
@@ -325,7 +115,7 @@ export function componentAtIndexFactory(
325
115
  if (enableReuseNotification) {
326
116
  flushOptions.listReuseNotification = {
327
117
  listElement: list,
328
- itemKey: platformInfo['item-key'],
118
+ itemKey: platformInfo['item-key']!,
329
119
  };
330
120
  }
331
121
  __FlushElementTree(root, flushOptions);
@@ -336,7 +126,7 @@ export function componentAtIndexFactory(
336
126
  if (enableReuseNotification) {
337
127
  flushOptions.listReuseNotification = {
338
128
  listElement: list,
339
- itemKey: platformInfo['item-key'],
129
+ itemKey: platformInfo['item-key']!,
340
130
  };
341
131
  }
342
132
  __FlushElementTree(root, flushOptions);
@@ -389,6 +179,7 @@ export function componentAtIndexFactory(
389
179
  }
390
180
 
391
181
  export function enqueueComponentFactory(): EnqueueComponentCallback {
182
+ // eslint-disable-next-line unicorn/consistent-function-scoping
392
183
  const enqueueComponent = (_: FiberElement, listID: number, sign: number) => {
393
184
  const signMap = gSignMap[listID];
394
185
  const recycleMap = gRecycleMap[listID];
@@ -401,7 +192,7 @@ export function enqueueComponentFactory(): EnqueueComponentCallback {
401
192
  return;
402
193
  }
403
194
 
404
- const platformInfo = childCtx.__listItemPlatformInfo || {};
195
+ const platformInfo = childCtx.__listItemPlatformInfo ?? {};
405
196
 
406
197
  const uniqID = childCtx.type + (platformInfo['reuse-identifier'] ?? '');
407
198
  if (!recycleMap.has(uniqID)) {
@@ -411,32 +202,3 @@ export function enqueueComponentFactory(): EnqueueComponentCallback {
411
202
  };
412
203
  return enqueueComponent;
413
204
  }
414
-
415
- export function snapshotCreateList(
416
- pageId: number,
417
- _ctx: SnapshotInstance,
418
- _expIndex: number,
419
- ): FiberElement {
420
- const signMap = new Map<number, SnapshotInstance>();
421
- const recycleMap = new Map<string, Map<number, SnapshotInstance>>();
422
- const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory([]);
423
- const list = __CreateList(
424
- pageId,
425
- componentAtIndex,
426
- enqueueComponentFactory(),
427
- {},
428
- componentAtIndexes,
429
- );
430
- const listID = __GetElementUniqueID(list);
431
- gSignMap[listID] = signMap;
432
- gRecycleMap[listID] = recycleMap;
433
- return list;
434
- }
435
-
436
- export function snapshotDestroyList(si: SnapshotInstance): void {
437
- const [, elementIndex] = si.__snapshot_def.slot[0]!;
438
- const list = si.__elements![elementIndex]!;
439
- const listID = __GetElementUniqueID(list);
440
- delete gSignMap[listID];
441
- delete gRecycleMap[listID];
442
- }