@lynx-js/react 0.106.5 → 0.107.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 (53) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/package.json +15 -2
  3. package/refresh/.turbo/turbo-build.log +1 -1
  4. package/refresh/package.json +1 -1
  5. package/runtime/jsx-dev-runtime/index.d.ts +12 -0
  6. package/runtime/jsx-runtime/index.d.ts +12 -0
  7. package/runtime/lib/lifecycle/patch/commit.d.ts +7 -1
  8. package/runtime/lib/lifecycle/patch/commit.js +44 -38
  9. package/runtime/lib/lifecycle/patch/commit.js.map +1 -1
  10. package/runtime/lib/lifecycle/patch/updateMainThread.js +6 -4
  11. package/runtime/lib/lifecycle/patch/updateMainThread.js.map +1 -1
  12. package/runtime/lib/list.d.ts +13 -6
  13. package/runtime/lib/list.js.map +1 -1
  14. package/runtime/lib/lynx/calledByNative.js +2 -2
  15. package/runtime/lib/lynx/calledByNative.js.map +1 -1
  16. package/runtime/lib/lynx/component.js +1 -1
  17. package/runtime/lib/lynx/component.js.map +1 -1
  18. package/runtime/lib/lynx/performance.d.ts +24 -14
  19. package/runtime/lib/lynx/performance.js +48 -23
  20. package/runtime/lib/lynx/performance.js.map +1 -1
  21. package/runtime/lib/lynx/runWithForce.d.ts +1 -0
  22. package/runtime/lib/lynx/runWithForce.js +45 -0
  23. package/runtime/lib/lynx/runWithForce.js.map +1 -0
  24. package/runtime/lib/lynx/tt.d.ts +2 -1
  25. package/runtime/lib/lynx/tt.js +27 -61
  26. package/runtime/lib/lynx/tt.js.map +1 -1
  27. package/runtime/lib/lynx.d.ts +1 -0
  28. package/runtime/lib/lynx.js +1 -0
  29. package/runtime/lib/lynx.js.map +1 -1
  30. package/runtime/src/lifecycle/patch/commit.ts +54 -41
  31. package/runtime/src/lifecycle/patch/updateMainThread.ts +6 -4
  32. package/runtime/src/list.ts +18 -10
  33. package/runtime/src/lynx/calledByNative.ts +2 -2
  34. package/runtime/src/lynx/component.ts +1 -1
  35. package/runtime/src/lynx/performance.ts +53 -22
  36. package/runtime/src/lynx/runWithForce.ts +52 -0
  37. package/runtime/src/lynx/tt.ts +42 -70
  38. package/runtime/src/lynx.ts +1 -0
  39. package/testing-library/README.md +70 -0
  40. package/testing-library/dist/env/vitest.js +548 -0
  41. package/testing-library/dist/index.d.ts +1504 -0
  42. package/testing-library/dist/index.js +12 -0
  43. package/testing-library/dist/pure.js +14729 -0
  44. package/testing-library/dist/pure.js.LICENSE.txt +17 -0
  45. package/testing-library/dist/vitest-global-setup.js +115 -0
  46. package/testing-library/dist/vitest.config.js +90 -0
  47. package/testing-library/types/entry.d.ts +257 -0
  48. package/testing-library/types/index.d.ts +15 -0
  49. package/testing-library/types/pure.d.ts +2 -0
  50. package/testing-library/types/vitest-config.d.ts +12 -0
  51. package/transform/dist/wasm.cjs +1 -1
  52. package/types/react.d.ts +31 -0
  53. package/LICENSE +0 -202
@@ -109,7 +109,7 @@ if (__JS__) {
109
109
  const timingFlag = this[NEXT_STATE][PerfSpecificKey];
110
110
  if (timingFlag) {
111
111
  globalFlushOptions.__lynx_timing_flag = timingFlag;
112
- markTimingLegacy(PerformanceTimingKeys.update_set_state_trigger, timingFlag);
112
+ markTimingLegacy(PerformanceTimingKeys.updateSetStateTrigger, timingFlag);
113
113
  this[NEXT_STATE][PerfSpecificKey] = '';
114
114
  }
115
115
  };
@@ -6,24 +6,38 @@ import type { VNode } from 'preact';
6
6
 
7
7
  import { DIFF } from '../renderToOpcodes/constants.js';
8
8
  import { __globalSnapshotPatch } from '../lifecycle/patch/snapshotPatch.js';
9
+ import { isSdkVersionGt } from '../utils.js';
9
10
 
10
11
  enum PerformanceTimingKeys {
11
- update_set_state_trigger,
12
- update_diff_vdom_start,
13
- update_diff_vdom_end,
14
- // update_set_state_trigger, update_diff_vdom_start and update_diff_vdom_end is deprecated
15
- diff_vdom_start,
16
- diff_vdom_end,
17
- pack_changes_start,
18
- pack_changes_end,
19
- parse_changes_start,
20
- parse_changes_end,
21
- patch_changes_start,
22
- patch_changes_end,
23
- hydrate_parse_snapshot_start,
24
- hydrate_parse_snapshot_end,
12
+ updateSetStateTrigger,
13
+ updateDiffVdomStart,
14
+ updateDiffVdomEnd,
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,
25
28
  }
26
29
 
30
+ const PerformanceTimingFlags = {
31
+ reactLynxHydrate: 'react_lynx_hydrate',
32
+ } as const;
33
+
34
+ const PipelineOrigins = {
35
+ reactLynxHydrate: 'reactLynxHydrate',
36
+ updateTriggeredByBts: 'updateTriggeredByBts',
37
+ } as const;
38
+
39
+ type PipelineOrigin = typeof PipelineOrigins[keyof typeof PipelineOrigins];
40
+
27
41
  /**
28
42
  * @deprecated used by old timing api(setState timing flag)
29
43
  */
@@ -39,13 +53,13 @@ let globalPipelineOptions: PipelineOptions | undefined;
39
53
  */
40
54
  function markTimingLegacy(key: PerformanceTimingKeys, timingFlag_?: string): void {
41
55
  switch (key) {
42
- case PerformanceTimingKeys.update_set_state_trigger: {
56
+ case PerformanceTimingKeys.updateSetStateTrigger: {
43
57
  shouldMarkDiffVdomStart = true;
44
58
  shouldMarkDiffVdomEnd = true;
45
59
  timingFlag = timingFlag_;
46
60
  break;
47
61
  }
48
- case PerformanceTimingKeys.update_diff_vdom_start: {
62
+ case PerformanceTimingKeys.updateDiffVdomStart: {
49
63
  /* v8 ignore start */
50
64
  if (!shouldMarkDiffVdomStart) {
51
65
  return;
@@ -54,7 +68,7 @@ function markTimingLegacy(key: PerformanceTimingKeys, timingFlag_?: string): voi
54
68
  shouldMarkDiffVdomStart = false;
55
69
  break;
56
70
  }
57
- case PerformanceTimingKeys.update_diff_vdom_end: {
71
+ case PerformanceTimingKeys.updateDiffVdomEnd: {
58
72
  if (!shouldMarkDiffVdomEnd) {
59
73
  return;
60
74
  }
@@ -65,11 +79,26 @@ function markTimingLegacy(key: PerformanceTimingKeys, timingFlag_?: string): voi
65
79
  lynx.getNativeApp().markTiming?.(timingFlag!, PerformanceTimingKeys[key]);
66
80
  }
67
81
 
68
- function beginPipeline(needTimestamps: boolean, timingFlag?: string): void {
82
+ function beginPipeline(needTimestamps: boolean, pipelineOrigin: PipelineOrigin, timingFlag?: string): void {
69
83
  globalPipelineOptions = lynx.performance?._generatePipelineOptions?.();
70
84
  if (globalPipelineOptions) {
71
85
  globalPipelineOptions.needTimestamps = needTimestamps;
72
- lynx.performance?._onPipelineStart?.(globalPipelineOptions.pipelineID);
86
+ globalPipelineOptions.pipelineOrigin = pipelineOrigin;
87
+ globalPipelineOptions.dsl = 'reactLynx';
88
+ switch (pipelineOrigin) {
89
+ case PipelineOrigins.reactLynxHydrate:
90
+ globalPipelineOptions.stage = 'hydrate';
91
+ break;
92
+ case PipelineOrigins.updateTriggeredByBts:
93
+ globalPipelineOptions.stage = 'update';
94
+ break;
95
+ }
96
+
97
+ if (isSdkVersionGt(3, 0)) {
98
+ lynx.performance?._onPipelineStart?.(globalPipelineOptions.pipelineID, globalPipelineOptions);
99
+ } else {
100
+ lynx.performance?._onPipelineStart?.(globalPipelineOptions.pipelineID);
101
+ }
73
102
  if (timingFlag) {
74
103
  lynx.performance?._bindPipelineIdWithTimingFlag?.(globalPipelineOptions.pipelineID, timingFlag);
75
104
  }
@@ -92,11 +121,11 @@ function initTimingAPI(): void {
92
121
  // check `__globalSnapshotPatch` to make sure this only runs after hydrate
93
122
  if (__JS__ && __globalSnapshotPatch) {
94
123
  if (!globalPipelineOptions) {
95
- beginPipeline(false);
96
- markTiming(PerformanceTimingKeys.diff_vdom_start, true);
124
+ beginPipeline(false, PipelineOrigins.updateTriggeredByBts);
125
+ markTiming(PerformanceTimingKeys.diffVdomStart, true);
97
126
  }
98
127
  if (shouldMarkDiffVdomStart) {
99
- markTimingLegacy(PerformanceTimingKeys.update_diff_vdom_start);
128
+ markTimingLegacy(PerformanceTimingKeys.updateDiffVdomStart);
100
129
  }
101
130
  }
102
131
  oldDiff?.(vnode);
@@ -108,6 +137,8 @@ function initTimingAPI(): void {
108
137
  */
109
138
  export {
110
139
  PerformanceTimingKeys,
140
+ PerformanceTimingFlags,
141
+ PipelineOrigins,
111
142
  PerfSpecificKey,
112
143
  markTimingLegacy,
113
144
  initTimingAPI,
@@ -0,0 +1,52 @@
1
+ import { options } from 'preact';
2
+ import type { VNode } from 'preact';
3
+ import { COMPONENT, DIFF, DIFFED, FORCE } from '../renderToOpcodes/constants.js';
4
+
5
+ export function runWithForce(cb: () => void): void {
6
+ // save vnode and its `_component` in WeakMap
7
+ const m = new WeakMap<VNode, any>();
8
+
9
+ const oldDiff = options[DIFF];
10
+
11
+ options[DIFF] = (vnode: VNode) => {
12
+ if (oldDiff) {
13
+ oldDiff(vnode);
14
+ }
15
+
16
+ // when `options[DIFF]` is called, a newVnode is passed in
17
+ // so its `vnode[COMPONENT]` should be null,
18
+ // but it will be set later
19
+ Object.defineProperty(vnode, COMPONENT, {
20
+ configurable: true,
21
+ set(c) {
22
+ m.set(vnode, c);
23
+ if (c) {
24
+ c[FORCE] = true;
25
+ }
26
+ },
27
+ get() {
28
+ return m.get(vnode);
29
+ },
30
+ });
31
+ };
32
+
33
+ const oldDiffed = options[DIFFED];
34
+
35
+ options[DIFFED] = (vnode: VNode) => {
36
+ if (oldDiffed) {
37
+ oldDiffed(vnode);
38
+ }
39
+
40
+ // delete is a reverse operation of previous `Object.defineProperty`
41
+ delete vnode[COMPONENT];
42
+ // restore
43
+ vnode[COMPONENT] = m.get(vnode);
44
+ };
45
+
46
+ try {
47
+ cb();
48
+ } finally {
49
+ options[DIFF] = oldDiff as (vnode: VNode) => void;
50
+ options[DIFFED] = oldDiffed as (vnode: VNode) => void;
51
+ }
52
+ }
@@ -1,72 +1,35 @@
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 { options } from 'preact';
5
- import type { VNode } from 'preact';
6
-
7
4
  import { LifecycleConstant, NativeUpdateDataType } from '../lifecycleConstant.js';
8
- import { PerformanceTimingKeys, beginPipeline, markTiming } from './performance.js';
5
+ import {
6
+ PerformanceTimingKeys,
7
+ PerformanceTimingFlags,
8
+ PipelineOrigins,
9
+ beginPipeline,
10
+ markTiming,
11
+ } from './performance.js';
9
12
  import { BackgroundSnapshotInstance, hydrate } from '../backgroundSnapshot.js';
10
13
  import { destroyBackground } from '../lifecycle/destroy.js';
11
14
  import { delayedEvents, delayedPublishEvent } from '../lifecycle/event/delayEvents.js';
12
15
  import { delayLifecycleEvent, delayedLifecycleEvents } from '../lifecycle/event/delayLifecycleEvents.js';
13
- import { commitPatchUpdate, genCommitTaskId, globalCommitTaskMap } from '../lifecycle/patch/commit.js';
16
+ import {
17
+ clearPatchesToCommit,
18
+ commitPatchUpdate,
19
+ genCommitTaskId,
20
+ globalCommitTaskMap,
21
+ patchesToCommit,
22
+ type PatchList,
23
+ } from '../lifecycle/patch/commit.js';
14
24
  import { reloadBackground } from '../lifecycle/reload.js';
15
25
  import { renderBackground } from '../lifecycle/render.js';
16
- import { CHILDREN, COMPONENT, DIFF, DIFFED, FORCE } from '../renderToOpcodes/constants.js';
26
+ import { CHILDREN } from '../renderToOpcodes/constants.js';
17
27
  import { __root } from '../root.js';
18
28
  import { globalRefsToSet, updateBackgroundRefs } from '../snapshot/ref.js';
19
29
  import { backgroundSnapshotInstanceManager } from '../snapshot.js';
20
30
  import { destroyWorklet } from '../worklet/destroy.js';
21
-
22
- export function runWithForce(cb: () => void): void {
23
- // save vnode and its `_component` in WeakMap
24
- const m = new WeakMap<VNode, any>();
25
-
26
- const oldDiff = options[DIFF];
27
-
28
- options[DIFF] = (vnode: VNode) => {
29
- if (oldDiff) {
30
- oldDiff(vnode);
31
- }
32
-
33
- // when `options[DIFF]` is called, a newVnode is passed in
34
- // so its `vnode[COMPONENT]` should be null,
35
- // but it will be set later
36
- Object.defineProperty(vnode, COMPONENT, {
37
- configurable: true,
38
- set(c) {
39
- m.set(vnode, c);
40
- if (c) {
41
- c[FORCE] = true;
42
- }
43
- },
44
- get() {
45
- return m.get(vnode);
46
- },
47
- });
48
- };
49
-
50
- const oldDiffed = options[DIFFED];
51
-
52
- options[DIFFED] = (vnode: VNode) => {
53
- if (oldDiffed) {
54
- oldDiffed(vnode);
55
- }
56
-
57
- // delete is a reverse operation of previous `Object.defineProperty`
58
- delete vnode[COMPONENT];
59
- // restore
60
- vnode[COMPONENT] = m.get(vnode);
61
- };
62
-
63
- try {
64
- cb();
65
- } finally {
66
- options[DIFF] = oldDiff as (vnode: VNode) => void;
67
- options[DIFFED] = oldDiffed as (vnode: VNode) => void;
68
- }
69
- }
31
+ import { runWithForce } from './runWithForce.js';
32
+ export { runWithForce };
70
33
 
71
34
  function injectTt(): void {
72
35
  // @ts-ignore
@@ -100,7 +63,7 @@ function onLifecycleEvent([type, data]: [string, any]) {
100
63
  }
101
64
 
102
65
  try {
103
- void onLifecycleEventImpl(type, data);
66
+ onLifecycleEventImpl(type, data);
104
67
  } catch (e) {
105
68
  lynx.reportError(e as Error);
106
69
  }
@@ -110,18 +73,18 @@ function onLifecycleEvent([type, data]: [string, any]) {
110
73
  }
111
74
  }
112
75
 
113
- async function onLifecycleEventImpl(type: string, data: any): Promise<void> {
76
+ function onLifecycleEventImpl(type: string, data: any): void {
114
77
  switch (type) {
115
78
  case LifecycleConstant.firstScreen: {
116
79
  const { root: lepusSide, refPatch, jsReadyEventIdSwap } = data;
117
80
  if (__PROFILE__) {
118
81
  console.profile('hydrate');
119
82
  }
120
- beginPipeline(true, 'react_lynx_hydrate');
121
- markTiming(PerformanceTimingKeys.hydrate_parse_snapshot_start);
83
+ beginPipeline(true, PipelineOrigins.reactLynxHydrate, PerformanceTimingFlags.reactLynxHydrate);
84
+ markTiming(PerformanceTimingKeys.hydrateParseSnapshotStart);
122
85
  const before = JSON.parse(lepusSide);
123
- markTiming(PerformanceTimingKeys.hydrate_parse_snapshot_end);
124
- markTiming(PerformanceTimingKeys.diff_vdom_start);
86
+ markTiming(PerformanceTimingKeys.hydrateParseSnapshotEnd);
87
+ markTiming(PerformanceTimingKeys.diffVdomStart);
125
88
  const snapshotPatch = hydrate(
126
89
  before,
127
90
  __root as BackgroundSnapshotInstance,
@@ -129,7 +92,7 @@ async function onLifecycleEventImpl(type: string, data: any): Promise<void> {
129
92
  if (__PROFILE__) {
130
93
  console.profileEnd();
131
94
  }
132
- markTiming(PerformanceTimingKeys.diff_vdom_end);
95
+ markTiming(PerformanceTimingKeys.diffVdomEnd);
133
96
 
134
97
  // TODO: It seems `delayedEvents` and `delayedLifecycleEvents` should be merged into one array to ensure the proper order of events.
135
98
  flushDelayedLifecycleEvents();
@@ -166,14 +129,23 @@ async function onLifecycleEventImpl(type: string, data: any): Promise<void> {
166
129
  console.profile('commitChanges');
167
130
  }
168
131
  const commitTaskId = genCommitTaskId();
169
- await commitPatchUpdate({ patchList: [{ snapshotPatch, id: commitTaskId }] }, { isHydration: true });
170
- updateBackgroundRefs(commitTaskId);
171
- globalCommitTaskMap.forEach((commitTask, id) => {
172
- if (id > commitTaskId) {
173
- return;
174
- }
175
- commitTask();
176
- globalCommitTaskMap.delete(id);
132
+ patchesToCommit.push(
133
+ { snapshotPatch, id: commitTaskId },
134
+ );
135
+ const patchList: PatchList = {
136
+ patchList: patchesToCommit,
137
+ };
138
+ clearPatchesToCommit();
139
+ const obj = commitPatchUpdate(patchList, { isHydration: true });
140
+ lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => {
141
+ updateBackgroundRefs(commitTaskId);
142
+ globalCommitTaskMap.forEach((commitTask, id) => {
143
+ if (id > commitTaskId) {
144
+ return;
145
+ }
146
+ commitTask();
147
+ globalCommitTaskMap.delete(id);
148
+ });
177
149
  });
178
150
  break;
179
151
  }
@@ -15,6 +15,7 @@ import { setupLynxEnv } from './lynx/env.js';
15
15
  import { injectLepusMethods } from './lynx/injectLepusMethods.js';
16
16
  import { initTimingAPI } from './lynx/performance.js';
17
17
  import { injectTt } from './lynx/tt.js';
18
+ export { runWithForce } from './lynx/runWithForce.js';
18
19
 
19
20
  // @ts-expect-error Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature
20
21
  if (__LEPUS__ && typeof globalThis.processEvalResult === 'undefined') {
@@ -0,0 +1,70 @@
1
+ # @lynx-js/react/testing-library
2
+
3
+ ReactLynx Testing Library is a simple and complete ReactLynx unit testing library that encourages good testing practices.
4
+
5
+ > Inspired completely by [react-testing-library](https://github.com/testing-library/react-testing-library)
6
+
7
+ Similar to [react-testing-library](https://github.com/testing-library/react-testing-library), this library is designed to test your ReactLynx components in the same way you would test React components using react-testing-library.
8
+
9
+ ## Setup
10
+
11
+ Setup vitest:
12
+
13
+ ```js
14
+ // vitest.config.js
15
+ import { defineConfig, mergeConfig } from 'vitest/config';
16
+ import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config';
17
+
18
+ const defaultConfig = createVitestConfig();
19
+ const config = defineConfig({
20
+ test: {
21
+ // ...
22
+ },
23
+ });
24
+
25
+ export default mergeConfig(defaultConfig, config);
26
+ ```
27
+
28
+ Then you can start writing tests and run them with vitest!
29
+
30
+ ## Usage
31
+
32
+ ```js
33
+ import '@testing-library/jest-dom';
34
+ import { test, expect } from 'vitest';
35
+ import { render } from '@lynx-js/react/testing-library';
36
+
37
+ test('renders options.wrapper around node', async () => {
38
+ const WrapperComponent = ({ children }) => (
39
+ <view data-testid='wrapper'>{children}</view>
40
+ );
41
+ const Comp = () => {
42
+ return <view data-testid='inner' style='background-color: yellow;' />;
43
+ };
44
+ const { container, getByTestId } = render(<Comp />, {
45
+ wrapper: WrapperComponent,
46
+ });
47
+ expect(getByTestId('wrapper')).toBeInTheDocument();
48
+ expect(container.firstChild).toMatchInlineSnapshot(`
49
+ <view
50
+ data-testid="wrapper"
51
+ >
52
+ <view
53
+ data-testid="inner"
54
+ style="background-color: yellow;"
55
+ />
56
+ </view>
57
+ `);
58
+ });
59
+ ```
60
+
61
+ 💡 Since our testing environment (`@lynx-js/test-environment`) is based on jsdom, You may also be interested in installing `@testing-library/jest-dom` so you can use
62
+ [the custom jest matchers](https://github.com/testing-library/jest-dom).
63
+
64
+ ## Examples
65
+
66
+ See our [examples](https://github.com/lynx-family/lynx-stack/tree/main/packages/react/testing-library/src/__tests__) for more usage.
67
+
68
+ ## Credits
69
+
70
+ - [Testing Library](https://testing-library.com/) for the testing utilities and good practices for React testing.