@lynx-js/react 0.110.1 → 0.111.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 (89) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/components/lib/DeferredListItem.d.ts +7 -0
  3. package/components/lib/DeferredListItem.jsx +40 -0
  4. package/components/lib/DeferredListItem.jsx.map +1 -0
  5. package/components/lib/index.d.ts +1 -0
  6. package/components/lib/index.js +1 -0
  7. package/components/lib/index.js.map +1 -1
  8. package/components/src/DeferredListItem.tsx +56 -0
  9. package/components/src/index.ts +1 -0
  10. package/package.json +1 -1
  11. package/refresh/.turbo/turbo-build.log +7 -3
  12. package/refresh/dist/index.js +223 -284
  13. package/refresh/package.json +3 -4
  14. package/refresh/rslib.config.ts +24 -0
  15. package/runtime/lazy/react-lepus.js +1 -0
  16. package/runtime/lazy/react.js +1 -0
  17. package/runtime/lepus/index.d.ts +1 -1
  18. package/runtime/lepus/index.js +44 -0
  19. package/runtime/lib/backgroundSnapshot.d.ts +2 -1
  20. package/runtime/lib/backgroundSnapshot.js +62 -40
  21. package/runtime/lib/backgroundSnapshot.js.map +1 -1
  22. package/runtime/lib/compat/initData.js +10 -0
  23. package/runtime/lib/compat/initData.js.map +1 -1
  24. package/runtime/lib/index.d.ts +2 -2
  25. package/runtime/lib/index.js +2 -2
  26. package/runtime/lib/index.js.map +1 -1
  27. package/runtime/lib/lifecycle/patch/commit.js +5 -5
  28. package/runtime/lib/lifecycle/patch/commit.js.map +1 -1
  29. package/runtime/lib/lifecycle/patch/snapshotPatch.d.ts +9 -9
  30. package/runtime/lib/lifecycle/patch/snapshotPatch.js +9 -10
  31. package/runtime/lib/lifecycle/patch/snapshotPatch.js.map +1 -1
  32. package/runtime/lib/lifecycle/patch/updateMainThread.js +7 -8
  33. package/runtime/lib/lifecycle/patch/updateMainThread.js.map +1 -1
  34. package/runtime/lib/lifecycleConstant.d.ts +2 -1
  35. package/runtime/lib/lifecycleConstant.js +1 -0
  36. package/runtime/lib/lifecycleConstant.js.map +1 -1
  37. package/runtime/lib/list.js +102 -12
  38. package/runtime/lib/list.js.map +1 -1
  39. package/runtime/lib/lynx/calledByNative.js +6 -9
  40. package/runtime/lib/lynx/calledByNative.js.map +1 -1
  41. package/runtime/lib/lynx/component.js +11 -14
  42. package/runtime/lib/lynx/component.js.map +1 -1
  43. package/runtime/lib/lynx/env.js +1 -2
  44. package/runtime/lib/lynx/env.js.map +1 -1
  45. package/runtime/lib/lynx/lazy-bundle.js +48 -21
  46. package/runtime/lib/lynx/lazy-bundle.js.map +1 -1
  47. package/runtime/lib/lynx/performance.d.ts +3 -19
  48. package/runtime/lib/lynx/performance.js +25 -26
  49. package/runtime/lib/lynx/performance.js.map +1 -1
  50. package/runtime/lib/lynx/tt.js +10 -5
  51. package/runtime/lib/lynx/tt.js.map +1 -1
  52. package/runtime/lib/lynx-api.d.ts +78 -1
  53. package/runtime/lib/lynx-api.js.map +1 -1
  54. package/runtime/lib/snapshot.d.ts +2 -0
  55. package/runtime/lib/snapshot.js +19 -5
  56. package/runtime/lib/snapshot.js.map +1 -1
  57. package/runtime/lib/utils.d.ts +1 -0
  58. package/runtime/lib/utils.js +6 -0
  59. package/runtime/lib/utils.js.map +1 -1
  60. package/runtime/src/backgroundSnapshot.ts +74 -55
  61. package/runtime/src/compat/initData.ts +10 -0
  62. package/runtime/src/index.ts +2 -0
  63. package/runtime/src/lifecycle/patch/commit.ts +5 -11
  64. package/runtime/src/lifecycle/patch/snapshotPatch.ts +9 -9
  65. package/runtime/src/lifecycle/patch/updateMainThread.ts +7 -8
  66. package/runtime/src/lifecycleConstant.ts +1 -0
  67. package/runtime/src/list.ts +118 -15
  68. package/runtime/src/lynx/calledByNative.ts +6 -8
  69. package/runtime/src/lynx/component.ts +17 -29
  70. package/runtime/src/lynx/env.ts +1 -2
  71. package/runtime/src/lynx/lazy-bundle.ts +55 -20
  72. package/runtime/src/lynx/performance.ts +26 -27
  73. package/runtime/src/lynx/tt.ts +10 -11
  74. package/runtime/src/lynx-api.ts +77 -1
  75. package/runtime/src/snapshot.ts +20 -5
  76. package/runtime/src/utils.ts +9 -0
  77. package/testing-library/dist/env/vitest.js +6 -3
  78. package/testing-library/dist/index.d.ts +4 -1
  79. package/testing-library/dist/pure.js +4 -2
  80. package/testing-library/dist/vitest-global-setup.js +2 -2
  81. package/testing-library/dist/vitest.config.js +38 -1
  82. package/testing-library/types/entry.d.ts +3 -2
  83. package/transform/dist/wasm.cjs +1 -1
  84. package/types/react.d.ts +21 -1
  85. package/types/react.docs.d.ts +1 -1
  86. package/worklet-runtime/dist/dev.js +403 -506
  87. package/worklet-runtime/dist/main.js +1 -517
  88. package/worklet-runtime/lib/workletRuntime.js +5 -5
  89. package/worklet-runtime/lib/workletRuntime.js.map +1 -1
@@ -23,13 +23,7 @@ import type { VNode } from 'preact';
23
23
  import { options } from 'preact';
24
24
 
25
25
  import { LifecycleConstant } from '../../lifecycleConstant.js';
26
- import {
27
- PerformanceTimingKeys,
28
- globalPipelineOptions,
29
- markTiming,
30
- markTimingLegacy,
31
- setPipeline,
32
- } from '../../lynx/performance.js';
26
+ import { globalPipelineOptions, markTiming, markTimingLegacy, setPipeline } from '../../lynx/performance.js';
33
27
  import { COMMIT } from '../../renderToOpcodes/constants.js';
34
28
  import { applyQueuedRefs } from '../../snapshot/ref.js';
35
29
  import { backgroundSnapshotInstanceManager } from '../../snapshot.js';
@@ -88,8 +82,8 @@ function replaceCommitHook(): void {
88
82
  }
89
83
 
90
84
  // Mark the end of virtual DOM diffing phase for performance tracking
91
- markTimingLegacy(PerformanceTimingKeys.updateDiffVdomEnd);
92
- markTiming(PerformanceTimingKeys.diffVdomEnd);
85
+ markTimingLegacy('updateDiffVdomEnd');
86
+ markTiming('diffVdomEnd');
93
87
 
94
88
  const backgroundSnapshotInstancesToRemove = globalBackgroundSnapshotInstancesToRemove;
95
89
  globalBackgroundSnapshotInstancesToRemove = [];
@@ -167,7 +161,7 @@ function commitPatchUpdate(patchList: PatchList, patchOptions: Omit<PatchOptions
167
161
  if (__PROFILE__) {
168
162
  console.profile('commitChanges');
169
163
  }
170
- markTiming(PerformanceTimingKeys.packChangesStart);
164
+ markTiming('packChangesStart');
171
165
  const obj: {
172
166
  data: string;
173
167
  patchOptions: PatchOptions;
@@ -178,7 +172,7 @@ function commitPatchUpdate(patchList: PatchList, patchOptions: Omit<PatchOptions
178
172
  reloadVersion: getReloadVersion(),
179
173
  },
180
174
  };
181
- markTiming(PerformanceTimingKeys.packChangesEnd);
175
+ markTiming('packChangesEnd');
182
176
  if (globalPipelineOptions) {
183
177
  obj.patchOptions.pipelineOptions = globalPipelineOptions;
184
178
  setPipeline(undefined);
@@ -8,16 +8,16 @@
8
8
  * efficient transmission between threads and application to element tree.
9
9
  */
10
10
 
11
- export const enum SnapshotOperation {
12
- CreateElement,
13
- InsertBefore,
14
- RemoveChild,
15
- SetAttribute,
16
- SetAttributes,
11
+ export const SnapshotOperation = {
12
+ CreateElement: 0,
13
+ InsertBefore: 1,
14
+ RemoveChild: 2,
15
+ SetAttribute: 3,
16
+ SetAttributes: 4,
17
17
 
18
- DEV_ONLY_AddSnapshot = 100,
19
- DEV_ONLY_RegisterWorklet = 101,
20
- }
18
+ DEV_ONLY_AddSnapshot: 100,
19
+ DEV_ONLY_RegisterWorklet: 101,
20
+ } as const;
21
21
 
22
22
  // Operation format definitions:
23
23
  //
@@ -8,7 +8,7 @@ import type { PatchList, PatchOptions } from './commit.js';
8
8
  import { setMainThreadHydrationFinished } from './isMainThreadHydrationFinished.js';
9
9
  import { snapshotPatchApply } from './snapshotPatchApply.js';
10
10
  import { LifecycleConstant } from '../../lifecycleConstant.js';
11
- import { PerformanceTimingKeys, markTiming, setPipeline } from '../../lynx/performance.js';
11
+ import { markTiming, setPipeline } from '../../lynx/performance.js';
12
12
  import { __pendingListUpdates } from '../../pendingListUpdates.js';
13
13
  import { applyRefQueue } from '../../snapshot/workletRef.js';
14
14
  import { __page } from '../../snapshot.js';
@@ -25,12 +25,12 @@ function updateMainThread(
25
25
  }
26
26
 
27
27
  setPipeline(patchOptions.pipelineOptions);
28
- markTiming(PerformanceTimingKeys.mtsRenderStart);
29
- markTiming(PerformanceTimingKeys.parseChangesStart);
28
+ markTiming('mtsRenderStart');
29
+ markTiming('parseChangesStart');
30
30
  const { patchList, flushOptions = {} } = JSON.parse(data) as PatchList;
31
31
 
32
- markTiming(PerformanceTimingKeys.parseChangesEnd);
33
- markTiming(PerformanceTimingKeys.patchChangesStart);
32
+ markTiming('parseChangesEnd');
33
+ markTiming('patchChangesStart');
34
34
 
35
35
  for (const { snapshotPatch, workletRefInitValuePatch } of patchList) {
36
36
  updateWorkletRefInitValueChanges(workletRefInitValuePatch);
@@ -42,8 +42,8 @@ function updateMainThread(
42
42
  // console.debug('********** Lepus updatePatch:');
43
43
  // printSnapshotInstance(snapshotInstanceManager.values.get(-1)!);
44
44
  }
45
- markTiming(PerformanceTimingKeys.patchChangesEnd);
46
- markTiming(PerformanceTimingKeys.mtsRenderEnd);
45
+ markTiming('patchChangesEnd');
46
+ markTiming('mtsRenderEnd');
47
47
  if (patchOptions.isHydration) {
48
48
  setMainThreadHydrationFinished(true);
49
49
  }
@@ -51,7 +51,6 @@ function updateMainThread(
51
51
  if (patchOptions.pipelineOptions) {
52
52
  flushOptions.pipelineOptions = patchOptions.pipelineOptions;
53
53
  }
54
- // TODO: triggerDataUpdated?
55
54
  __FlushElementTree(__page, flushOptions);
56
55
  }
57
56
 
@@ -7,6 +7,7 @@ export const enum LifecycleConstant {
7
7
  globalEventFromLepus = 'globalEventFromLepus',
8
8
  jsReady = 'rLynxJSReady',
9
9
  patchUpdate = 'rLynxChange',
10
+ publishEvent = 'rLynxPublishEvent',
10
11
  }
11
12
 
12
13
  export interface FirstScreenData {
@@ -1,12 +1,15 @@
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 { LifecycleConstant } from './lifecycleConstant.js';
4
5
  import { applyRefQueue } from './snapshot/workletRef.js';
5
6
  import type { SnapshotInstance } from './snapshot.js';
7
+ import { maybePromise } from './utils.js';
6
8
 
7
9
  export const gSignMap: Record<number, Map<number, SnapshotInstance>> = {};
8
10
  export const gRecycleMap: Record<number, Map<string, Map<number, SnapshotInstance>>> = {};
9
11
  const gParentWeakMap: WeakMap<SnapshotInstance, unknown> = new WeakMap();
12
+ const resolvedPromise = /* @__PURE__ */ Promise.resolve();
10
13
 
11
14
  export function clearListGlobal(): void {
12
15
  for (const key in gSignMap) {
@@ -38,10 +41,10 @@ export function componentAtIndexFactory(
38
41
  }
39
42
  });
40
43
 
41
- const componentAtIndex = (
44
+ const componentAtChildCtx = (
42
45
  list: FiberElement,
43
46
  listID: number,
44
- cellIndex: number,
47
+ childCtx: SnapshotInstance,
45
48
  operationID: number,
46
49
  enableReuseNotification: boolean,
47
50
  enableBatchRender: boolean = false,
@@ -53,13 +56,55 @@ export function componentAtIndexFactory(
53
56
  throw new Error('componentAtIndex called on removed list');
54
57
  }
55
58
 
56
- const childCtx = ctx[cellIndex];
57
- if (!childCtx) {
58
- throw new Error('childCtx not found');
59
- }
60
-
61
59
  const platformInfo = childCtx.__listItemPlatformInfo ?? {};
62
60
 
61
+ // The lifecycle of this `__extraProps.isReady`:
62
+ // 0 -> Promise<number> -> 1
63
+ // 0: The initial state, the list-item is not ready yet, we will send a event to background
64
+ // when `componentAtIndex` is called on it
65
+ // Promise<number>: A promise that will be resolved when the list-item is ready
66
+ // 1: The list-item is ready, we can use it to render the list
67
+ if (childCtx.__extraProps?.['isReady'] === 0) {
68
+ if (
69
+ typeof __GetAttributeByName === 'function'
70
+ && __GetAttributeByName(list, 'custom-list-name') === 'list-container'
71
+ ) {
72
+ // we are in supported env
73
+ // do not throw
74
+ } else {
75
+ throw new Error(
76
+ 'Unsupported: `<list-item/>` with `defer={true}` must be used with `<list custom-list-name="list-container"/>`',
77
+ );
78
+ }
79
+
80
+ __OnLifecycleEvent([LifecycleConstant.publishEvent, {
81
+ handlerName: `${childCtx.__id}:__extraProps:onComponentAtIndex`,
82
+ data: {},
83
+ }]);
84
+
85
+ let p: Promise<number>;
86
+ return (p = new Promise<number>((resolve) => {
87
+ Object.defineProperty(childCtx.__extraProps, 'isReady', {
88
+ set(isReady) {
89
+ if (isReady === 1) {
90
+ delete childCtx.__extraProps!['isReady'];
91
+ childCtx.__extraProps!['isReady'] = 1;
92
+
93
+ void resolvedPromise.then(() => {
94
+ // the cellIndex may be changed already, but the `childCtx` is the same
95
+ resolve(componentAtChildCtx(list, listID, childCtx, operationID, enableReuseNotification));
96
+ });
97
+ }
98
+ },
99
+ get() {
100
+ return p;
101
+ },
102
+ });
103
+ }));
104
+ } else if (maybePromise<number>(childCtx.__extraProps?.['isReady'])) {
105
+ throw new Error('componentAtIndex was called on a pending deferred list item');
106
+ }
107
+
63
108
  const uniqID = childCtx.type + (platformInfo['reuse-identifier'] ?? '');
64
109
  const recycleSignMap = recycleMap.get(uniqID);
65
110
 
@@ -102,6 +147,11 @@ export function componentAtIndexFactory(
102
147
  oldCtx.unRenderElements();
103
148
  if (!oldCtx.__id) {
104
149
  oldCtx.tearDown();
150
+ } else if (oldCtx.__extraProps?.['isReady'] === 1) {
151
+ __OnLifecycleEvent([LifecycleConstant.publishEvent, {
152
+ handlerName: `${oldCtx.__id}:__extraProps:onRecycleComponent`,
153
+ data: {},
154
+ }]);
105
155
  }
106
156
  const root = childCtx.__element_root!;
107
157
  applyRefQueue();
@@ -156,25 +206,78 @@ export function componentAtIndexFactory(
156
206
  return sign;
157
207
  };
158
208
 
159
- const componentAtIndexes = (
209
+ function componentAtIndex(
210
+ list: FiberElement,
211
+ listID: number,
212
+ cellIndex: number,
213
+ operationID: number,
214
+ enableReuseNotification: boolean,
215
+ ) {
216
+ const childCtx = ctx[cellIndex];
217
+ if (!childCtx) {
218
+ throw new Error('childCtx not found');
219
+ }
220
+ const r = componentAtChildCtx(list, listID, childCtx, operationID, enableReuseNotification);
221
+
222
+ /* v8 ignore start */
223
+ if (process.env['NODE_ENV'] === 'test') {
224
+ return r;
225
+ } else {
226
+ return typeof r === 'number' ? r : undefined;
227
+ }
228
+ /* v8 ignore end */
229
+ }
230
+
231
+ function componentAtIndexes(
160
232
  list: FiberElement,
161
233
  listID: number,
162
234
  cellIndexes: number[],
163
235
  operationIDs: number[],
164
236
  enableReuseNotification: boolean,
165
237
  asyncFlush: boolean,
166
- ) => {
167
- const uiSigns = cellIndexes.map((cellIndex, index) => {
168
- const operationID = operationIDs[index] ?? 0;
169
- return componentAtIndex(list, listID, cellIndex, operationID, enableReuseNotification, true, asyncFlush);
238
+ ) {
239
+ let hasUnready = false;
240
+ const p: Array<Promise<number> | number> = [];
241
+
242
+ cellIndexes.forEach((cellIndex, index) => {
243
+ const operationID = operationIDs[index]!;
244
+ const childCtx = ctx[cellIndex];
245
+ if (!childCtx) {
246
+ throw new Error('childCtx not found');
247
+ }
248
+
249
+ const u = componentAtChildCtx(list, listID, childCtx, operationID, enableReuseNotification, true, asyncFlush);
250
+ if (typeof u === 'number') {
251
+ // ready
252
+ } else {
253
+ hasUnready = true;
254
+ }
255
+ p.push(u);
170
256
  });
257
+
258
+ // We need __FlushElementTree twice:
259
+ // 1. The first time is sync, we flush the items that are ready, with unready items' uiSign as -1.
260
+ // 2. The second time is async, with all the uiSigns.
261
+ // NOTE: The `operationIDs` passed to __FlushElementTree must be the one passed in,
262
+ // not the one generated by any code here, to workaround a bug of Lynx Engine.
263
+ // So we CANNOT split the `operationIDs` into two parts: one for ready items, one for unready items.
264
+ if (hasUnready) {
265
+ void Promise.all(p).then((uiSigns) => {
266
+ __FlushElementTree(list, {
267
+ triggerLayout: true,
268
+ operationIDs,
269
+ elementIDs: uiSigns,
270
+ listID,
271
+ });
272
+ });
273
+ }
171
274
  __FlushElementTree(list, {
172
275
  triggerLayout: true,
173
- operationIDs: operationIDs,
174
- elementIDs: uiSigns,
276
+ operationIDs,
277
+ elementIDs: cellIndexes.map((_, index) => typeof p[index] === 'number' ? p[index] : -1),
175
278
  listID,
176
279
  });
177
- };
280
+ }
178
281
  return [componentAtIndex, componentAtIndexes] as const;
179
282
  }
180
283
 
@@ -12,7 +12,7 @@ import { __root, setRoot } from '../root.js';
12
12
  import { applyRefQueue } from '../snapshot/workletRef.js';
13
13
  import { SnapshotInstance, __page, setupPage } from '../snapshot.js';
14
14
  import { isEmptyObject } from '../utils.js';
15
- import { PerformanceTimingKeys, markTiming, setPipeline } from './performance.js';
15
+ import { markTiming, setPipeline } from './performance.js';
16
16
 
17
17
  function ssrEncode() {
18
18
  const { __opcodes } = __root;
@@ -116,13 +116,14 @@ function updatePage(data: Record<string, unknown> | undefined, options?: UpdateP
116
116
  Object.assign(lynx.__initData, data);
117
117
  }
118
118
 
119
+ const flushOptions = options ?? {};
119
120
  if (!isJSReady) {
120
121
  const oldRoot = __root;
121
122
  setRoot(new SnapshotInstance('root'));
122
123
  __root.__jsx = oldRoot.__jsx;
123
124
 
124
125
  setPipeline(options?.pipelineOptions);
125
- markTiming(PerformanceTimingKeys.updateDiffVdomStart);
126
+ markTiming('updateDiffVdomStart');
126
127
  {
127
128
  __pendingListUpdates.clear();
128
129
  renderMainThread();
@@ -138,14 +139,11 @@ function updatePage(data: Record<string, unknown> | undefined, options?: UpdateP
138
139
  __pendingListUpdates.flush();
139
140
  applyRefQueue();
140
141
  }
141
- markTiming(PerformanceTimingKeys.updateDiffVdomEnd);
142
+ flushOptions.triggerDataUpdated = true;
143
+ markTiming('updateDiffVdomEnd');
142
144
  }
143
145
 
144
- if (options) {
145
- __FlushElementTree(__page, options);
146
- } else {
147
- __FlushElementTree();
148
- }
146
+ __FlushElementTree(__page, flushOptions);
149
147
  }
150
148
 
151
149
  function updateGlobalProps(_data: any, options?: UpdatePageOption): void {
@@ -6,23 +6,28 @@
6
6
 
7
7
  import { Component } from 'preact';
8
8
 
9
- import { PerfSpecificKey, PerformanceTimingKeys, markTimingLegacy } from './performance.js';
9
+ import { PerfSpecificKey, markTimingLegacy } from './performance.js';
10
10
  import { globalFlushOptions } from '../lifecycle/patch/commit.js';
11
11
  import { NEXT_STATE } from '../renderToOpcodes/constants.js';
12
12
 
13
13
  if (__JS__) {
14
- const __Component = Component as any;
15
-
16
- __Component.prototype._reactAppInstance = lynxCoreInject.tt;
17
-
18
- __Component.prototype.getNodeRef = function(a: string, b?: boolean) {
14
+ function reportRefDeprecationError(fnName: string, newFnName: string) {
19
15
  if (!__DISABLE_CREATE_SELECTOR_QUERY_INCOMPATIBLE_WARNING__) {
20
16
  lynx.reportError(
21
17
  new Error(
22
- 'getNodeRef is deprecated and has different behavior in ReactLynx 3.0, please use ref or lynx.createSelectorQuery instead.',
18
+ `${fnName} is deprecated and has different behavior in ReactLynx 3.0, please use ref or ${newFnName} instead.`,
23
19
  ),
24
20
  );
25
21
  }
22
+ }
23
+
24
+ const __Component = Component as any;
25
+
26
+ __Component.prototype._reactAppInstance = lynxCoreInject.tt;
27
+
28
+ __Component.prototype.getNodeRef = function(a: string, b?: boolean) {
29
+ reportRefDeprecationError('getNodeRef', 'lynx.createSelectorQuery');
30
+
26
31
  // @ts-expect-error hack lynx-kernel
27
32
  return lynxCoreInject.tt._reactLynx.ReactComponent.prototype.getNodeRef
28
33
  .call(
@@ -40,13 +45,8 @@ if (__JS__) {
40
45
  };
41
46
 
42
47
  __Component.prototype.getNodeRefFromRoot = function(a: string) {
43
- if (!__DISABLE_CREATE_SELECTOR_QUERY_INCOMPATIBLE_WARNING__) {
44
- lynx.reportError(
45
- new Error(
46
- 'getNodeRefFromRoot is deprecated and has different behavior in ReactLynx 3.0, please use ref or lynx.createSelectorQuery instead.',
47
- ),
48
- );
49
- }
48
+ reportRefDeprecationError('getNodeRefFromRoot', 'lynx.createSelectorQuery');
49
+
50
50
  // @ts-expect-error hack lynx-kernel
51
51
  return lynxCoreInject.tt._reactLynx.ReactComponent.prototype
52
52
  .getNodeRefFromRoot.call(
@@ -88,26 +88,14 @@ if (__JS__) {
88
88
  };
89
89
 
90
90
  __Component.prototype.getElementById = function(id: string) {
91
- if (!__DISABLE_CREATE_SELECTOR_QUERY_INCOMPATIBLE_WARNING__) {
92
- lynx.reportError(
93
- new Error(
94
- 'getElementById on component instance is deprecated and has different behavior in ReactLynx 3.0, please use ref or lynx.getElementById instead.',
95
- ),
96
- );
97
- }
91
+ reportRefDeprecationError('getElementById', 'lynx.getElementById');
98
92
  return lynx.getElementById(id);
99
93
  };
100
94
 
101
95
  __Component.prototype.GlobalEventEmitter = lynxCoreInject.tt.GlobalEventEmitter;
102
96
 
103
97
  __Component.prototype.createSelectorQuery = function() {
104
- if (!__DISABLE_CREATE_SELECTOR_QUERY_INCOMPATIBLE_WARNING__) {
105
- lynx.reportError(
106
- new Error(
107
- 'createSelectorQuery on component instance is deprecated and has different behavior in ReactLynx 3.0, please use ref or lynx.createSelectorQuery instead.',
108
- ),
109
- );
110
- }
98
+ reportRefDeprecationError('createSelectorQuery on component instance', 'lynx.createSelectorQuery');
111
99
  return lynx.createSelectorQuery();
112
100
  };
113
101
 
@@ -118,7 +106,7 @@ if (__JS__) {
118
106
  const timingFlag = this[NEXT_STATE][PerfSpecificKey];
119
107
  if (timingFlag) {
120
108
  globalFlushOptions.__lynx_timing_flag = timingFlag;
121
- markTimingLegacy(PerformanceTimingKeys.updateSetStateTrigger, timingFlag);
109
+ markTimingLegacy('updateSetStateTrigger', timingFlag);
122
110
  this[NEXT_STATE][PerfSpecificKey] = '';
123
111
  }
124
112
  };
@@ -56,8 +56,7 @@ export function setupLynxEnv(): void {
56
56
  let r: InitData | InitDataRaw;
57
57
  try {
58
58
  if (processorName) {
59
- r = dataProcessorDefinition?.dataProcessors?.[processorName]?.(data)
60
- ?? data;
59
+ r = dataProcessorDefinition?.dataProcessors?.[processorName]?.(data) as InitData ?? data;
61
60
  } else {
62
61
  r = dataProcessorDefinition?.defaultDataProcessor?.(data) ?? data;
63
62
  }
@@ -33,7 +33,7 @@ export const makeSyncThen = function<T>(result: T): Promise<T>['then'] {
33
33
  // Calling `then` and passing a callback is standard behavior
34
34
  // but in Lepus runtime the callback will never be called
35
35
  // So can be simplified to code below
36
- return new Promise(() => {});
36
+ return ret as Promise<TR1>;
37
37
 
38
38
  // TODO(hongzhiyuan.hzy): Avoid warning that cannot be turned-off, so the warning is commented
39
39
  // lynx.reportError(
@@ -90,27 +90,40 @@ export const loadLazyBundle: <
90
90
  r.then = makeSyncThen(result);
91
91
  return r;
92
92
  } else if (__JS__) {
93
- return new Promise((resolve, reject) => {
94
- const callback: (result: { code: number; detail: { schema: string } }) => void = result => {
95
- const { code, detail } = result;
96
- if (code === 0) {
97
- const { schema } = detail;
98
- const exports = lynxCoreInject.tt.getDynamicComponentExports(schema);
99
- // `code === 0` means that the lazy bundle has been successfully parsed. However,
100
- // its javascript files may still fail to run, which would prevent the retrieval of the exports object.
101
- if (exports) {
102
- resolve(exports as T);
103
- return;
104
- }
93
+ const resolver = withSyncResolvers<T>();
94
+
95
+ const callback: (result: { code: number; detail: { schema: string } }) => void = result => {
96
+ const { code, detail } = result;
97
+ if (code === 0) {
98
+ const { schema } = detail;
99
+ const exports = lynxCoreInject.tt.getDynamicComponentExports(schema);
100
+ // `code === 0` means that the lazy bundle has been successfully parsed. However,
101
+ // its javascript files may still fail to run, which would prevent the retrieval of the exports object.
102
+ if (exports) {
103
+ resolver.resolve(exports as T);
104
+ return;
105
105
  }
106
- reject(new Error('Lazy bundle load failed: ' + JSON.stringify(result)));
107
- };
108
- if (typeof lynx.QueryComponent === 'function') {
109
- lynx.QueryComponent(source, callback);
110
- } else {
111
- lynx.getNativeLynx().QueryComponent!(source, callback);
112
106
  }
113
- });
107
+ resolver.reject(new Error('Lazy bundle load failed: ' + JSON.stringify(result)));
108
+ };
109
+ if (typeof lynx.QueryComponent === 'function') {
110
+ lynx.QueryComponent(source, callback);
111
+ } else {
112
+ lynx.getNativeLynx().QueryComponent!(source, callback);
113
+ }
114
+
115
+ if (resolver.result !== null) {
116
+ const p = Promise.resolve(resolver.result);
117
+ p.then = makeSyncThen(resolver.result) as Promise<Awaited<T>>['then'];
118
+ return p;
119
+ } else if (resolver.error === null) {
120
+ return new Promise((_resolve, _reject) => {
121
+ resolver.resolve = _resolve;
122
+ resolver.reject = _reject;
123
+ });
124
+ } else {
125
+ return Promise.reject(resolver.error);
126
+ }
114
127
  }
115
128
 
116
129
  throw new Error('unreachable');
@@ -119,6 +132,28 @@ export const loadLazyBundle: <
119
132
  return loadLazyBundle;
120
133
  })();
121
134
 
135
+ function withSyncResolvers<T>() {
136
+ 'background-only';
137
+
138
+ const resolver: {
139
+ result: T | null;
140
+ error: Error | null;
141
+ resolve(result: T): void;
142
+ reject(error: Error): void;
143
+ } = {
144
+ resolve: (result: T): void => {
145
+ resolver.result = result;
146
+ },
147
+ reject: (error: Error): void => {
148
+ resolver.error = error;
149
+ },
150
+ result: null,
151
+ error: null,
152
+ };
153
+
154
+ return resolver;
155
+ }
156
+
122
157
  /**
123
158
  * @internal
124
159
  */
@@ -8,24 +8,24 @@ import { __globalSnapshotPatch } from '../lifecycle/patch/snapshotPatch.js';
8
8
  import { DIFF } from '../renderToOpcodes/constants.js';
9
9
  import { isSdkVersionGt } from '../utils.js';
10
10
 
11
- enum PerformanceTimingKeys {
12
- updateSetStateTrigger,
13
- updateDiffVdomStart,
14
- updateDiffVdomEnd,
11
+ const PerformanceTimingKeys = [
12
+ 'updateSetStateTrigger',
13
+ 'updateDiffVdomStart',
14
+ 'updateDiffVdomEnd',
15
15
  // updateSetStateTrigger, updateDiffVdomStart and updateDiffVdomEnd is deprecated
16
- diffVdomStart,
17
- diffVdomEnd,
18
- packChangesStart,
19
- packChangesEnd,
20
- parseChangesStart,
21
- parseChangesEnd,
22
- patchChangesStart,
23
- patchChangesEnd,
24
- hydrateParseSnapshotStart,
25
- hydrateParseSnapshotEnd,
26
- mtsRenderStart,
27
- mtsRenderEnd,
28
- }
16
+ 'diffVdomStart',
17
+ 'diffVdomEnd',
18
+ 'packChangesStart',
19
+ 'packChangesEnd',
20
+ 'parseChangesStart',
21
+ 'parseChangesEnd',
22
+ 'patchChangesStart',
23
+ 'patchChangesEnd',
24
+ 'hydrateParseSnapshotStart',
25
+ 'hydrateParseSnapshotEnd',
26
+ 'mtsRenderStart',
27
+ 'mtsRenderEnd',
28
+ ] as const;
29
29
 
30
30
  const PerformanceTimingFlags = {
31
31
  reactLynxHydrate: 'react_lynx_hydrate',
@@ -51,15 +51,15 @@ let globalPipelineOptions: PipelineOptions | undefined;
51
51
  /**
52
52
  * @deprecated used by old timing api(setState timing flag)
53
53
  */
54
- function markTimingLegacy(key: PerformanceTimingKeys, timingFlag_?: string): void {
54
+ function markTimingLegacy(key: typeof PerformanceTimingKeys[number], timingFlag_?: string): void {
55
55
  switch (key) {
56
- case PerformanceTimingKeys.updateSetStateTrigger: {
56
+ case 'updateSetStateTrigger': {
57
57
  shouldMarkDiffVdomStart = true;
58
58
  shouldMarkDiffVdomEnd = true;
59
59
  timingFlag = timingFlag_;
60
60
  break;
61
61
  }
62
- case PerformanceTimingKeys.updateDiffVdomStart: {
62
+ case 'updateDiffVdomStart': {
63
63
  /* v8 ignore start */
64
64
  if (!shouldMarkDiffVdomStart) {
65
65
  return;
@@ -68,7 +68,7 @@ function markTimingLegacy(key: PerformanceTimingKeys, timingFlag_?: string): voi
68
68
  shouldMarkDiffVdomStart = false;
69
69
  break;
70
70
  }
71
- case PerformanceTimingKeys.updateDiffVdomEnd: {
71
+ case 'updateDiffVdomEnd': {
72
72
  if (!shouldMarkDiffVdomEnd) {
73
73
  return;
74
74
  }
@@ -76,7 +76,7 @@ function markTimingLegacy(key: PerformanceTimingKeys, timingFlag_?: string): voi
76
76
  break;
77
77
  }
78
78
  }
79
- lynx.getNativeApp().markTiming?.(timingFlag!, PerformanceTimingKeys[key]);
79
+ lynx.getNativeApp().markTiming?.(timingFlag!, key);
80
80
  }
81
81
 
82
82
  function beginPipeline(needTimestamps: boolean, pipelineOrigin: PipelineOrigin, timingFlag?: string): void {
@@ -109,9 +109,9 @@ function setPipeline(pipeline: PipelineOptions | undefined): void {
109
109
  globalPipelineOptions = pipeline;
110
110
  }
111
111
 
112
- function markTiming(timestampKey: PerformanceTimingKeys, force?: boolean): void {
112
+ function markTiming(timestampKey: typeof PerformanceTimingKeys[number], force?: boolean): void {
113
113
  if (globalPipelineOptions && (force || globalPipelineOptions.needTimestamps)) {
114
- lynx.performance?._markTiming?.(globalPipelineOptions.pipelineID, PerformanceTimingKeys[timestampKey]);
114
+ lynx.performance?._markTiming?.(globalPipelineOptions.pipelineID, timestampKey);
115
115
  }
116
116
  }
117
117
 
@@ -122,10 +122,10 @@ function initTimingAPI(): void {
122
122
  if (__JS__ && __globalSnapshotPatch) {
123
123
  if (!globalPipelineOptions) {
124
124
  beginPipeline(false, PipelineOrigins.updateTriggeredByBts);
125
- markTiming(PerformanceTimingKeys.diffVdomStart, true);
125
+ markTiming('diffVdomStart', true);
126
126
  }
127
127
  if (shouldMarkDiffVdomStart) {
128
- markTimingLegacy(PerformanceTimingKeys.updateDiffVdomStart);
128
+ markTimingLegacy('updateDiffVdomStart');
129
129
  }
130
130
  }
131
131
  oldDiff?.(vnode);
@@ -136,7 +136,6 @@ function initTimingAPI(): void {
136
136
  * @internal
137
137
  */
138
138
  export {
139
- PerformanceTimingKeys,
140
139
  PerformanceTimingFlags,
141
140
  PipelineOrigins,
142
141
  PerfSpecificKey,