@lynx-js/react 0.110.1 → 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.
Files changed (80) hide show
  1. package/CHANGELOG.md +44 -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 +1 -1
  12. package/runtime/lazy/react-lepus.js +1 -0
  13. package/runtime/lazy/react.js +1 -0
  14. package/runtime/lepus/index.d.ts +1 -1
  15. package/runtime/lepus/index.js +44 -0
  16. package/runtime/lib/backgroundSnapshot.d.ts +2 -1
  17. package/runtime/lib/backgroundSnapshot.js +62 -40
  18. package/runtime/lib/backgroundSnapshot.js.map +1 -1
  19. package/runtime/lib/compat/initData.js +10 -0
  20. package/runtime/lib/compat/initData.js.map +1 -1
  21. package/runtime/lib/index.d.ts +2 -2
  22. package/runtime/lib/index.js +2 -2
  23. package/runtime/lib/index.js.map +1 -1
  24. package/runtime/lib/lifecycle/patch/commit.js +5 -5
  25. package/runtime/lib/lifecycle/patch/commit.js.map +1 -1
  26. package/runtime/lib/lifecycle/patch/snapshotPatch.d.ts +9 -9
  27. package/runtime/lib/lifecycle/patch/snapshotPatch.js +9 -10
  28. package/runtime/lib/lifecycle/patch/snapshotPatch.js.map +1 -1
  29. package/runtime/lib/lifecycle/patch/updateMainThread.js +7 -8
  30. package/runtime/lib/lifecycle/patch/updateMainThread.js.map +1 -1
  31. package/runtime/lib/lifecycleConstant.d.ts +2 -1
  32. package/runtime/lib/lifecycleConstant.js +1 -0
  33. package/runtime/lib/lifecycleConstant.js.map +1 -1
  34. package/runtime/lib/list.js +102 -12
  35. package/runtime/lib/list.js.map +1 -1
  36. package/runtime/lib/lynx/calledByNative.js +6 -9
  37. package/runtime/lib/lynx/calledByNative.js.map +1 -1
  38. package/runtime/lib/lynx/component.js +11 -14
  39. package/runtime/lib/lynx/component.js.map +1 -1
  40. package/runtime/lib/lynx/env.js +1 -2
  41. package/runtime/lib/lynx/env.js.map +1 -1
  42. package/runtime/lib/lynx/lazy-bundle.js +48 -21
  43. package/runtime/lib/lynx/lazy-bundle.js.map +1 -1
  44. package/runtime/lib/lynx/performance.d.ts +3 -19
  45. package/runtime/lib/lynx/performance.js +25 -26
  46. package/runtime/lib/lynx/performance.js.map +1 -1
  47. package/runtime/lib/lynx/tt.js +10 -5
  48. package/runtime/lib/lynx/tt.js.map +1 -1
  49. package/runtime/lib/lynx-api.d.ts +78 -1
  50. package/runtime/lib/lynx-api.js.map +1 -1
  51. package/runtime/lib/snapshot.d.ts +2 -0
  52. package/runtime/lib/snapshot.js +19 -5
  53. package/runtime/lib/snapshot.js.map +1 -1
  54. package/runtime/lib/utils.d.ts +1 -0
  55. package/runtime/lib/utils.js +6 -0
  56. package/runtime/lib/utils.js.map +1 -1
  57. package/runtime/src/backgroundSnapshot.ts +74 -55
  58. package/runtime/src/compat/initData.ts +10 -0
  59. package/runtime/src/index.ts +2 -0
  60. package/runtime/src/lifecycle/patch/commit.ts +5 -11
  61. package/runtime/src/lifecycle/patch/snapshotPatch.ts +9 -9
  62. package/runtime/src/lifecycle/patch/updateMainThread.ts +7 -8
  63. package/runtime/src/lifecycleConstant.ts +1 -0
  64. package/runtime/src/list.ts +118 -15
  65. package/runtime/src/lynx/calledByNative.ts +6 -8
  66. package/runtime/src/lynx/component.ts +17 -29
  67. package/runtime/src/lynx/env.ts +1 -2
  68. package/runtime/src/lynx/lazy-bundle.ts +55 -20
  69. package/runtime/src/lynx/performance.ts +26 -27
  70. package/runtime/src/lynx/tt.ts +10 -11
  71. package/runtime/src/lynx-api.ts +77 -1
  72. package/runtime/src/snapshot.ts +20 -5
  73. package/runtime/src/utils.ts +9 -0
  74. package/testing-library/dist/index.d.ts +4 -1
  75. package/testing-library/dist/pure.js +1 -1
  76. package/testing-library/dist/vitest.config.js +38 -1
  77. package/testing-library/types/entry.d.ts +3 -2
  78. package/transform/dist/wasm.cjs +1 -1
  79. package/types/react.d.ts +21 -1
  80. package/types/react.docs.d.ts +1 -1
@@ -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,
@@ -5,13 +5,7 @@ import { render } from 'preact';
5
5
 
6
6
  import { LifecycleConstant, NativeUpdateDataType } from '../lifecycleConstant.js';
7
7
  import type { FirstScreenData } from '../lifecycleConstant.js';
8
- import {
9
- PerformanceTimingFlags,
10
- PerformanceTimingKeys,
11
- PipelineOrigins,
12
- beginPipeline,
13
- markTiming,
14
- } from './performance.js';
8
+ import { PerformanceTimingFlags, PipelineOrigins, beginPipeline, markTiming } from './performance.js';
15
9
  import { BackgroundSnapshotInstance, hydrate } from '../backgroundSnapshot.js';
16
10
  import { runWithForce } from './runWithForce.js';
17
11
  import { destroyBackground } from '../lifecycle/destroy.js';
@@ -80,10 +74,10 @@ function onLifecycleEventImpl(type: LifecycleConstant, data: unknown): void {
80
74
  console.profile('hydrate');
81
75
  }
82
76
  beginPipeline(true, PipelineOrigins.reactLynxHydrate, PerformanceTimingFlags.reactLynxHydrate);
83
- markTiming(PerformanceTimingKeys.hydrateParseSnapshotStart);
77
+ markTiming('hydrateParseSnapshotStart');
84
78
  const before = JSON.parse(lepusSide) as SerializedSnapshotInstance;
85
- markTiming(PerformanceTimingKeys.hydrateParseSnapshotEnd);
86
- markTiming(PerformanceTimingKeys.diffVdomStart);
79
+ markTiming('hydrateParseSnapshotEnd');
80
+ markTiming('diffVdomStart');
87
81
  const snapshotPatch = hydrate(
88
82
  before,
89
83
  __root as BackgroundSnapshotInstance,
@@ -91,7 +85,7 @@ function onLifecycleEventImpl(type: LifecycleConstant, data: unknown): void {
91
85
  if (__PROFILE__) {
92
86
  console.profileEnd();
93
87
  }
94
- markTiming(PerformanceTimingKeys.diffVdomEnd);
88
+ markTiming('diffVdomEnd');
95
89
 
96
90
  // TODO: It seems `delayedEvents` and `delayedLifecycleEvents` should be merged into one array to ensure the proper order of events.
97
91
  flushDelayedLifecycleEvents();
@@ -141,6 +135,11 @@ function onLifecycleEventImpl(type: LifecycleConstant, data: unknown): void {
141
135
  lynx.getJSModule('GlobalEventEmitter').trigger(eventName, params);
142
136
  break;
143
137
  }
138
+ case LifecycleConstant.publishEvent: {
139
+ const { handlerName, data: d } = data as { handlerName: string; data: unknown };
140
+ lynxCoreInject.tt.publishEvent(handlerName, d);
141
+ break;
142
+ }
144
143
  }
145
144
  }
146
145
 
@@ -218,6 +218,82 @@ export interface InitData {}
218
218
 
219
219
  export { withInitDataInState };
220
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
+
221
297
  /**
222
298
  * Definition of DataProcessor(s)
223
299
  * @public
@@ -238,7 +314,7 @@ export interface DataProcessorDefinition {
238
314
  *
239
315
  * @public
240
316
  */
241
- dataProcessors?: Record<string, ((rawInitData: InitDataRaw) => InitData)>;
317
+ dataProcessors?: DataProcessors;
242
318
  }
243
319
 
244
320
  /**
@@ -165,10 +165,17 @@ export const backgroundSnapshotInstanceManager: {
165
165
  return null;
166
166
  }
167
167
  const spreadKey = res[2];
168
- if (spreadKey) {
169
- return (ctx.__values![expIndex] as { [spreadKey]: unknown })[spreadKey];
168
+ if (res[1] === '__extraProps') {
169
+ if (spreadKey) {
170
+ return ctx.__extraProps![spreadKey];
171
+ }
172
+ throw new Error('unreachable');
170
173
  } else {
171
- return ctx.__values![expIndex];
174
+ if (spreadKey) {
175
+ return (ctx.__values![expIndex] as { [spreadKey]: unknown })[spreadKey];
176
+ } else {
177
+ return ctx.__values![expIndex];
178
+ }
172
179
  }
173
180
  },
174
181
  };
@@ -241,6 +248,7 @@ export interface SerializedSnapshotInstance {
241
248
  id: number;
242
249
  type: string;
243
250
  values?: any[] | undefined;
251
+ extraProps?: Record<string, unknown> | undefined;
244
252
  children?: SerializedSnapshotInstance[] | undefined;
245
253
  }
246
254
 
@@ -263,6 +271,7 @@ export class SnapshotInstance {
263
271
  __current_slot_index = 0;
264
272
  __worklet_ref_set?: Set<WorkletRefImpl<any> | Worklet>;
265
273
  __listItemPlatformInfo?: PlatformInfo;
274
+ __extraProps?: Record<string, unknown> | undefined;
266
275
 
267
276
  constructor(public type: string, id?: number) {
268
277
  this.__snapshot_def = snapshotManager.values.get(type)!;
@@ -596,9 +605,14 @@ export class SnapshotInstance {
596
605
  return;
597
606
  }
598
607
 
599
- const index = typeof key === 'string' ? Number(key.slice(2)) : key;
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
+
600
614
  this.__values ??= [];
601
- this.callUpdateIfNotDirectOrDeepEqual(index, this.__values[index], this.__values[index] = value);
615
+ this.callUpdateIfNotDirectOrDeepEqual(key, this.__values[key], this.__values[key] = value);
602
616
  }
603
617
 
604
618
  toJSON(): Omit<SerializedSnapshotInstance, 'children'> & { children: SnapshotInstance[] | undefined } {
@@ -606,6 +620,7 @@ export class SnapshotInstance {
606
620
  id: this.__id,
607
621
  type: this.type,
608
622
  values: this.__values,
623
+ extraProps: this.__extraProps,
609
624
  children: this.__firstChild ? this.childNodes : undefined,
610
625
  };
611
626
  }
@@ -33,3 +33,12 @@ export function pick<T extends object, K extends keyof T>(obj: T, keys: Iterable
33
33
  }
34
34
  return result as Pick<T, K>;
35
35
  }
36
+
37
+ export function maybePromise<T>(value: unknown): value is Promise<T> {
38
+ return (
39
+ typeof value === 'object'
40
+ && value !== null
41
+ // @ts-expect-error the check is safe
42
+ && typeof value.then === 'function'
43
+ );
44
+ }
@@ -1,3 +1,4 @@
1
+ import { act } from 'preact/test-utils';
1
2
  import { ARIARole } from 'aria-query';
2
3
  import { ComponentChild } from 'preact';
3
4
  import { ComponentType } from 'preact';
@@ -5,6 +6,8 @@ import { JSDOM } from 'jsdom';
5
6
  import { OptionsReceived } from 'pretty-format';
6
7
  import * as prettyFormat from 'pretty-format';
7
8
 
9
+ export { act }
10
+
8
11
  export declare type AllByAttribute = (
9
12
  attribute: string,
10
13
  container: HTMLElement,
@@ -1493,7 +1496,7 @@ export declare interface waitForOptions {
1493
1496
  *
1494
1497
  * It will be useful when you want to wait for the next event loop to finish.
1495
1498
  *
1496
- * @public
1499
+ * @deprecated Please use `act` instead
1497
1500
  */
1498
1501
  export declare function waitSchedule(): Promise<void>;
1499
1502
 
@@ -14725,4 +14725,4 @@ var __webpack_exports__waitForElementToBeRemoved = dom_esm.mz;
14725
14725
  var __webpack_exports__within = dom_esm.uh;
14726
14726
  var __webpack_exports__wrapAllByQueryWithSuggestion = dom_esm.bH;
14727
14727
  var __webpack_exports__wrapSingleQueryWithSuggestion = dom_esm.kG;
14728
- export { cleanup, fireEvent, pure_render as render, renderHook, waitSchedule, __webpack_exports__buildQueries as buildQueries, __webpack_exports__configure as configure, __webpack_exports__createEvent as createEvent, __webpack_exports__findAllByAltText as findAllByAltText, __webpack_exports__findAllByDisplayValue as findAllByDisplayValue, __webpack_exports__findAllByLabelText as findAllByLabelText, __webpack_exports__findAllByPlaceholderText as findAllByPlaceholderText, __webpack_exports__findAllByRole as findAllByRole, __webpack_exports__findAllByTestId as findAllByTestId, __webpack_exports__findAllByText as findAllByText, __webpack_exports__findAllByTitle as findAllByTitle, __webpack_exports__findByAltText as findByAltText, __webpack_exports__findByDisplayValue as findByDisplayValue, __webpack_exports__findByLabelText as findByLabelText, __webpack_exports__findByPlaceholderText as findByPlaceholderText, __webpack_exports__findByRole as findByRole, __webpack_exports__findByTestId as findByTestId, __webpack_exports__findByText as findByText, __webpack_exports__findByTitle as findByTitle, __webpack_exports__getAllByAltText as getAllByAltText, __webpack_exports__getAllByDisplayValue as getAllByDisplayValue, __webpack_exports__getAllByLabelText as getAllByLabelText, __webpack_exports__getAllByPlaceholderText as getAllByPlaceholderText, __webpack_exports__getAllByRole as getAllByRole, __webpack_exports__getAllByTestId as getAllByTestId, __webpack_exports__getAllByText as getAllByText, __webpack_exports__getAllByTitle as getAllByTitle, __webpack_exports__getByAltText as getByAltText, __webpack_exports__getByDisplayValue as getByDisplayValue, __webpack_exports__getByLabelText as getByLabelText, __webpack_exports__getByPlaceholderText as getByPlaceholderText, __webpack_exports__getByRole as getByRole, __webpack_exports__getByTestId as getByTestId, __webpack_exports__getByText as getByText, __webpack_exports__getByTitle as getByTitle, __webpack_exports__getConfig as getConfig, __webpack_exports__getDefaultNormalizer as getDefaultNormalizer, __webpack_exports__getElementError as getElementError, __webpack_exports__getMultipleElementsFoundError as getMultipleElementsFoundError, __webpack_exports__getNodeText as getNodeText, __webpack_exports__getQueriesForElement as getQueriesForElement, __webpack_exports__getRoles as getRoles, __webpack_exports__getSuggestedQuery as getSuggestedQuery, __webpack_exports__isInaccessible as isInaccessible, __webpack_exports__logDOM as logDOM, __webpack_exports__logRoles as logRoles, __webpack_exports__makeFindQuery as makeFindQuery, __webpack_exports__makeGetAllQuery as makeGetAllQuery, __webpack_exports__makeSingleQuery as makeSingleQuery, __webpack_exports__prettyDOM as prettyDOM, __webpack_exports__prettyFormat as prettyFormat, __webpack_exports__queries as queries, __webpack_exports__queryAllByAltText as queryAllByAltText, __webpack_exports__queryAllByAttribute as queryAllByAttribute, __webpack_exports__queryAllByDisplayValue as queryAllByDisplayValue, __webpack_exports__queryAllByLabelText as queryAllByLabelText, __webpack_exports__queryAllByPlaceholderText as queryAllByPlaceholderText, __webpack_exports__queryAllByRole as queryAllByRole, __webpack_exports__queryAllByTestId as queryAllByTestId, __webpack_exports__queryAllByText as queryAllByText, __webpack_exports__queryAllByTitle as queryAllByTitle, __webpack_exports__queryByAltText as queryByAltText, __webpack_exports__queryByAttribute as queryByAttribute, __webpack_exports__queryByDisplayValue as queryByDisplayValue, __webpack_exports__queryByLabelText as queryByLabelText, __webpack_exports__queryByPlaceholderText as queryByPlaceholderText, __webpack_exports__queryByRole as queryByRole, __webpack_exports__queryByTestId as queryByTestId, __webpack_exports__queryByText as queryByText, __webpack_exports__queryByTitle as queryByTitle, __webpack_exports__queryHelpers as queryHelpers, __webpack_exports__screen as screen, __webpack_exports__waitFor as waitFor, __webpack_exports__waitForElementToBeRemoved as waitForElementToBeRemoved, __webpack_exports__within as within, __webpack_exports__wrapAllByQueryWithSuggestion as wrapAllByQueryWithSuggestion, __webpack_exports__wrapSingleQueryWithSuggestion as wrapSingleQueryWithSuggestion };
14728
+ export { act, cleanup, fireEvent, pure_render as render, renderHook, waitSchedule, __webpack_exports__buildQueries as buildQueries, __webpack_exports__configure as configure, __webpack_exports__createEvent as createEvent, __webpack_exports__findAllByAltText as findAllByAltText, __webpack_exports__findAllByDisplayValue as findAllByDisplayValue, __webpack_exports__findAllByLabelText as findAllByLabelText, __webpack_exports__findAllByPlaceholderText as findAllByPlaceholderText, __webpack_exports__findAllByRole as findAllByRole, __webpack_exports__findAllByTestId as findAllByTestId, __webpack_exports__findAllByText as findAllByText, __webpack_exports__findAllByTitle as findAllByTitle, __webpack_exports__findByAltText as findByAltText, __webpack_exports__findByDisplayValue as findByDisplayValue, __webpack_exports__findByLabelText as findByLabelText, __webpack_exports__findByPlaceholderText as findByPlaceholderText, __webpack_exports__findByRole as findByRole, __webpack_exports__findByTestId as findByTestId, __webpack_exports__findByText as findByText, __webpack_exports__findByTitle as findByTitle, __webpack_exports__getAllByAltText as getAllByAltText, __webpack_exports__getAllByDisplayValue as getAllByDisplayValue, __webpack_exports__getAllByLabelText as getAllByLabelText, __webpack_exports__getAllByPlaceholderText as getAllByPlaceholderText, __webpack_exports__getAllByRole as getAllByRole, __webpack_exports__getAllByTestId as getAllByTestId, __webpack_exports__getAllByText as getAllByText, __webpack_exports__getAllByTitle as getAllByTitle, __webpack_exports__getByAltText as getByAltText, __webpack_exports__getByDisplayValue as getByDisplayValue, __webpack_exports__getByLabelText as getByLabelText, __webpack_exports__getByPlaceholderText as getByPlaceholderText, __webpack_exports__getByRole as getByRole, __webpack_exports__getByTestId as getByTestId, __webpack_exports__getByText as getByText, __webpack_exports__getByTitle as getByTitle, __webpack_exports__getConfig as getConfig, __webpack_exports__getDefaultNormalizer as getDefaultNormalizer, __webpack_exports__getElementError as getElementError, __webpack_exports__getMultipleElementsFoundError as getMultipleElementsFoundError, __webpack_exports__getNodeText as getNodeText, __webpack_exports__getQueriesForElement as getQueriesForElement, __webpack_exports__getRoles as getRoles, __webpack_exports__getSuggestedQuery as getSuggestedQuery, __webpack_exports__isInaccessible as isInaccessible, __webpack_exports__logDOM as logDOM, __webpack_exports__logRoles as logRoles, __webpack_exports__makeFindQuery as makeFindQuery, __webpack_exports__makeGetAllQuery as makeGetAllQuery, __webpack_exports__makeSingleQuery as makeSingleQuery, __webpack_exports__prettyDOM as prettyDOM, __webpack_exports__prettyFormat as prettyFormat, __webpack_exports__queries as queries, __webpack_exports__queryAllByAltText as queryAllByAltText, __webpack_exports__queryAllByAttribute as queryAllByAttribute, __webpack_exports__queryAllByDisplayValue as queryAllByDisplayValue, __webpack_exports__queryAllByLabelText as queryAllByLabelText, __webpack_exports__queryAllByPlaceholderText as queryAllByPlaceholderText, __webpack_exports__queryAllByRole as queryAllByRole, __webpack_exports__queryAllByTestId as queryAllByTestId, __webpack_exports__queryAllByText as queryAllByText, __webpack_exports__queryAllByTitle as queryAllByTitle, __webpack_exports__queryByAltText as queryByAltText, __webpack_exports__queryByAttribute as queryByAttribute, __webpack_exports__queryByDisplayValue as queryByDisplayValue, __webpack_exports__queryByLabelText as queryByLabelText, __webpack_exports__queryByPlaceholderText as queryByPlaceholderText, __webpack_exports__queryByRole as queryByRole, __webpack_exports__queryByTestId as queryByTestId, __webpack_exports__queryByText as queryByText, __webpack_exports__queryByTitle as queryByTitle, __webpack_exports__queryHelpers as queryHelpers, __webpack_exports__screen as screen, __webpack_exports__waitFor as waitFor, __webpack_exports__waitForElementToBeRemoved as waitForElementToBeRemoved, __webpack_exports__within as within, __webpack_exports__wrapAllByQueryWithSuggestion as wrapAllByQueryWithSuggestion, __webpack_exports__wrapSingleQueryWithSuggestion as wrapSingleQueryWithSuggestion };
@@ -16,7 +16,39 @@ async function ensurePackagesInstalled() {
16
16
  }
17
17
  const createVitestConfig = async (options)=>{
18
18
  await ensurePackagesInstalled();
19
- const runtimePkgName = options?.runtimePkgName ?? '@lynx-js/react';
19
+ const runtimeOSSPkgName = '@lynx-js/react';
20
+ const runtimePkgName = options?.runtimePkgName ?? runtimeOSSPkgName;
21
+ const runtimeDir = path.dirname(vitest_config_require.resolve(`${runtimePkgName}/package.json`));
22
+ const runtimeOSSDir = path.dirname(vitest_config_require.resolve(`${runtimeOSSPkgName}/package.json`, {
23
+ paths: [
24
+ runtimeDir
25
+ ]
26
+ }));
27
+ const preactDir = path.dirname(vitest_config_require.resolve('preact/package.json', {
28
+ paths: [
29
+ runtimeOSSDir
30
+ ]
31
+ }));
32
+ const generateAlias = (pkgName, pkgDir, resolveDir)=>{
33
+ const pkgExports = vitest_config_require(path.join(pkgDir, 'package.json')).exports;
34
+ const pkgAlias = [];
35
+ Object.keys(pkgExports).forEach((key)=>{
36
+ const name = path.posix.join(pkgName, key);
37
+ pkgAlias.push({
38
+ find: new RegExp('^' + name + '$'),
39
+ replacement: vitest_config_require.resolve(name, {
40
+ paths: [
41
+ resolveDir
42
+ ]
43
+ })
44
+ });
45
+ });
46
+ return pkgAlias;
47
+ };
48
+ const runtimeOSSAlias = generateAlias(runtimeOSSPkgName, runtimeOSSDir, runtimeDir);
49
+ let runtimeAlias = [];
50
+ if (runtimePkgName !== runtimeOSSPkgName) runtimeAlias = generateAlias(runtimePkgName, runtimeDir, vitest_config_dirname);
51
+ const preactAlias = generateAlias('preact', preactDir, runtimeOSSDir);
20
52
  function transformReactLynxPlugin() {
21
53
  return {
22
54
  name: 'transformReactLynxPlugin',
@@ -83,6 +115,11 @@ const createVitestConfig = async (options)=>{
83
115
  globals: true,
84
116
  setupFiles: [
85
117
  path.join(vitest_config_dirname, 'vitest-global-setup')
118
+ ],
119
+ alias: [
120
+ ...runtimeOSSAlias,
121
+ ...runtimeAlias,
122
+ ...preactAlias
86
123
  ]
87
124
  }
88
125
  });
@@ -10,8 +10,9 @@
10
10
  import { queries, Queries, BoundFunction } from '@testing-library/dom';
11
11
  import { LynxElement, type ElementTree, type LynxTestingEnv } from '@lynx-js/testing-environment';
12
12
  import { ComponentChild, ComponentType } from 'preact';
13
+ import { act } from 'preact/test-utils';
13
14
  export * from '@testing-library/dom';
14
- export { ElementTree, LynxTestingEnv };
15
+ export { ElementTree, LynxTestingEnv, act };
15
16
 
16
17
  /**
17
18
  * The options for {@link render}.
@@ -252,6 +253,6 @@ export function renderHook<Result, Props>(
252
253
  *
253
254
  * It will be useful when you want to wait for the next event loop to finish.
254
255
  *
255
- * @public
256
+ * @deprecated Please use `act` instead
256
257
  */
257
258
  export function waitSchedule(): Promise<void>;