diff --git a/.changeset/dlxg-utsc-owqi.md b/.changeset/dlxg-utsc-owqi.md new file mode 100644 index 0000000000..a78783f6d9 --- /dev/null +++ b/.changeset/dlxg-utsc-owqi.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/react": minor +--- + +Refactor preact to support multi-slots children and reduce the number and depth of snapshot. diff --git a/.changeset/flat-bats-create.md b/.changeset/flat-bats-create.md new file mode 100644 index 0000000000..9fc16dd8c5 --- /dev/null +++ b/.changeset/flat-bats-create.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/react-rsbuild-plugin": patch +--- + +Supports @lynx-js/react 0.120.0 diff --git a/.github/react-runtime.instructions.md b/.github/react-runtime.instructions.md new file mode 100644 index 0000000000..cc2b05cd23 --- /dev/null +++ b/.github/react-runtime.instructions.md @@ -0,0 +1,6 @@ +--- +applyTo: "packages/react/runtime/**" +--- + +When updating runtime snapshot tests for preview preact builds that carry named children props like `$0` and slot-index semantics, keep `list` host snapshots isolated from surrounding `view`/`text` snapshots by creating a standalone `__SNAPSHOT__({HOLE})` and inserting it into the outer snapshot. +Do not drop `packages/react/runtime/__test__/list.test.jsx` cases added on `main`; preserve nested-list recording and `__DestroyLifetime` callback cleanup assertions while adapting expectations to the new snapshot structure. diff --git a/.github/react-transform.instructions.md b/.github/react-transform.instructions.md index 2228cdcbca..6a0623319c 100644 --- a/.github/react-transform.instructions.md +++ b/.github/react-transform.instructions.md @@ -2,6 +2,7 @@ applyTo: "packages/react/transform/**/*" --- +When validating SWC transform snapshot changes, update the Rust fixtures with `UPDATE=1 cargo test -p swc_plugin_snapshot` and `UPDATE=1 cargo test -p swc_plugin_list --features napi` so the stored snapshots match the current transform output. When a crate exposes both core Rust structs and `napi` wrapper structs with the same semantic shape, keep internal transform pipelines and shared `Rc>` state on the core types and convert to the `napi` types only at the JS boundary. Do not mix `swc_plugin_*::napi::*` record types into internal plugin wiring such as `.with_*_records(...)`, or wasm builds can fail with mismatched type errors. When recording source locations from SWC spans, guard `SourceMap::lookup_char_pos` for synthetic spans such as `DUMMY_SP` (`span.lo == 0`). Compat and other transforms may synthesize JSX nodes with default spans, and wasm builds can surface panics from source map lookups on those spans as `RuntimeError: unreachable`. Expose recorded columns as 1-based values so `uiSourceMapRecords` can be fed directly into editor locations such as VS Code without an extra offset conversion. diff --git a/packages/react-umd/rslib.config.ts b/packages/react-umd/rslib.config.ts index 82f936f186..aed02459ff 100644 --- a/packages/react-umd/rslib.config.ts +++ b/packages/react-umd/rslib.config.ts @@ -15,4 +15,7 @@ export default defineExternalBundleRslibConfig({ cleanDistPath: false, distPath: './dist', }, + performance: { + buildCache: false, + }, }); diff --git a/packages/react/package.json b/packages/react/package.json index 0f344d5738..5bd27b01f6 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -196,7 +196,7 @@ "build": "rslib build" }, "dependencies": { - "preact": "npm:@lynx-js/internal-preact@10.28.4-2d1d67a" + "preact": "npm:@lynx-js/internal-preact@10.28.4-dfff9aa" }, "devDependencies": { "@lynx-js/types": "3.7.0", diff --git a/packages/react/runtime/__test__/basic.test.jsx b/packages/react/runtime/__test__/basic.test.jsx index ede6922cd9..f4d7b0fced 100644 --- a/packages/react/runtime/__test__/basic.test.jsx +++ b/packages/react/runtime/__test__/basic.test.jsx @@ -102,6 +102,8 @@ describe('insertBefore', () => { const b = new SnapshotInstance(snapshot2); const c = new SnapshotInstance(snapshot2); + b.__slotIndex = 0; + c.__slotIndex = 1; a.insertBefore(b); a.insertBefore(c); @@ -114,23 +116,27 @@ describe('insertBefore', () => { /> - - - - - + + + + + + + - - - - - + + + + + + + `); @@ -154,14 +160,7 @@ describe('insertBefore', () => { const a = new SnapshotInstance(snapshot1); a.ensureElements(); - // Insert two wrappers for stable slot index - const b = new SnapshotInstance('wrapper'); - const c = new SnapshotInstance('wrapper'); expect(a.__current_slot_index).toBe(0); - a.insertBefore(b); - expect(a.__current_slot_index).toBe(1); - a.insertBefore(c); - expect(a.__current_slot_index).toBe(2); expect(a.__element_root).toMatchInlineSnapshot(` @@ -175,12 +174,14 @@ describe('insertBefore', () => { `); const d = new SnapshotInstance(snapshot2); + d.__slotIndex = 0; const e = new SnapshotInstance(snapshot2); + e.__slotIndex = 1; - b.insertBefore(d); - c.insertBefore(e); + a.insertBefore(d); + a.insertBefore(e); - expect(a.__current_slot_index).toBe(2); + expect(a.__current_slot_index).toBe(0); expect(a.__element_root).toMatchInlineSnapshot(` @@ -209,9 +210,9 @@ describe('insertBefore', () => { `); - b.removeChild(d); + a.removeChild(d); - expect(a.__current_slot_index).toBe(2); + expect(a.__current_slot_index).toBe(0); expect(a.__element_root).toMatchInlineSnapshot(` @@ -252,6 +253,9 @@ describe('insertBefore', () => { const a = new SnapshotInstance(snapshot1); const b = new SnapshotInstance(snapshot2); const c = new SnapshotInstance(snapshot2); + b.__slotIndex = 0; + c.__slotIndex = 1; + a.insertBefore(b); a.insertBefore(c); @@ -265,23 +269,27 @@ describe('insertBefore', () => { /> - - - - - + + + + + + + - - - - - + + + + + + + `); @@ -467,42 +475,88 @@ describe('setAttribute', () => { describe('dynamic key in snapshot', () => { it('multiple slots 0', () => { + const snapshotFoo = __SNAPSHOT__(foo); + const snapshotBar = __SNAPSHOT__(bar); + const snapshotWithDynamicKey = __SNAPSHOT__( + + + {snapshotFoo} + + + {snapshotBar} + + , + ); + const snapshot = __SNAPSHOT__( - - - {foo} - - - {bar} - - + {snapshotWithDynamicKey} , ); const a = new SnapshotInstance(snapshot); a.ensureElements(); + expect(a.__element_root).toMatchInlineSnapshot(``); + + const b = new SnapshotInstance(snapshotWithDynamicKey); + b.__slotIndex = 0; + + a.insertBefore(b); + expect(a.__element_root).toMatchInlineSnapshot(` - + + + + + `); + + const foo = new SnapshotInstance(snapshotFoo); + foo.__slotIndex = 0; + const bar = new SnapshotInstance(snapshotBar); + bar.__slotIndex = 1; + b.insertBefore(foo); + b.insertBefore(bar); + + expect(a.__element_root).toMatchInlineSnapshot(` + + + + + + + + + + + + `); }); it('multiple slots 2', () => { + const snapshotFoo = __SNAPSHOT__(foo); + const snapshotBar = __SNAPSHOT__(bar); const snapshot = __SNAPSHOT__( - {foo} + {snapshotFoo} - {bar} + {snapshotBar} , @@ -515,23 +569,61 @@ describe('dynamic key in snapshot', () => { - + + + + `); - }); - it('multiple slots 3', () => { - const snapshot = __SNAPSHOT__( - - Hello {HOLE} - + const foo = new SnapshotInstance(snapshotFoo); + foo.__slotIndex = 0; + const bar = new SnapshotInstance(snapshotBar); + bar.__slotIndex = 1; + a.insertBefore(foo); + a.insertBefore(bar); + + expect(a.__element_root).toMatchInlineSnapshot(` + + - {foo} + + + - {bar} + + + + + `); + }); + + it('multiple slots 3', () => { + const snapshotFoo = __SNAPSHOT__(foo); + const snapshotBar = __SNAPSHOT__(bar); + const snapshotDynamic = __SNAPSHOT__( + + + {snapshotFoo} + + + {snapshotBar} + + , + ); + const snapshot = __SNAPSHOT__( + + Hello {HOLE} + {snapshotDynamic} , ); @@ -546,11 +638,68 @@ describe('dynamic key in snapshot', () => { /> - + + + `); + + const b = new SnapshotInstance(snapshotDynamic); + b.__slotIndex = 1; + a.insertBefore(b); + + expect(a.__element_root).toMatchInlineSnapshot(` + + + - + + + + + + + + + `); + + const foo = new SnapshotInstance(snapshotFoo); + foo.__slotIndex = 0; + const bar = new SnapshotInstance(snapshotBar); + bar.__slotIndex = 1; + b.insertBefore(foo); + b.insertBefore(bar); + + expect(a.__element_root).toMatchInlineSnapshot(` + + + + + + + + + + + + + + + + + + + `); }); diff --git a/packages/react/runtime/__test__/debug/backgroundSnapshot-profile.test.jsx b/packages/react/runtime/__test__/debug/backgroundSnapshot-profile.test.jsx index 8f94a51a07..78170ee72e 100644 --- a/packages/react/runtime/__test__/debug/backgroundSnapshot-profile.test.jsx +++ b/packages/react/runtime/__test__/debug/backgroundSnapshot-profile.test.jsx @@ -55,11 +55,14 @@ function createBeforeTree() { const root = new SnapshotInstance(ROOT); const a = new SnapshotInstance(ITEM_A); + a.__slotIndex = 0; a.setAttribute(0, 'a-old'); a.setAttribute('meta', 'meta-old'); const b = new SnapshotInstance(ITEM_B); + b.__slotIndex = 0; const c = new SnapshotInstance(ITEM_C); + c.__slotIndex = 0; root.insertBefore(a); root.insertBefore(b); @@ -72,7 +75,9 @@ function createAfterTree(metaValue) { const root = new BackgroundSnapshotInstance(ROOT); const b = new BackgroundSnapshotInstance(ITEM_B); + b.__slotIndex = 0; const a = new BackgroundSnapshotInstance(ITEM_A); + a.__slotIndex = 0; a.setAttribute(0, 'a-new'); a.setAttribute('meta', metaValue); @@ -82,11 +87,43 @@ function createAfterTree(metaValue) { return root; } +function createBeforeTreeWithProfileInsert() { + const root = new SnapshotInstance(ROOT); + const a = new SnapshotInstance(ITEM_A); + a.__slotIndex = 0; + const c = new SnapshotInstance(ITEM_C); + c.__slotIndex = 0; + + root.insertBefore(a); + root.insertBefore(c); + + return JSON.parse(JSON.stringify(root)); +} + +function createAfterTreeWithProfileInsert() { + const root = new BackgroundSnapshotInstance(ROOT); + const b = new BackgroundSnapshotInstance(ITEM_B); + b.__slotIndex = 0; + const a = new BackgroundSnapshotInstance(ITEM_A); + a.__slotIndex = 0; + const c = new BackgroundSnapshotInstance(ITEM_C); + c.__slotIndex = 0; + + root.insertBefore(b); + root.insertBefore(a); + root.insertBefore(c); + + return root; +} + function createBeforeTreeWithDefinedTargetMove() { const root = new SnapshotInstance(ROOT); const a = new SnapshotInstance(ITEM_A); + a.__slotIndex = 0; const b = new SnapshotInstance(ITEM_B); + b.__slotIndex = 0; const c = new SnapshotInstance(ITEM_C); + c.__slotIndex = 0; root.insertBefore(a); root.insertBefore(b); @@ -98,8 +135,11 @@ function createBeforeTreeWithDefinedTargetMove() { function createAfterTreeWithDefinedTargetMove() { const root = new BackgroundSnapshotInstance(ROOT); const b = new BackgroundSnapshotInstance(ITEM_B); + b.__slotIndex = 0; const a = new BackgroundSnapshotInstance(ITEM_A); + a.__slotIndex = 0; const c = new BackgroundSnapshotInstance(ITEM_C); + c.__slotIndex = 0; root.insertBefore(b); root.insertBefore(a); @@ -224,7 +264,7 @@ describe('backgroundSnapshot profile', () => { }), expect.objectContaining({ op: SnapshotOperation.InsertBefore, - args: [before.id, before.children[0].id, undefined], + args: [before.id, before.children[0].id, undefined, 0], }), ]), ); @@ -256,6 +296,58 @@ describe('backgroundSnapshot profile', () => { expect(insertBeforeCalls.some(([, option]) => option?.args?.targetId === '')).toBe(true); }); + it('should profile reconstructInstanceTree for inserted children', () => { + globalThis.__PROFILE__ = true; + + const before = createBeforeTreeWithProfileInsert(); + const after = createAfterTreeWithProfileInsert(); + + lynx.performance.profileStart.mockClear(); + lynx.performance.profileEnd.mockClear(); + + hydrate(before, after); + + const reconstructCalls = lynx.performance.profileStart.mock.calls.filter( + ([traceName]) => traceName === 'ReactLynx::BSI::reconstructInstanceTree', + ); + + expect(reconstructCalls).toHaveLength(1); + expect(reconstructCalls[0][1]).toEqual( + expect.objectContaining({ + args: expect.objectContaining({ + id: String(after.childNodes[0].__id), + snapshotType: after.childNodes[0].type, + }), + }), + ); + }); + + it('should profile move branch with defined target id', () => { + globalThis.__PROFILE__ = true; + + const before = createBeforeTreeWithDefinedTargetMove(); + const after = createAfterTreeWithDefinedTargetMove(); + + lynx.performance.profileStart.mockClear(); + lynx.performance.profileEnd.mockClear(); + + const patch = hydrate(before, after); + const operations = decodePatch(patch); + const moveWithDefinedTarget = operations.find(({ op, args }) => ( + op === SnapshotOperation.InsertBefore + && args[0] === before.id + && args[2] === before.children[2].id + )); + const insertBeforeCalls = lynx.performance.profileStart.mock.calls.filter( + ([traceName]) => traceName === 'ReactLynx::hydrate::insertBefore', + ); + + expect(moveWithDefinedTarget).toBeDefined(); + expect( + insertBeforeCalls.some(([, option]) => option?.args?.targetId === String(before.children[2].id)), + ).toBe(true); + }); + it('should apply non-profile move branch with defined target id', () => { globalThis.__PROFILE__ = false; diff --git a/packages/react/runtime/__test__/debug/formatPatch.test.ts b/packages/react/runtime/__test__/debug/formatPatch.test.ts index cd85df45bf..57baa68610 100644 --- a/packages/react/runtime/__test__/debug/formatPatch.test.ts +++ b/packages/react/runtime/__test__/debug/formatPatch.test.ts @@ -12,6 +12,7 @@ describe('formatPatch', () => { 1, 2, undefined, + 0, SnapshotOperation.RemoveChild, 1, 2, @@ -32,7 +33,7 @@ describe('formatPatch', () => { const formatted = prettyFormatSnapshotPatch(snapshotPatch); expect(formatted).toEqual([ { op: 'CreateElement', type: 'span', id: 2 }, - { op: 'InsertBefore', parentId: 1, childId: 2, beforeId: undefined }, + { op: 'InsertBefore', parentId: 1, childId: 2, beforeId: undefined, slotIndex: 0 }, { op: 'RemoveChild', parentId: 1, childId: 2 }, { op: 'SetAttribute', id: 2, dynamicPartIndex: 1, value: 'disabled' }, { op: 'SetAttributes', id: 2, values: { hidden: true } }, diff --git a/packages/react/runtime/__test__/delayed-lifecycle-events.test.jsx b/packages/react/runtime/__test__/delayed-lifecycle-events.test.jsx index c3684ff0b5..77d43cbc5d 100644 --- a/packages/react/runtime/__test__/delayed-lifecycle-events.test.jsx +++ b/packages/react/runtime/__test__/delayed-lifecycle-events.test.jsx @@ -31,7 +31,7 @@ describe('delayedLifecycleEvents', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_1"}]}", + "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_1","__slotIndex":0}]}", }, ], ], @@ -44,7 +44,7 @@ describe('delayedLifecycleEvents', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_1"}]}", + "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_1","__slotIndex":0}]}", }, ], ] diff --git a/packages/react/runtime/__test__/hydrate.test.jsx b/packages/react/runtime/__test__/hydrate.test.jsx index 0f77fb1bff..7a234b995f 100644 --- a/packages/react/runtime/__test__/hydrate.test.jsx +++ b/packages/react/runtime/__test__/hydrate.test.jsx @@ -49,8 +49,11 @@ describe('dual-runtime hydrate', () => { const a = new SnapshotInstance(s); a.ensureElements(); const b1 = new SnapshotInstance(s1); + b1.__slotIndex = 0; const b2 = new SnapshotInstance(s1); + b2.__slotIndex = 0; const b3 = new SnapshotInstance(s1); + b3.__slotIndex = 0; b1.setAttribute(0, 'id~'); a.insertBefore(b1); a.insertBefore(b2); @@ -98,10 +101,15 @@ describe('dual-runtime hydrate', () => { const aa = new BackgroundSnapshotInstance(s); const bb1 = new BackgroundSnapshotInstance(s1); + bb1.__slotIndex = 0; const bb2 = new BackgroundSnapshotInstance(s2); + bb2.__slotIndex = 0; const bb3 = new BackgroundSnapshotInstance(s1); + bb3.__slotIndex = 0; const bb4 = new BackgroundSnapshotInstance(s1); + bb4.__slotIndex = 0; const bb5 = new BackgroundSnapshotInstance(s1); + bb5.__slotIndex = 0; bb1.setAttribute(0, '~id'); bb5.setAttribute(0, '~id2'); aa.insertBefore(bb1); @@ -111,8 +119,11 @@ describe('dual-runtime hydrate', () => { aa.insertBefore(bb5); const cc1 = new BackgroundSnapshotInstance(s3); + cc1.__slotIndex = 0; const cc2 = new BackgroundSnapshotInstance(s3); + cc2.__slotIndex = 0; const cc3 = new BackgroundSnapshotInstance(s1); + cc3.__slotIndex = 0; cc3.setAttribute(0, '~id3'); bb2.insertBefore(cc1); bb2.insertBefore(cc2); @@ -135,6 +146,7 @@ describe('dual-runtime hydrate', () => { 7, undefined, 0, + 0, "__snapshot_a94a8_test_4", 8, 1, @@ -142,6 +154,7 @@ describe('dual-runtime hydrate', () => { 8, undefined, 0, + 0, "__snapshot_a94a8_test_2", 9, 4, @@ -153,11 +166,13 @@ describe('dual-runtime hydrate', () => { 3, 9, undefined, + 0, 1, -1, 3, -3, 0, + 0, "__snapshot_a94a8_test_2", 6, 4, @@ -169,6 +184,7 @@ describe('dual-runtime hydrate', () => { -1, 6, undefined, + 0, ] `); backgroundSnapshotInstanceManager.values.forEach((v, k) => { @@ -181,9 +197,13 @@ describe('dual-runtime hydrate', () => { const a = new SnapshotInstance(s); a.ensureElements(); const b1 = new SnapshotInstance(s1); + b1.__slotIndex = 0; const b2 = new SnapshotInstance(s1); + b2.__slotIndex = 0; const b3 = new SnapshotInstance(s1); + b3.__slotIndex = 0; const b4 = new SnapshotInstance(s1); + b4.__slotIndex = 0; a.insertBefore(b1); a.insertBefore(b2); a.insertBefore(b3); @@ -192,8 +212,11 @@ describe('dual-runtime hydrate', () => { const aa = new BackgroundSnapshotInstance(s); const bb1 = new BackgroundSnapshotInstance(s1); + bb1.__slotIndex = 0; const bb2 = new BackgroundSnapshotInstance(s1); + bb2.__slotIndex = 0; const bb3 = new BackgroundSnapshotInstance(s1); + bb3.__slotIndex = 0; aa.insertBefore(bb1); aa.insertBefore(bb2); aa.insertBefore(bb3); @@ -211,9 +234,13 @@ describe('dual-runtime hydrate', () => { const a = new SnapshotInstance(s); a.ensureElements(); const b1 = new SnapshotInstance(s1); + b1.__slotIndex = 0; const b2 = new SnapshotInstance(s1); + b2.__slotIndex = 0; const b3 = new SnapshotInstance(s2); + b3.__slotIndex = 0; const b4 = new SnapshotInstance(s1); + b4.__slotIndex = 0; a.insertBefore(b1); a.insertBefore(b2); a.insertBefore(b3); @@ -222,9 +249,13 @@ describe('dual-runtime hydrate', () => { const aa = new BackgroundSnapshotInstance(s); const bb1 = new BackgroundSnapshotInstance(s1); + bb1.__slotIndex = 0; const bb2 = new BackgroundSnapshotInstance(s2); + bb2.__slotIndex = 0; const bb3 = new BackgroundSnapshotInstance(s1); + bb3.__slotIndex = 0; const bb4 = new BackgroundSnapshotInstance(s1); + bb4.__slotIndex = 0; aa.insertBefore(bb1); aa.insertBefore(bb2); aa.insertBefore(bb3); @@ -236,6 +267,7 @@ describe('dual-runtime hydrate', () => { -1, -3, -5, + 0, ] `); }); @@ -258,6 +290,7 @@ describe('dual-runtime hydrate', () => { -1, 2, undefined, + undefined, 0, "__snapshot_a94a8_test_3", 3, @@ -265,6 +298,7 @@ describe('dual-runtime hydrate', () => { -1, 3, undefined, + undefined, ] `); }); @@ -287,24 +321,34 @@ describe('dual-runtime hydrate - with slot (multi-children)', () => { const a = new SnapshotInstance(s); a.ensureElements(); const b1 = new SnapshotInstance(slot1); + b1.__slotIndex = 0; const b2 = new SnapshotInstance(slot2); + b2.__slotIndex = 1; a.insertBefore(b1); a.insertBefore(b2); const c1 = new SnapshotInstance(s1); + c1.__slotIndex = 0; const c2 = new SnapshotInstance(s1); + c2.__slotIndex = 0; const c3 = new SnapshotInstance(s1); + c3.__slotIndex = 0; b1.insertBefore(c1); b1.insertBefore(c2); b2.insertBefore(c3); const aa = new BackgroundSnapshotInstance(s); const bb1 = new BackgroundSnapshotInstance(slot1); + bb1.__slotIndex = 0; const bb2 = new BackgroundSnapshotInstance(slot2); + bb2.__slotIndex = 1; aa.insertBefore(bb1); aa.insertBefore(bb2); const cc1 = new BackgroundSnapshotInstance(s1); + cc1.__slotIndex = 0; const cc2 = new BackgroundSnapshotInstance(s1); + cc2.__slotIndex = 0; const cc3 = new BackgroundSnapshotInstance(s1); + cc3.__slotIndex = 0; bb1.insertBefore(cc1); bb2.insertBefore(cc2); bb2.insertBefore(cc3); @@ -321,6 +365,7 @@ describe('dual-runtime hydrate - with slot (multi-children)', () => { -3, 6, undefined, + 0, ] `); }); diff --git a/packages/react/runtime/__test__/lifecycle.test.jsx b/packages/react/runtime/__test__/lifecycle.test.jsx index 29db60d394..ea8b6ed70a 100644 --- a/packages/react/runtime/__test__/lifecycle.test.jsx +++ b/packages/react/runtime/__test__/lifecycle.test.jsx @@ -120,7 +120,7 @@ describe('componentDidMount', () => { expect(mtCallback[0]).toEqual(LifecycleConstant.patchUpdate); expect(mtCallback[1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":6,"snapshotPatch":[0,"__snapshot_a94a8_test_3",2,0,null,3,3,3,0,1,1,2,3,null,1,1,2,null]}]}", + "data": "{"patchList":[{"id":6,"snapshotPatch":[0,"__snapshot_a94a8_test_3",2,0,null,3,3,3,0,1,1,2,3,null,0,1,1,2,null,0]}]}", "patchOptions": { "reloadVersion": 0, }, @@ -167,7 +167,7 @@ describe('componentDidMount', () => { expect(mtCallback[0]).toEqual(LifecycleConstant.patchUpdate); expect(mtCallback[1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":9,"snapshotPatch":[0,"__snapshot_a94a8_test_4",2,0,null,3,3,3,0,1,1,2,3,null,1,1,2,null]}]}", + "data": "{"patchList":[{"id":9,"snapshotPatch":[0,"__snapshot_a94a8_test_4",2,0,null,3,3,3,0,1,1,2,3,null,0,1,1,2,null,0]}]}", "patchOptions": { "reloadVersion": 0, }, @@ -550,7 +550,7 @@ describe('useState', () => { await waitSchedule(); expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1].data).toMatchInlineSnapshot( - `"{"patchList":[{"id":27,"snapshotPatch":[0,"__snapshot_a94a8_test_15",2,4,2,[false,{"str":"str"}],1,-1,2,null]}]}"`, + `"{"patchList":[{"id":27,"snapshotPatch":[0,"__snapshot_a94a8_test_15",2,4,2,[false,{"str":"str"}],1,-1,2,null,0]}]}"`, ); } }); diff --git a/packages/react/runtime/__test__/lifecycle/reload.test.jsx b/packages/react/runtime/__test__/lifecycle/reload.test.jsx index 22bb645e80..9073b307ff 100644 --- a/packages/react/runtime/__test__/lifecycle/reload.test.jsx +++ b/packages/react/runtime/__test__/lifecycle/reload.test.jsx @@ -123,7 +123,7 @@ describe('reload', () => { expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":3,"snapshotPatch":[3,-5,0,{"dataX2":"WorldX2"},3,-7,0,"update",3,-8,0,{"attr":{"dataX2":"WorldX2"}}]}]}", + "data": "{"patchList":[{"id":3,"snapshotPatch":[3,-2,0,{"dataX2":"WorldX2"},3,-4,0,"update",3,-5,0,{"attr":{"dataX2":"WorldX2"}}]}]}", "patchOptions": { "flowIds": [ 666, @@ -234,7 +234,7 @@ describe('reload', () => { expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":4,"snapshotPatch":[3,-7,0,"???"]}]}", + "data": "{"patchList":[{"id":4,"snapshotPatch":[3,-4,0,"???"]}]}", "patchOptions": { "flowIds": [ 666, @@ -305,7 +305,7 @@ describe('reload', () => { [ "rLynxFirstScreen", { - "root": "{"id":-9,"type":"root","children":[{"id":-13,"type":"__snapshot_a94a8_test_2","values":[{"dataX":"WorldX"}],"children":[{"id":-10,"type":"__snapshot_a94a8_test_3","children":[{"id":-14,"type":null,"values":["Enjoy"]}]},{"id":-11,"type":"__snapshot_a94a8_test_4","children":[{"id":-15,"type":null,"values":["World"]}]},{"id":-12,"type":"wrapper","children":[{"id":-16,"type":"__snapshot_a94a8_test_1","values":[{"attr":{"dataX":"WorldX"}}]}]}]}]}", + "root": "{"id":-6,"type":"root","children":[{"id":-7,"type":"__snapshot_a94a8_test_2","values":[{"dataX":"WorldX"}],"children":[{"id":-8,"type":null,"values":["Enjoy"],"__slotIndex":0},{"id":-9,"type":null,"values":["World"],"__slotIndex":1},{"id":-10,"type":"__snapshot_a94a8_test_1","values":[{"attr":{"dataX":"WorldX"}}],"__slotIndex":2}],"__slotIndex":0}]}", }, ], ], @@ -386,7 +386,7 @@ describe('reload', () => { expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":8,"snapshotPatch":[3,-15,0,"update"]}]}", + "data": "{"patchList":[{"id":8,"snapshotPatch":[3,-9,0,"update"]}]}", "patchOptions": { "flowIds": [ 666, @@ -535,7 +535,7 @@ describe('reload', () => { expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":11,"snapshotPatch":[3,-5,0,{"dataX2":"WorldX2"},3,-7,0,"update",3,-8,0,{"attr":{"dataX2":"WorldX2"}}]}]}", + "data": "{"patchList":[{"id":11,"snapshotPatch":[3,-2,0,{"dataX2":"WorldX2"},3,-4,0,"update",3,-5,0,{"attr":{"dataX2":"WorldX2"}}]}]}", "patchOptions": { "flowIds": [ 666, @@ -650,7 +650,7 @@ describe('reload', () => { expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":12,"snapshotPatch":[3,-7,0,"???"]}]}", + "data": "{"patchList":[{"id":12,"snapshotPatch":[3,-4,0,"???"]}]}", "patchOptions": { "flowIds": [ 666, @@ -723,7 +723,7 @@ describe('reload', () => { [ "rLynxFirstScreen", { - "root": "{"id":-9,"type":"root","children":[{"id":-10,"type":"__snapshot_a94a8_test_5","children":[{"id":-14,"type":"__snapshot_a94a8_test_2","values":[{"dataX":"WorldX"}],"children":[{"id":-11,"type":"__snapshot_a94a8_test_3","children":[{"id":-15,"type":null,"values":["Enjoy"]}]},{"id":-12,"type":"__snapshot_a94a8_test_4","children":[{"id":-16,"type":null,"values":["World"]}]},{"id":-13,"type":"wrapper","children":[{"id":-17,"type":"__snapshot_a94a8_test_1","values":[{"attr":{"dataX":"WorldX"}}]}]}]}]}]}", + "root": "{"id":-6,"type":"root","children":[{"id":-7,"type":"__snapshot_a94a8_test_3","children":[{"id":-8,"type":"__snapshot_a94a8_test_2","values":[{"dataX":"WorldX"}],"children":[{"id":-9,"type":null,"values":["Enjoy"],"__slotIndex":0},{"id":-10,"type":null,"values":["World"],"__slotIndex":1},{"id":-11,"type":"__snapshot_a94a8_test_1","values":[{"attr":{"dataX":"WorldX"}}],"__slotIndex":2}],"__slotIndex":0}],"__slotIndex":0}]}", }, ], ], @@ -806,7 +806,7 @@ describe('reload', () => { expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":16,"snapshotPatch":[3,-16,0,"update"]}]}", + "data": "{"patchList":[{"id":16,"snapshotPatch":[3,-10,0,"update"]}]}", "patchOptions": { "flowIds": [ 666, @@ -884,17 +884,17 @@ describe('reload', () => { { "item-key": 0, "position": 0, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 1, "position": 1, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 2, "position": 2, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, ], "removeAction": [], @@ -943,17 +943,17 @@ describe('reload', () => { { "item-key": 0, "position": 0, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 1, "position": 1, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 2, "position": 2, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, ], "removeAction": [], @@ -1022,17 +1022,17 @@ describe('reload', () => { { "item-key": 0, "position": 0, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 1, "position": 1, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 2, "position": 2, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, ], "removeAction": [], @@ -1081,17 +1081,17 @@ describe('reload', () => { { "item-key": 0, "position": 0, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 1, "position": 1, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 2, "position": 2, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, ], "removeAction": [], @@ -1297,24 +1297,18 @@ describe('firstScreenSyncTiming - jsReady', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": { - "-1": -9, - "-10": -18, - "-11": -19, - "-12": -20, - "-13": -21, - "-14": -22, - "-15": -23, - "-16": -24, - "-2": -10, - "-3": -11, - "-4": -12, - "-5": -13, - "-6": -14, - "-7": -15, - "-8": -16, - "-9": -17, + "-1": -6, + "-10": -15, + "-2": -7, + "-3": -8, + "-4": -9, + "-5": -10, + "-6": -11, + "-7": -12, + "-8": -13, + "-9": -14, }, - "root": "{"id":-17,"type":"root","children":[{"id":-21,"type":"__snapshot_a94a8_test_2","values":[{"dataX":"WorldX"}],"children":[{"id":-18,"type":"__snapshot_a94a8_test_3","children":[{"id":-22,"type":null,"values":["Hello 2"]}]},{"id":-19,"type":"__snapshot_a94a8_test_4","children":[{"id":-23,"type":null,"values":["World"]}]},{"id":-20,"type":"wrapper","children":[{"id":-24,"type":"__snapshot_a94a8_test_1","values":[{"attr":{"dataX":"WorldX"}}]}]}]}]}", + "root": "{"id":-11,"type":"root","children":[{"id":-12,"type":"__snapshot_a94a8_test_2","values":[{"dataX":"WorldX"}],"children":[{"id":-13,"type":null,"values":["Hello 2"],"__slotIndex":0},{"id":-14,"type":null,"values":["World"],"__slotIndex":1},{"id":-15,"type":"__snapshot_a94a8_test_1","values":[{"attr":{"dataX":"WorldX"}}],"__slotIndex":2}],"__slotIndex":0}]}", }, ], ] @@ -1322,7 +1316,7 @@ describe('firstScreenSyncTiming - jsReady', () => { expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"snapshotPatch":[2,-17,-21,0,"__snapshot_a94a8_test_5",2,0,"__snapshot_a94a8_test_2",3,4,3,[{"dataX":"WorldX"}],0,"__snapshot_a94a8_test_3",4,0,null,5,4,5,["Hello 2"],1,4,5,null,1,3,4,null,0,"__snapshot_a94a8_test_4",6,0,null,7,4,7,["World"],1,6,7,null,1,3,6,null,0,"wrapper",8,0,"__snapshot_a94a8_test_1",9,4,9,[{"attr":{"dataX":"WorldX"}}],1,8,9,null,1,3,8,null,1,2,3,null,1,-17,2,null],"id":21}]}", + "data": "{"patchList":[{"snapshotPatch":[2,-11,-12,0,"__snapshot_a94a8_test_3",2,0,"__snapshot_a94a8_test_2",3,4,3,[{"dataX":"WorldX"}],0,null,4,4,4,["Hello 2"],1,3,4,null,0,0,null,5,4,5,["World"],1,3,5,null,1,0,"__snapshot_a94a8_test_1",6,4,6,[{"attr":{"dataX":"WorldX"}}],1,3,6,null,2,1,2,3,null,0,1,-11,2,null,0],"id":21}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { @@ -1364,17 +1358,17 @@ describe('firstScreenSyncTiming - jsReady', () => { { "item-key": 0, "position": 0, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 1, "position": 1, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 2, "position": 2, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, ], "removeAction": [], @@ -1404,17 +1398,17 @@ describe('firstScreenSyncTiming - jsReady', () => { { "item-key": 0, "position": 0, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 1, "position": 1, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 2, "position": 2, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, ], "removeAction": [], @@ -1449,17 +1443,17 @@ describe('firstScreenSyncTiming - jsReady', () => { { "item-key": 0, "position": 0, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 1, "position": 1, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, { "item-key": 2, "position": 2, - "type": "__snapshot_a94a8_test_8", + "type": "__snapshot_a94a8_test_6", }, ], "removeAction": [], @@ -1517,7 +1511,7 @@ describe('firstScreenSyncTiming - jsReady', () => { "-5": -13, "-9": -17, }, - "root": "{"id":-17,"type":"root","children":[{"id":-21,"type":"__snapshot_a94a8_test_7","children":[{"id":-18,"type":"__snapshot_a94a8_test_8","values":[{"item-key":0}],"children":[{"id":-22,"type":"__snapshot_a94a8_test_6","values":["a"]}]},{"id":-19,"type":"__snapshot_a94a8_test_8","values":[{"item-key":1}],"children":[{"id":-23,"type":"__snapshot_a94a8_test_6","values":["b"]}]},{"id":-20,"type":"__snapshot_a94a8_test_8","values":[{"item-key":2}],"children":[{"id":-24,"type":"__snapshot_a94a8_test_6","values":["c"]}]}]}]}", + "root": "{"id":-17,"type":"root","children":[{"id":-21,"type":"__snapshot_a94a8_test_5","children":[{"id":-18,"type":"__snapshot_a94a8_test_6","values":[{"item-key":0}],"children":[{"id":-22,"type":"__snapshot_a94a8_test_4","values":["a"],"__slotIndex":0}],"__slotIndex":0},{"id":-19,"type":"__snapshot_a94a8_test_6","values":[{"item-key":1}],"children":[{"id":-23,"type":"__snapshot_a94a8_test_4","values":["b"],"__slotIndex":0}],"__slotIndex":0},{"id":-20,"type":"__snapshot_a94a8_test_6","values":[{"item-key":2}],"children":[{"id":-24,"type":"__snapshot_a94a8_test_4","values":["c"],"__slotIndex":0}],"__slotIndex":0}],"__slotIndex":0}]}", }, ], ] @@ -1579,17 +1573,17 @@ describe('firstScreenSyncTiming - jsReady', () => { { "item-key": 0, "position": 0, - "type": "__snapshot_a94a8_test_10", + "type": "__snapshot_a94a8_test_8", }, { "item-key": 1, "position": 1, - "type": "__snapshot_a94a8_test_10", + "type": "__snapshot_a94a8_test_8", }, { "item-key": 2, "position": 2, - "type": "__snapshot_a94a8_test_10", + "type": "__snapshot_a94a8_test_8", }, ], "removeAction": [], @@ -1619,17 +1613,17 @@ describe('firstScreenSyncTiming - jsReady', () => { { "item-key": 0, "position": 0, - "type": "__snapshot_a94a8_test_10", + "type": "__snapshot_a94a8_test_8", }, { "item-key": 1, "position": 1, - "type": "__snapshot_a94a8_test_10", + "type": "__snapshot_a94a8_test_8", }, { "item-key": 2, "position": 2, - "type": "__snapshot_a94a8_test_10", + "type": "__snapshot_a94a8_test_8", }, ], "removeAction": [], @@ -1681,7 +1675,7 @@ describe('firstScreenSyncTiming - jsReady', () => { "-2": -10, "-6": -14, }, - "root": "{"id":-10,"type":"root","children":[{"id":-14,"type":"__snapshot_a94a8_test_9","children":[{"id":-11,"type":"__snapshot_a94a8_test_10","values":[{"item-key":0}],"children":[{"id":-15,"type":"__snapshot_a94a8_test_6","values":["a"]}]},{"id":-12,"type":"__snapshot_a94a8_test_10","values":[{"item-key":1}],"children":[{"id":-16,"type":"__snapshot_a94a8_test_6","values":["b"]}]},{"id":-13,"type":"__snapshot_a94a8_test_10","values":[{"item-key":2}],"children":[{"id":-17,"type":"__snapshot_a94a8_test_6","values":["c"]}]}]}]}", + "root": "{"id":-10,"type":"root","children":[{"id":-14,"type":"__snapshot_a94a8_test_7","children":[{"id":-11,"type":"__snapshot_a94a8_test_8","values":[{"item-key":0}],"children":[{"id":-15,"type":"__snapshot_a94a8_test_4","values":["a"],"__slotIndex":0}],"__slotIndex":0},{"id":-12,"type":"__snapshot_a94a8_test_8","values":[{"item-key":1}],"children":[{"id":-16,"type":"__snapshot_a94a8_test_4","values":["b"],"__slotIndex":0}],"__slotIndex":0},{"id":-13,"type":"__snapshot_a94a8_test_8","values":[{"item-key":2}],"children":[{"id":-17,"type":"__snapshot_a94a8_test_4","values":["c"],"__slotIndex":0}],"__slotIndex":0}],"__slotIndex":0}]}", }, ], ] @@ -1874,7 +1868,7 @@ describe('firstScreenSyncTiming - jsReady', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-17,"type":"root","children":[{"id":-21,"type":"__snapshot_a94a8_test_2","values":[{"dataX":"WorldX"}],"children":[{"id":-18,"type":"__snapshot_a94a8_test_3","children":[{"id":-22,"type":null,"values":["Hello 2"]}]},{"id":-19,"type":"__snapshot_a94a8_test_4","children":[{"id":-23,"type":null,"values":["World"]}]},{"id":-20,"type":"wrapper","children":[{"id":-24,"type":"__snapshot_a94a8_test_1","values":[{"attr":{"dataX":"WorldX"}}]}]}]}]}", + "root": "{"id":-11,"type":"root","children":[{"id":-12,"type":"__snapshot_a94a8_test_2","values":[{"dataX":"WorldX"}],"children":[{"id":-13,"type":null,"values":["Hello 2"],"__slotIndex":0},{"id":-14,"type":null,"values":["World"],"__slotIndex":1},{"id":-15,"type":"__snapshot_a94a8_test_1","values":[{"attr":{"dataX":"WorldX"}}],"__slotIndex":2}],"__slotIndex":0}]}", }, ], ] @@ -1882,7 +1876,7 @@ describe('firstScreenSyncTiming - jsReady', () => { expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"snapshotPatch":[2,-17,-21,0,"__snapshot_a94a8_test_5",2,0,"__snapshot_a94a8_test_2",3,4,3,[{"dataX":"WorldX"}],0,"__snapshot_a94a8_test_3",4,0,null,5,4,5,["Hello 2"],1,4,5,null,1,3,4,null,0,"__snapshot_a94a8_test_4",6,0,null,7,4,7,["World"],1,6,7,null,1,3,6,null,0,"wrapper",8,0,"__snapshot_a94a8_test_1",9,4,9,[{"attr":{"dataX":"WorldX"}}],1,8,9,null,1,3,8,null,1,2,3,null,1,-17,2,null],"id":27}]}", + "data": "{"patchList":[{"snapshotPatch":[2,-11,-12,0,"__snapshot_a94a8_test_3",2,0,"__snapshot_a94a8_test_2",3,4,3,[{"dataX":"WorldX"}],0,null,4,4,4,["Hello 2"],1,3,4,null,0,0,null,5,4,5,["World"],1,3,5,null,1,0,"__snapshot_a94a8_test_1",6,4,6,[{"attr":{"dataX":"WorldX"}}],1,3,6,null,2,1,2,3,null,0,1,-11,2,null,0],"id":27}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { diff --git a/packages/react/runtime/__test__/list.test.jsx b/packages/react/runtime/__test__/list.test.jsx index 70ea0303e0..600daae508 100644 --- a/packages/react/runtime/__test__/list.test.jsx +++ b/packages/react/runtime/__test__/list.test.jsx @@ -79,9 +79,12 @@ describe('list', () => { const s1 = __SNAPSHOT__( 111 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const s2 = __SNAPSHOT__(World); const s3 = __SNAPSHOT__( @@ -93,17 +96,19 @@ describe('list', () => { a.ensureElements(); const b = new SnapshotInstance(s1); + const b1 = new SnapshotInstance(s11); const c = new SnapshotInstance(s2); a.insertBefore(b); + b.insertBefore(b1); a.insertBefore(c); const d1 = new SnapshotInstance(s3); const d2 = new SnapshotInstance(s3); const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); expect(a.__element_root).toMatchInlineSnapshot(` @@ -119,7 +124,9 @@ describe('list', () => { text="111" /> - + + + { `); - expect(b.childNodes.length).toMatchInlineSnapshot(`3`); + expect(b1.childNodes.length).toMatchInlineSnapshot(`3`); b.insertBefore(d2); b.removeChild(d2); - expect(b.childNodes.length).toMatchInlineSnapshot(`2`); + expect(b1.childNodes.length).toMatchInlineSnapshot(`2`); }); it('list slot count > 1 (the wrapper should be generated)', async function() { const s1 = __SNAPSHOT__( 111 - - {HOLE} - - + {HOLE} {HOLE} , ); + const s11 = __SNAPSHOT__( + + {HOLE} + + , + ); const s2 = __SNAPSHOT__(World); const a = new SnapshotInstance(s); a.ensureElements(); const b = new SnapshotInstance(s1); + const b1 = new SnapshotInstance(s11); const c = new SnapshotInstance(s2); a.insertBefore(b); + b.insertBefore(b1); a.insertBefore(c); expect(a.__element_root).toMatchInlineSnapshot(` @@ -173,8 +185,9 @@ describe('list', () => { text="111" /> - - + + + { const s1 = __SNAPSHOT__( 111 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const s2 = __SNAPSHOT__(World); const s3 = __SNAPSHOT__( @@ -217,36 +233,38 @@ describe(`list "update-list-info"`, () => { a.ensureElements(); const b = new SnapshotInstance(s1); + const b1 = new SnapshotInstance(s11); const c = new SnapshotInstance(s2); a.insertBefore(b); + b.insertBefore(b1); a.insertBefore(c); const d1 = new SnapshotInstance(s3); const d2 = new SnapshotInstance(s3); const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); - expect(b.childNodes.length).toMatchInlineSnapshot(`3`); + expect(b1.childNodes.length).toMatchInlineSnapshot(`3`); expect(__pendingListUpdates.values).toMatchInlineSnapshot(` { - "-6": [ + "-8": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_14", + "type": "__snapshot_a94a8_test_16", }, { "position": 1, - "type": "__snapshot_a94a8_test_14", + "type": "__snapshot_a94a8_test_16", }, { "position": 2, - "type": "__snapshot_a94a8_test_14", + "type": "__snapshot_a94a8_test_16", }, ], "removeAction": [], @@ -262,31 +280,31 @@ describe(`list "update-list-info"`, () => { const d5 = new SnapshotInstance(s3); const d6 = new SnapshotInstance(s4); const d7 = new SnapshotInstance(s4); - b.insertBefore(d4); - b.insertBefore(d5, d2); - b.insertBefore(d6, d2); - b.insertBefore(d7, d2); - b.removeChild(d2); + b1.insertBefore(d4); + b1.insertBefore(d5, d2); + b1.insertBefore(d6, d2); + b1.insertBefore(d7, d2); + b1.removeChild(d2); expect(__pendingListUpdates.values).toMatchInlineSnapshot(` { - "-6": [ + "-8": [ { "insertAction": [ { "position": 1, - "type": "__snapshot_a94a8_test_14", + "type": "__snapshot_a94a8_test_16", }, { "position": 2, - "type": "__snapshot_a94a8_test_15", + "type": "__snapshot_a94a8_test_17", }, { "position": 3, - "type": "__snapshot_a94a8_test_15", + "type": "__snapshot_a94a8_test_17", }, { "position": 5, - "type": "__snapshot_a94a8_test_14", + "type": "__snapshot_a94a8_test_16", }, ], "removeAction": [ @@ -301,15 +319,15 @@ describe(`list "update-list-info"`, () => { { __pendingListUpdates.clearAttachedLists(); - b.insertBefore(d3); // move + b1.insertBefore(d3); // move expect(__pendingListUpdates.values).toMatchInlineSnapshot(` { - "-6": [ + "-8": [ { "insertAction": [ { "position": 5, - "type": "__snapshot_a94a8_test_14", + "type": "__snapshot_a94a8_test_16", }, ], "removeAction": [ @@ -327,13 +345,19 @@ describe(`list "update-list-info"`, () => { const s1 = __SNAPSHOT__( 111 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const b = new SnapshotInstance(s1); b.ensureElements(); + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const s3 = __SNAPSHOT__( World @@ -343,16 +367,16 @@ describe(`list "update-list-info"`, () => { const d1 = new SnapshotInstance(s3); const d2 = new SnapshotInstance(s3); const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); __pendingListUpdates.clearAttachedLists(); d1.setAttribute(0, { 'item-key': 1 }); d3.setAttribute(0, { 'item-key': 3 }); expect(__pendingListUpdates.values).toMatchInlineSnapshot(` { - "-2": [ + "-4": [ { "insertAction": [], "removeAction": [], @@ -362,14 +386,14 @@ describe(`list "update-list-info"`, () => { "from": 0, "item-key": 1, "to": 0, - "type": "__snapshot_a94a8_test_17", + "type": "__snapshot_a94a8_test_20", }, { "flush": false, "from": 2, "item-key": 3, "to": 2, - "type": "__snapshot_a94a8_test_17", + "type": "__snapshot_a94a8_test_20", }, ], }, @@ -389,9 +413,12 @@ describe(`list componentAtIndex`, () => { const s1 = __SNAPSHOT__( 111 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const s3 = __SNAPSHOT__( @@ -402,14 +429,16 @@ describe(`list componentAtIndex`, () => { it('basic componentAtIndex after insert', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const d1 = new SnapshotInstance(s3); const d2 = new SnapshotInstance(s3); const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); // initial there is no child, because "update-list-info" is not flush expect(() => { @@ -418,9 +447,9 @@ describe(`list componentAtIndex`, () => { // only call componentAtIndex after flush __pendingListUpdates.flush(); - expect(elementTree.triggerComponentAtIndex(listRef, 0)).toMatchInlineSnapshot(`4`); - expect(elementTree.triggerComponentAtIndex(listRef, 1)).toMatchInlineSnapshot(`7`); - expect(elementTree.triggerComponentAtIndex(listRef, 2)).toMatchInlineSnapshot(`10`); + expect(elementTree.triggerComponentAtIndex(listRef, 0)).toMatchInlineSnapshot(`5`); + expect(elementTree.triggerComponentAtIndex(listRef, 1)).toMatchInlineSnapshot(`8`); + expect(elementTree.triggerComponentAtIndex(listRef, 2)).toMatchInlineSnapshot(`11`); }); it('remove list si', () => { @@ -429,7 +458,9 @@ describe(`list componentAtIndex`, () => { const b = new SnapshotInstance(s1); a.insertBefore(b); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const listRef = b1.__elements[0]; expect(() => { elementTree.triggerComponentAtIndex(listRef, 0); }).toThrowErrorMatchingInlineSnapshot(`[Error: childCtx not found]`); @@ -437,13 +468,12 @@ describe(`list componentAtIndex`, () => { const d1 = new SnapshotInstance(s3); const d2 = new SnapshotInstance(s3); const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); __pendingListUpdates.flush(); - a.removeChild(b); - + b.removeChild(b1); expect(listRef.componentAtIndex()).toBe(-1); expect(listRef.enqueueComponent()).toBeUndefined(); expect(listRef.componentAtIndexes()).toBeUndefined(); @@ -452,7 +482,9 @@ describe(`list componentAtIndex`, () => { it('should reuse and hydrate', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const s3 = __SNAPSHOT__( @@ -468,12 +500,12 @@ describe(`list componentAtIndex`, () => { const c3 = new SnapshotInstance(s3); const c4 = new SnapshotInstance(s3); const c5 = new SnapshotInstance(s3); - b.insertBefore(c0); - b.insertBefore(c1); - b.insertBefore(c2); - b.insertBefore(c3); - b.insertBefore(c4); - b.insertBefore(c5); + b1.insertBefore(c0); + b1.insertBefore(c1); + b1.insertBefore(c2); + b1.insertBefore(c3); + b1.insertBefore(c4); + b1.insertBefore(c5); // item-key c0.setAttribute(0, { 'item-key': 'key-0' }); @@ -530,32 +562,32 @@ describe(`list componentAtIndex`, () => { { "item-key": "key-0", "position": 0, - "type": "__snapshot_a94a8_test_21", + "type": "__snapshot_a94a8_test_25", }, { "item-key": "key-1", "position": 1, - "type": "__snapshot_a94a8_test_21", + "type": "__snapshot_a94a8_test_25", }, { "item-key": "key-2", "position": 2, - "type": "__snapshot_a94a8_test_21", + "type": "__snapshot_a94a8_test_25", }, { "item-key": "key-3", "position": 3, - "type": "__snapshot_a94a8_test_21", + "type": "__snapshot_a94a8_test_25", }, { "item-key": "key-4", "position": 4, - "type": "__snapshot_a94a8_test_21", + "type": "__snapshot_a94a8_test_25", }, { "item-key": "key-5", "position": 5, - "type": "__snapshot_a94a8_test_21", + "type": "__snapshot_a94a8_test_25", }, ], "removeAction": [], @@ -570,7 +602,7 @@ describe(`list componentAtIndex`, () => { @@ -585,7 +617,7 @@ describe(`list componentAtIndex`, () => { @@ -600,7 +632,7 @@ describe(`list componentAtIndex`, () => { @@ -615,7 +647,7 @@ describe(`list componentAtIndex`, () => { @@ -634,24 +666,34 @@ describe(`list componentAtIndex`, () => { it('should reuse and hydrate - with childNodes', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + b.insertBefore(b1); + const listRef = b1.__elements[0]; const c0 = new SnapshotInstance(_s3); + c0.__slotIndex = 0; const c1 = new SnapshotInstance(_s3); + c1.__slotIndex = 0; const c2 = new SnapshotInstance(_s3); - b.insertBefore(c0); - b.insertBefore(c1); - b.insertBefore(c2); + c2.__slotIndex = 0; + b1.insertBefore(c0); + b1.insertBefore(c1); + b1.insertBefore(c2); const c0_d0 = new SnapshotInstance(_s4); + c0_d0.__slotIndex = 0; const c0_d1 = new SnapshotInstance(_s5); + c0_d1.__slotIndex = 0; c0.insertBefore(c0_d0); c0.insertBefore(c0_d1); const c1_d0 = new SnapshotInstance(_s4); + c1_d0.__slotIndex = 0; c1.insertBefore(c1_d0); const c2_d0 = new SnapshotInstance(_s5); + c2_d0.__slotIndex = 0; c2.insertBefore(c2_d0); __pendingListUpdates.flush(); @@ -742,7 +784,9 @@ describe(`list componentAtIndex`, () => { it('should reuse and hydrate - with childNodes - move', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const s3 = __SNAPSHOT__({HOLE}); const s4 = __SNAPSHOT__(Hello); @@ -750,23 +794,33 @@ describe(`list componentAtIndex`, () => { const s6 = __SNAPSHOT__(!); const c0 = new SnapshotInstance(s3); + c0.__slotIndex = 0; const c1 = new SnapshotInstance(s3); - b.insertBefore(c0); - b.insertBefore(c1); + c1.__slotIndex = 0; + b1.insertBefore(c0); + b1.insertBefore(c1); const c0_d0 = new SnapshotInstance(s4); + c0_d0.__slotIndex = 0; const c0_d1 = new SnapshotInstance(s5); + c0_d1.__slotIndex = 0; const c0_d2 = new SnapshotInstance(s6); + c0_d2.__slotIndex = 0; const c0_d0_ = new SnapshotInstance(s4); + c0_d0_.__slotIndex = 0; c0.insertBefore(c0_d0); c0.insertBefore(c0_d1); c0.insertBefore(c0_d2); c0.insertBefore(c0_d0_); const c1_d0 = new SnapshotInstance(s4); + c1_d0.__slotIndex = 0; const c1_d1 = new SnapshotInstance(s5); + c1_d1.__slotIndex = 0; const c1_d2 = new SnapshotInstance(s6); + c1_d2.__slotIndex = 0; const c1_d0_ = new SnapshotInstance(s4); + c1_d0_.__slotIndex = 0; c1.insertBefore(c1_d0); c1.insertBefore(c1_d2); c1.insertBefore(c1_d1); @@ -786,11 +840,11 @@ describe(`list componentAtIndex`, () => { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_25", + "type": "__snapshot_a94a8_test_29", }, { "position": 1, - "type": "__snapshot_a94a8_test_25", + "type": "__snapshot_a94a8_test_29", }, ], "removeAction": [], @@ -837,11 +891,11 @@ describe(`list componentAtIndex`, () => { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_25", + "type": "__snapshot_a94a8_test_29", }, { "position": 1, - "type": "__snapshot_a94a8_test_25", + "type": "__snapshot_a94a8_test_29", }, ], "removeAction": [], @@ -883,7 +937,9 @@ describe(`list componentAtIndex`, () => { it('should reuse and hydrate - item removed can be reused correctly', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const c0 = new SnapshotInstance(_s3); const c1 = new SnapshotInstance(_s3); @@ -904,12 +960,12 @@ describe(`list componentAtIndex`, () => { c.insertBefore(d3); }); - b.insertBefore(c0); - b.insertBefore(c1); - b.insertBefore(c2); - b.insertBefore(c3); - b.insertBefore(c4); - b.insertBefore(c5); + b1.insertBefore(c0); + b1.insertBefore(c1); + b1.insertBefore(c2); + b1.insertBefore(c3); + b1.insertBefore(c4); + b1.insertBefore(c5); // item-key c0.setAttribute(0, { 'item-key': 'key-0' }); @@ -940,7 +996,7 @@ describe(`list componentAtIndex`, () => { elementTree.triggerEnqueueComponent(listRef, 99999); } - b.removeChild(c3); + b1.removeChild(c3); __pendingListUpdates.flush(); elementTree.triggerEnqueueComponent(listRef, component[3]); @@ -1008,8 +1064,8 @@ describe(`list componentAtIndex`, () => { , { - "elementID": 31, - "listID": 3, + "elementID": 32, + "listID": 4, "operationID": undefined, "triggerLayout": true, }, @@ -1022,27 +1078,37 @@ describe(`list componentAtIndex`, () => { it('should reuse and hydrate - with slot', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const s3 = __SNAPSHOT__( {HOLE}!{HOLE} , ); + const x = {HOLE}; const slot = __SNAPSHOT__({HOLE}); + const slotInner = __SNAPSHOT__(); const c0 = new SnapshotInstance(s3); const c1 = new SnapshotInstance(s3); - b.insertBefore(c0); - b.insertBefore(c1); + b1.insertBefore(c0); + b1.insertBefore(c1); const c0_d0 = new SnapshotInstance(slot); + c0_d0.__slotIndex = 0; + c0_d0.insertBefore(new SnapshotInstance(slotInner)); const c0_d1 = new SnapshotInstance(slot); + c0_d1.__slotIndex = 1; + c0_d1.insertBefore(new SnapshotInstance(slotInner)); c0.insertBefore(c0_d0); c0.insertBefore(c0_d1); const c1_d0 = new SnapshotInstance(slot); + c1_d0.insertBefore(new SnapshotInstance(slotInner)); const c1_d1 = new SnapshotInstance(slot); + c1_d1.insertBefore(new SnapshotInstance(slotInner)); c1.insertBefore(c1_d0); c1.insertBefore(c1_d1); @@ -1066,11 +1132,11 @@ describe(`list componentAtIndex`, () => { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_29", + "type": "__snapshot_a94a8_test_33", }, { "position": 1, - "type": "__snapshot_a94a8_test_29", + "type": "__snapshot_a94a8_test_33", }, ], "removeAction": [], @@ -1080,15 +1146,23 @@ describe(`list componentAtIndex`, () => { } > - + + + + + - + + + + + `); @@ -1097,7 +1171,9 @@ describe(`list componentAtIndex`, () => { it('should handle continuous componentAtIndex on same index', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const s3 = __SNAPSHOT__( @@ -1108,9 +1184,9 @@ describe(`list componentAtIndex`, () => { const d1 = new SnapshotInstance(s3); const d2 = new SnapshotInstance(s3); const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); // initial there is no child, because "update-list-info" is not flush expect(() => { @@ -1119,14 +1195,16 @@ describe(`list componentAtIndex`, () => { // only call componentAtIndex after flush __pendingListUpdates.flush(); - expect(elementTree.triggerComponentAtIndex(listRef, 0)).toMatchInlineSnapshot(`4`); - expect(elementTree.triggerComponentAtIndex(listRef, 0)).toMatchInlineSnapshot(`7`); // should return a new uiSign + expect(elementTree.triggerComponentAtIndex(listRef, 0)).toMatchInlineSnapshot(`5`); + expect(elementTree.triggerComponentAtIndex(listRef, 0)).toMatchInlineSnapshot(`8`); // should return a new uiSign }); it('should handle continuous componentAtIndex on same index - self reuse', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const s3 = __SNAPSHOT__( @@ -1137,9 +1215,9 @@ describe(`list componentAtIndex`, () => { const d1 = new SnapshotInstance(s3); const d2 = new SnapshotInstance(s3); const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); // initial there is no child, because "update-list-info" is not flush expect(() => { @@ -1149,15 +1227,17 @@ describe(`list componentAtIndex`, () => { // only call componentAtIndex after flush __pendingListUpdates.flush(); let uiSign; - expect(uiSign = elementTree.triggerComponentAtIndex(listRef, 0)).toMatchInlineSnapshot(`4`); + expect(uiSign = elementTree.triggerComponentAtIndex(listRef, 0)).toMatchInlineSnapshot(`5`); elementTree.triggerEnqueueComponent(listRef, uiSign); - expect(elementTree.triggerComponentAtIndex(listRef, 0)).toMatchInlineSnapshot(`4`); // should reuse self + expect(elementTree.triggerComponentAtIndex(listRef, 0)).toMatchInlineSnapshot(`5`); // should reuse self }); it('should handle componentAtIndex when `enableReuseNotification` is true', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const s3 = __SNAPSHOT__( @@ -1177,12 +1257,12 @@ describe(`list componentAtIndex`, () => { d3.setAttribute(0, { 'item-key': '3' }); d4.setAttribute(0, { 'item-key': '4' }); d5.setAttribute(0, { 'item-key': '5' }); - b.insertBefore(d0); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); - b.insertBefore(d4); - b.insertBefore(d5); + b1.insertBefore(d0); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); + b1.insertBefore(d4); + b1.insertBefore(d5); __pendingListUpdates.flush(); @@ -1214,27 +1294,27 @@ describe(`list componentAtIndex`, () => { expect(fn.mock.calls).toMatchInlineSnapshot(` [ [ - 4, + 5, undefined, ], [ - 7, + 8, undefined, ], [ - 10, + 11, undefined, ], [ - 13, + 14, undefined, ], [ - 4, + 5, "4", ], [ - 7, + 8, "5", ], ] @@ -1244,7 +1324,9 @@ describe(`list componentAtIndex`, () => { it('should handle componentAtIndex when there is `reuse-identifier`', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const d0 = new SnapshotInstance(s3); const d1 = new SnapshotInstance(s3); @@ -1260,12 +1342,12 @@ describe(`list componentAtIndex`, () => { d4.setAttribute(0, { 'item-key': '4', 'reuse-identifier': 'b' }); d5.setAttribute(0, { 'item-key': '5', 'reuse-identifier': 'b' }); - b.insertBefore(d0); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); - b.insertBefore(d4); - b.insertBefore(d5); + b1.insertBefore(d0); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); + b1.insertBefore(d4); + b1.insertBefore(d5); __pendingListUpdates.flush(); @@ -1307,14 +1389,20 @@ describe('list reload', () => { const s1 = __SNAPSHOT__( 111 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); it('For same-type list-item with different item-key, do an insert + remove so the SDK detects it.', () => { const b = new SnapshotInstance(s1); b.ensureElements(); const root = b.__element_root; + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + b.insertBefore(b1); const s3 = __SNAPSHOT__( @@ -1323,35 +1411,46 @@ describe('list reload', () => { ); const d1 = new SnapshotInstance(s3); // a + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); // b + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); // c + d3.__slotIndex = 0; const d4 = new SnapshotInstance(s3); // d + d4.__slotIndex = 0; d1.setAttribute(0, { 'item-key': 'a' }); d2.setAttribute(0, { 'item-key': 'b' }); d3.setAttribute(0, { 'item-key': 'c' }); d4.setAttribute(0, { 'item-key': 'd' }); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); - b.insertBefore(d4); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); + b1.insertBefore(d4); __pendingListUpdates.flush(); const bb = new SnapshotInstance(s1); { + const bb1 = new SnapshotInstance(s11); + bb1.__slotIndex = 0; + bb.insertBefore(bb1); const d1 = new SnapshotInstance(s3); // a1 + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); // b1 + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); // c1 + d3.__slotIndex = 0; const d4 = new SnapshotInstance(s3); // d1 + d4.__slotIndex = 0; d1.setAttribute(0, { 'item-key': 'a1' }); d2.setAttribute(0, { 'item-key': 'b1' }); d3.setAttribute(0, { 'item-key': 'c1' }); d4.setAttribute(0, { 'item-key': 'd1' }); - bb.insertBefore(d1); - bb.insertBefore(d2); - bb.insertBefore(d3); - bb.insertBefore(d4); + bb1.insertBefore(d1); + bb1.insertBefore(d2); + bb1.insertBefore(d3); + bb1.insertBefore(d4); } hydrate(b, bb); @@ -1364,70 +1463,72 @@ describe('list reload', () => { text="111" /> - + + + `); }); @@ -1437,6 +1538,10 @@ describe('list reload', () => { b.ensureElements(); const root = b.__element_root; + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + b.insertBefore(b1); + const s3 = __SNAPSHOT__( World @@ -1444,20 +1549,29 @@ describe('list reload', () => { ); const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + d3.__slotIndex = 0; + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); __pendingListUpdates.flush(); const bb = new SnapshotInstance(s1); { + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + bb.insertBefore(b1); + const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); - bb.insertBefore(d1); - bb.insertBefore(d2); + d2.__slotIndex = 0; + b1.insertBefore(d1); + b1.insertBefore(d2); } hydrate(b, bb); @@ -1470,38 +1584,40 @@ describe('list reload', () => { text="111" /> - + + + `); @@ -1515,48 +1631,50 @@ describe('list reload', () => { text="111" /> - + + + `); }); @@ -1566,6 +1684,11 @@ describe('list reload', () => { b.ensureElements(); const root = b.__element_root; + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + b.insertBefore(b1); + const listRef = b1.__elements[0]; + const s3 = __SNAPSHOT__( World @@ -1579,35 +1702,46 @@ describe('list reload', () => { ); const d1 = new SnapshotInstance(s3); // a + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); // b + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s4); // c + d3.__slotIndex = 0; const d4 = new SnapshotInstance(s3); // d + d4.__slotIndex = 0; d1.setAttribute(0, { 'item-key': 'a' }); d2.setAttribute(0, { 'item-key': 'b' }); d3.setAttribute(0, { 'item-key': 'c' }); d4.setAttribute(0, { 'item-key': 'd' }); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); - b.insertBefore(d4); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); + b1.insertBefore(d4); __pendingListUpdates.flush(); const bb = new SnapshotInstance(s1); + const bb1 = new SnapshotInstance(s11); + bb1.__slotIndex = 0; + bb.insertBefore(bb1); { const d1 = new SnapshotInstance(s3); // a + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s4); // c + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); // b + d3.__slotIndex = 0; const d4 = new SnapshotInstance(s3); // d + d4.__slotIndex = 0; d1.setAttribute(0, { 'item-key': 'a' }); d2.setAttribute(0, { 'item-key': 'c' }); d3.setAttribute(0, { 'item-key': 'b' }); d4.setAttribute(0, { 'item-key': 'd' }); - bb.insertBefore(d1); - bb.insertBefore(d2); - bb.insertBefore(d3); - bb.insertBefore(d4); + bb1.insertBefore(d1); + bb1.insertBefore(d2); + bb1.insertBefore(d3); + bb1.insertBefore(d4); } hydrate(b, bb); @@ -1620,52 +1754,54 @@ describe('list reload', () => { text="111" /> - + + + `); }); @@ -1673,8 +1809,11 @@ describe('list reload', () => { it('list-item with same type - with one list-item rendered', () => { const b = new SnapshotInstance(s1); b.ensureElements(); + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + b.insertBefore(b1); const root = b.__elements[0]; - const listRef = b.__elements[3]; + const listRef = b1.__elements[0]; const s3 = __SNAPSHOT__( @@ -1683,23 +1822,33 @@ describe('list reload', () => { ); const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + d3.__slotIndex = 0; + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); __pendingListUpdates.flush(); elementTree.triggerComponentAtIndex(listRef, 0); const bb = new SnapshotInstance(s1); { + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + bb.insertBefore(b1); + const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); - bb.insertBefore(d1); - bb.insertBefore(d2); - bb.insertBefore(d3); + d3.__slotIndex = 0; + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); } hydrate(b, bb); @@ -1713,44 +1862,46 @@ describe('list reload', () => { text="111" /> - - - - - - - + + + + + + + + + `); }); @@ -1758,8 +1909,13 @@ describe('list reload', () => { it('list-item with same type - platformInfo change', () => { const b = new SnapshotInstance(s1); b.ensureElements(); + + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + b.insertBefore(b1); + const root = b.__elements[0]; - const listRef = b.__elements[3]; + const listRef = b1.__elements[0]; const s3 = __SNAPSHOT__( @@ -1768,28 +1924,38 @@ describe('list reload', () => { ); const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); + d3.__slotIndex = 0; d1.setAttribute(0, { 'item-key': '1', 'full-span': true }); d2.setAttribute(0, { 'item-key': '2', 'full-span': true }); d3.setAttribute(0, { 'item-key': '3', 'full-span': true }); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); __pendingListUpdates.flush(); const bb = new SnapshotInstance(s1); { + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + bb.insertBefore(b1); + const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); + d3.__slotIndex = 0; d1.setAttribute(0, { 'item-key': '1', 'full-span': true }); d2.setAttribute(0, { 'item-key': '2', 'full-span': false }); d3.setAttribute(0, { 'item-key': '3', 'full-span': true }); - bb.insertBefore(d1); - bb.insertBefore(d2); - bb.insertBefore(d3); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); } hydrate(b, bb); @@ -1802,51 +1968,53 @@ describe('list reload', () => { text="111" /> - + + + `); }); @@ -1854,8 +2022,11 @@ describe('list reload', () => { it('list-item with different type', () => { const b = new SnapshotInstance(s1); b.ensureElements(); + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + b.insertBefore(b1); const root = b.__elements[0]; - const listRef = b.__elements[3]; + const listRef = b1.__elements[0]; const s3 = __SNAPSHOT__( @@ -1870,13 +2041,17 @@ describe('list reload', () => { ); const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); + d3.__slotIndex = 0; const d4 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); - b.insertBefore(d4); + d4.__slotIndex = 0; + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); + b1.insertBefore(d4); __pendingListUpdates.flush(); const uiSign0 = elementTree.triggerComponentAtIndex(listRef, 0); @@ -1890,99 +2065,108 @@ describe('list reload', () => { const recycleSignMap = recycleMap.get(s3); expect(signMap.get(__GetElementUniqueID(d1.__element_root))).toBe(d1); - expect(signMap.get(__GetElementUniqueID(d2.__element_root))).toBe(d2); - expect(signMap.get(__GetElementUniqueID(d3.__element_root))).toBe(d3); - expect(recycleSignMap.get(__GetElementUniqueID(d3.__element_root))).toBe(d3); - - const bb = new SnapshotInstance(s1); - const d1_ = new SnapshotInstance(s3_alt); - const d2_ = new SnapshotInstance(s3); - const d3_ = new SnapshotInstance(s3); - const d4_ = new SnapshotInstance(s3); - bb.insertBefore(d1_); - bb.insertBefore(d2_); - bb.insertBefore(d3_); - bb.insertBefore(d4_); - - hydrate(b, bb); - b.unRenderElements(); - - // Should only update `list-item` in the recycling pool - // Should not add the on-screen `list-item` to the recycling pool, - expect([...recycleSignMap.keys()]).toStrictEqual([__GetElementUniqueID(d3.__element_root)]); - expect(recycleSignMap.get(__GetElementUniqueID(d3.__element_root))).not.toBe(d3); - - // The one rendered should be removed - expect(root).toMatchInlineSnapshot(` - - - - - - - - - - - - - - - - - - - - - + expect(signMap.get(__GetElementUniqueID(d2.__element_root))).toBe(d2); + expect(signMap.get(__GetElementUniqueID(d3.__element_root))).toBe(d3); + expect(recycleSignMap.get(__GetElementUniqueID(d3.__element_root))).toBe(d3); + + const bb = new SnapshotInstance(s1); + const bb1 = new SnapshotInstance(s11); + bb1.__slotIndex = 0; + bb.insertBefore(bb1); + const d1_ = new SnapshotInstance(s3_alt); + d1_.__slotIndex = 0; + const d2_ = new SnapshotInstance(s3); + d2_.__slotIndex = 0; + const d3_ = new SnapshotInstance(s3); + d3_.__slotIndex = 0; + const d4_ = new SnapshotInstance(s3); + d4_.__slotIndex = 0; + bb1.insertBefore(d1_); + bb1.insertBefore(d2_); + bb1.insertBefore(d3_); + bb1.insertBefore(d4_); + + hydrate(b, bb); + b.unRenderElements(); + + // Should only update `list-item` in the recycling pool + // Should not add the on-screen `list-item` to the recycling pool, + expect([...recycleSignMap.keys()]).toStrictEqual([__GetElementUniqueID(d3.__element_root)]); + expect(recycleSignMap.get(__GetElementUniqueID(d3.__element_root))).not.toBe(d3); + + // The one rendered should be removed + expect(root).toMatchInlineSnapshot(` + + + + + + + + + + + + + + + + + + + + + + + `); @@ -2001,76 +2185,78 @@ describe('list reload', () => { text="111" /> - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + `); @@ -2136,9 +2322,12 @@ describe('list bug', () => { const s1 = __SNAPSHOT__( 111 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const s2 = __SNAPSHOT__(World); const s3 = __SNAPSHOT__( @@ -2150,6 +2339,10 @@ describe('list bug', () => { a.ensureElements(); const b = new SnapshotInstance(s1); + const b1 = new SnapshotInstance(s11); + + b.insertBefore(b1); + const c = new SnapshotInstance(s2); a.insertBefore(b); @@ -2158,28 +2351,28 @@ describe('list bug', () => { const d1 = new SnapshotInstance(s3); const d2 = new SnapshotInstance(s3); const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); - expect(b.childNodes.length).toMatchInlineSnapshot(`3`); + expect(b1.childNodes.length).toMatchInlineSnapshot(`3`); expect(__pendingListUpdates.values).toMatchInlineSnapshot(` { - "-5": [ + "-7": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, { "position": 1, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, { "position": 2, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, ], "removeAction": [], @@ -2188,19 +2381,19 @@ describe('list bug', () => { "flush": false, "from": 0, "to": 0, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, { "flush": false, "from": 1, "to": 1, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, { "flush": false, "from": 2, "to": 2, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, ], }, @@ -2210,15 +2403,15 @@ describe('list bug', () => { { __pendingListUpdates.clearAttachedLists(); - b.insertBefore(d3); // move + b1.insertBefore(d3); // move expect(__pendingListUpdates.values).toMatchInlineSnapshot(` { - "-5": [ + "-7": [ { "insertAction": [ { "position": 2, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, ], "removeAction": [ @@ -2229,19 +2422,19 @@ describe('list bug', () => { "flush": false, "from": 0, "to": 0, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, { "flush": false, "from": 1, "to": 1, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, { "flush": false, "from": 2, "to": 2, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, ], }, @@ -2252,10 +2445,10 @@ describe('list bug', () => { { __pendingListUpdates.clearAttachedLists(); - b.removeChild(d3); // move + b1.removeChild(d3); // move expect(__pendingListUpdates.values).toMatchInlineSnapshot(` { - "-5": [ + "-7": [ { "insertAction": [], "removeAction": [ @@ -2266,13 +2459,13 @@ describe('list bug', () => { "flush": false, "from": 0, "to": 0, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, { "flush": false, "from": 1, "to": 1, - "type": "__snapshot_a94a8_test_47", + "type": "__snapshot_a94a8_test_55", }, ], }, @@ -2289,15 +2482,23 @@ describe('list-item JSXSpread', () => { const s1 = __SNAPSHOT__( 111 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); it('list-item with same type - platformInfo change', () => { const b = new SnapshotInstance(s1); b.ensureElements(); + + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + b.insertBefore(b1); + const root = b.__elements[0]; - const listRef = b.__elements[3]; + const listRef = b1.__elements[0]; const s3 = __SNAPSHOT__( @@ -2307,11 +2508,14 @@ describe('list-item JSXSpread', () => { { const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + d3.__slotIndex = 0; + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); d1.setAttribute(0, { 'item-key': '1', 'full-span': true, 'recyclable': true }); d2.setAttribute(0, { 'item-key': '2', 'full-span': true, 'recyclable': true }); @@ -2322,12 +2526,19 @@ describe('list-item JSXSpread', () => { const bb = new SnapshotInstance(s1); + const bb1 = new SnapshotInstance(s11); + bb1.__slotIndex = 0; + bb.insertBefore(bb1); + const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); - bb.insertBefore(d1); - bb.insertBefore(d2); - bb.insertBefore(d3); + d3.__slotIndex = 0; + bb1.insertBefore(d1); + bb1.insertBefore(d2); + bb1.insertBefore(d3); d1.setAttribute(0, { 'item-key': '1', 'full-span': true, 'recyclable': false }); d2.setAttribute(0, { 'item-key': '2', 'full-span': false, 'recyclable': false }); @@ -2343,73 +2554,75 @@ describe('list-item JSXSpread', () => { text="111" /> - + + + `); @@ -2422,85 +2635,87 @@ describe('list-item JSXSpread', () => { text="111" /> - - + - - - - - + + + + + + + `); @@ -2512,85 +2727,87 @@ describe('list-item JSXSpread', () => { text="111" /> - - + - - - - - + + + + + + + `); }); @@ -2600,9 +2817,12 @@ describe('list-item with platform info attributes', () => { const s1 = __SNAPSHOT__( 111 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const s3 = __SNAPSHOT__( { it('basic list-item with platform info attributes', () => { const b = new SnapshotInstance(s1); b.ensureElements(); + + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + const root = b.__elements[0]; - const listRef = b.__elements[3]; + const listRef = b1.__elements[0]; { const d0 = new SnapshotInstance(s3); const d1 = new SnapshotInstance(s3); const d2 = new SnapshotInstance(s3); - b.insertBefore(d0); - b.insertBefore(d1); - b.insertBefore(d2); + b1.insertBefore(d0); + b1.insertBefore(d1); + b1.insertBefore(d2); d0.setAttribute(0, { 'item-key': 'list-item-0', @@ -2678,58 +2902,60 @@ describe('list-item with platform info attributes', () => { text="111" /> - + + + `); @@ -2758,7 +2984,7 @@ describe('list-item with platform info attributes', () => { "reuse-identifier": "A", "sticky-bottom": false, "sticky-top": true, - "type": "__snapshot_a94a8_test_51", + "type": "__snapshot_a94a8_test_61", }, { "estimated-height": 100, @@ -2771,7 +2997,7 @@ describe('list-item with platform info attributes', () => { "reuse-identifier": "A", "sticky-bottom": false, "sticky-top": false, - "type": "__snapshot_a94a8_test_51", + "type": "__snapshot_a94a8_test_61", }, { "estimated-height": 100, @@ -2784,7 +3010,7 @@ describe('list-item with platform info attributes', () => { "reuse-identifier": "A", "sticky-bottom": true, "sticky-top": false, - "type": "__snapshot_a94a8_test_51", + "type": "__snapshot_a94a8_test_61", }, ], "removeAction": [], @@ -2847,9 +3073,12 @@ describe('list componentAtIndexes', () => { const s0 = __SNAPSHOT__( 111 - {HOLE} + {HOLE} , ); + const s01 = __SNAPSHOT__( + {HOLE}, + ); const s1 = __SNAPSHOT__( @@ -2860,7 +3089,9 @@ describe('list componentAtIndexes', () => { it('basic componentAtIndexes with async flush', () => { const b = new SnapshotInstance(s0); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s01); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const d0 = new SnapshotInstance(s1); const d1 = new SnapshotInstance(s1); @@ -2868,9 +3099,9 @@ describe('list componentAtIndexes', () => { d0.setAttribute(0, { 'item-key': 'list-item-0' }); d1.setAttribute(0, { 'item-key': 'list-item-1' }); d2.setAttribute(0, { 'item-key': 'list-item-2' }); - b.insertBefore(d0); - b.insertBefore(d1); - b.insertBefore(d2); + b1.insertBefore(d0); + b1.insertBefore(d1); + b1.insertBefore(d2); __pendingListUpdates.flush(); const fn = vi.fn(); @@ -2908,11 +3139,11 @@ describe('list componentAtIndexes', () => { [ { "elementIDs": [ - 4, - 7, - 10, + 5, + 8, + 11, ], - "listID": 3, + "listID": 4, "operationIDs": [ 0, 1, @@ -2938,7 +3169,9 @@ describe('list componentAtIndexes', () => { it('basic componentAtIndexes with no async flush', () => { const b = new SnapshotInstance(s0); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s01); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const d0 = new SnapshotInstance(s1); const d1 = new SnapshotInstance(s1); @@ -2946,9 +3179,9 @@ describe('list componentAtIndexes', () => { d0.setAttribute(0, { 'item-key': 'list-item-0' }); d1.setAttribute(0, { 'item-key': 'list-item-1' }); d2.setAttribute(0, { 'item-key': 'list-item-2' }); - b.insertBefore(d0); - b.insertBefore(d1); - b.insertBefore(d2); + b1.insertBefore(d0); + b1.insertBefore(d1); + b1.insertBefore(d2); __pendingListUpdates.flush(); const fn = vi.fn(); @@ -2970,11 +3203,11 @@ describe('list componentAtIndexes', () => { [ { "elementIDs": [ - 4, - 7, - 10, + 5, + 8, + 11, ], - "listID": 3, + "listID": 4, "operationIDs": [ 11, 22, @@ -2990,7 +3223,9 @@ describe('list componentAtIndexes', () => { it('basic componentAtIndexes with async flush and `enableReuseNotification` is true', () => { const b = new SnapshotInstance(s0); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s01); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const d0 = new SnapshotInstance(s1); const d1 = new SnapshotInstance(s1); const d2 = new SnapshotInstance(s1); @@ -3003,12 +3238,12 @@ describe('list componentAtIndexes', () => { d3.setAttribute(0, { 'item-key': 'list-item-3' }); d4.setAttribute(0, { 'item-key': 'list-item-4' }); d5.setAttribute(0, { 'item-key': 'list-item-5' }); - b.insertBefore(d0); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); - b.insertBefore(d4); - b.insertBefore(d5); + b1.insertBefore(d0); + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); + b1.insertBefore(d4); + b1.insertBefore(d5); __pendingListUpdates.flush(); { @@ -3072,11 +3307,11 @@ describe('list componentAtIndexes', () => { [ { "elementIDs": [ - 4, - 7, - 10, + 5, + 8, + 11, ], - "listID": 3, + "listID": 4, "operationIDs": [ 3, 4, @@ -3092,16 +3327,18 @@ describe('list componentAtIndexes', () => { it('should handle continuous componentAtIndexes on same index - self reuse', () => { const b = new SnapshotInstance(s0); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s01); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const d0 = new SnapshotInstance(s1); const d1 = new SnapshotInstance(s1); const d2 = new SnapshotInstance(s1); d0.setAttribute(0, { 'item-key': 'list-item-0' }); d1.setAttribute(0, { 'item-key': 'list-item-1' }); d2.setAttribute(0, { 'item-key': 'list-item-2' }); - b.insertBefore(d0); - b.insertBefore(d1); - b.insertBefore(d2); + b1.insertBefore(d0); + b1.insertBefore(d1); + b1.insertBefore(d2); __pendingListUpdates.flush(); { @@ -3149,11 +3386,11 @@ describe('list componentAtIndexes', () => { [ { "elementIDs": [ - 4, - 7, - 10, + 5, + 8, + 11, ], - "listID": 3, + "listID": 4, "operationIDs": [ 0, 1, @@ -3169,16 +3406,18 @@ describe('list componentAtIndexes', () => { it('should update signMap before __FlushElementTree', () => { const b = new SnapshotInstance(s0); b.ensureElements(); - const listRef = b.__elements[3]; + const b1 = new SnapshotInstance(s01); + b.insertBefore(b1); + const listRef = b1.__elements[0]; const d0 = new SnapshotInstance(s1); const d1 = new SnapshotInstance(s1); const d2 = new SnapshotInstance(s1); d0.setAttribute(0, { 'item-key': 'list-item-0' }); d1.setAttribute(0, { 'item-key': 'list-item-1' }); d2.setAttribute(0, { 'item-key': 'list-item-2' }); - b.insertBefore(d0); - b.insertBefore(d1); - b.insertBefore(d2); + b1.insertBefore(d0); + b1.insertBefore(d1); + b1.insertBefore(d2); __pendingListUpdates.flush(); const listID = __GetElementUniqueID(listRef); @@ -3259,7 +3498,7 @@ describe('list-item with "defer" attribute', () => { { "item-key": "1", "position": 0, - "type": "__snapshot_a94a8_test_55", + "type": "__snapshot_a94a8_test_66", }, ], "removeAction": [], @@ -3296,7 +3535,7 @@ describe('list-item with "defer" attribute', () => { { "item-key": "1", "position": 0, - "type": "__snapshot_a94a8_test_55", + "type": "__snapshot_a94a8_test_66", }, ], "removeAction": [], @@ -3389,17 +3628,17 @@ describe('list-item with "defer" attribute', () => { { "item-key": "0", "position": 0, - "type": "__snapshot_a94a8_test_60", + "type": "__snapshot_a94a8_test_71", }, { "item-key": "1", "position": 1, - "type": "__snapshot_a94a8_test_61", + "type": "__snapshot_a94a8_test_72", }, { "item-key": "2", "position": 2, - "type": "__snapshot_a94a8_test_62", + "type": "__snapshot_a94a8_test_73", }, ], "removeAction": [], @@ -3472,17 +3711,17 @@ describe('list-item with "defer" attribute', () => { { "item-key": "0", "position": 0, - "type": "__snapshot_a94a8_test_60", + "type": "__snapshot_a94a8_test_71", }, { "item-key": "1", "position": 1, - "type": "__snapshot_a94a8_test_61", + "type": "__snapshot_a94a8_test_72", }, { "item-key": "2", "position": 2, - "type": "__snapshot_a94a8_test_62", + "type": "__snapshot_a94a8_test_73", }, ], "removeAction": [], @@ -3553,17 +3792,17 @@ describe('list-item with "defer" attribute', () => { { "item-key": "0", "position": 0, - "type": "__snapshot_a94a8_test_65", + "type": "__snapshot_a94a8_test_76", }, { "item-key": "1", "position": 1, - "type": "__snapshot_a94a8_test_65", + "type": "__snapshot_a94a8_test_76", }, { "item-key": "2", "position": 2, - "type": "__snapshot_a94a8_test_65", + "type": "__snapshot_a94a8_test_76", }, ], "removeAction": [], @@ -3600,17 +3839,17 @@ describe('list-item with "defer" attribute', () => { { "item-key": "0", "position": 0, - "type": "__snapshot_a94a8_test_65", + "type": "__snapshot_a94a8_test_76", }, { "item-key": "1", "position": 1, - "type": "__snapshot_a94a8_test_65", + "type": "__snapshot_a94a8_test_76", }, { "item-key": "2", "position": 2, - "type": "__snapshot_a94a8_test_65", + "type": "__snapshot_a94a8_test_76", }, ], "removeAction": [], @@ -3659,17 +3898,17 @@ describe('list-item with "defer" attribute', () => { { "item-key": "0", "position": 0, - "type": "__snapshot_a94a8_test_65", + "type": "__snapshot_a94a8_test_76", }, { "item-key": "1", "position": 1, - "type": "__snapshot_a94a8_test_65", + "type": "__snapshot_a94a8_test_76", }, { "item-key": "2", "position": 2, - "type": "__snapshot_a94a8_test_65", + "type": "__snapshot_a94a8_test_76", }, ], "removeAction": [], @@ -3756,15 +3995,21 @@ describe('nested list', () => { const s1 = __SNAPSHOT__( s1 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const s2 = __SNAPSHOT__( s2 - {HOLE} + {HOLE} , ); + const s21 = __SNAPSHOT__( + {HOLE}, + ); const s3 = __SNAPSHOT__( s3 @@ -3775,65 +4020,67 @@ describe('nested list', () => { const b = new SnapshotInstance(s1); a.insertBefore(b); + + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); + b.ensureElements(); - const parentListRef = b.__elements[3]; + const parentListRef = b1.__elements[0]; const c1 = new SnapshotInstance(s2); + const c11 = new SnapshotInstance(s21); const c2 = new SnapshotInstance(s2); + const c21 = new SnapshotInstance(s21); const c3 = new SnapshotInstance(s2); - b.insertBefore(c1); - b.insertBefore(c2); - b.insertBefore(c3); + const c31 = new SnapshotInstance(s21); + c1.insertBefore(c11); + c2.insertBefore(c21); + c3.insertBefore(c31); + b1.insertBefore(c1); + b1.insertBefore(c2); + b1.insertBefore(c3); const d1 = new SnapshotInstance(s3); - c1.insertBefore(d1); + c11.insertBefore(d1); const d2 = new SnapshotInstance(s3); - c2.insertBefore(d2); + c21.insertBefore(d2); const d3 = new SnapshotInstance(s3); - c3.insertBefore(d3); + c31.insertBefore(d3); expect(__pendingListUpdates.values).toMatchInlineSnapshot(` { - "-5": [ + "-10": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_70", - }, - { - "position": 1, - "type": "__snapshot_a94a8_test_70", - }, - { - "position": 2, - "type": "__snapshot_a94a8_test_70", + "type": "__snapshot_a94a8_test_84", }, ], "removeAction": [], "updateAction": [], }, ], - "-6": [ + "-12": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_71", + "type": "__snapshot_a94a8_test_84", }, ], "removeAction": [], "updateAction": [], }, ], - "-7": [ + "-14": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_71", + "type": "__snapshot_a94a8_test_84", }, ], "removeAction": [], @@ -3845,7 +4092,15 @@ describe('nested list', () => { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_71", + "type": "__snapshot_a94a8_test_82", + }, + { + "position": 1, + "type": "__snapshot_a94a8_test_82", + }, + { + "position": 2, + "type": "__snapshot_a94a8_test_82", }, ], "removeAction": [], @@ -3860,36 +4115,36 @@ describe('nested list', () => { // children list should not be cleared expect(__pendingListUpdates.values).toMatchInlineSnapshot(` { - "-6": [ + "-10": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_71", + "type": "__snapshot_a94a8_test_84", }, ], "removeAction": [], "updateAction": [], }, ], - "-7": [ + "-12": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_71", + "type": "__snapshot_a94a8_test_84", }, ], "removeAction": [], "updateAction": [], }, ], - "-8": [ + "-14": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_71", + "type": "__snapshot_a94a8_test_84", }, ], "removeAction": [], @@ -3899,20 +4154,20 @@ describe('nested list', () => { } `); - expect(elementTree.triggerComponentAtIndex(parentListRef, 0)).toMatchInlineSnapshot(`4`); - expect(elementTree.triggerComponentAtIndex(parentListRef, 1)).toMatchInlineSnapshot(`8`); - expect(elementTree.triggerComponentAtIndex(parentListRef, 2)).toMatchInlineSnapshot(`12`); + expect(elementTree.triggerComponentAtIndex(parentListRef, 0)).toMatchInlineSnapshot(`5`); + expect(elementTree.triggerComponentAtIndex(parentListRef, 1)).toMatchInlineSnapshot(`10`); + expect(elementTree.triggerComponentAtIndex(parentListRef, 2)).toMatchInlineSnapshot(`15`); __pendingListUpdates.flush(); // all lists should be cleared expect(__pendingListUpdates.values).toMatchInlineSnapshot(`{}`); - const childListRef1 = c1.__elements[3]; - const childListRef2 = c2.__elements[3]; - const childListRef3 = c3.__elements[3]; - expect(elementTree.triggerComponentAtIndex(childListRef1, 0)).toMatchInlineSnapshot(`16`); - expect(elementTree.triggerComponentAtIndex(childListRef2, 0)).toMatchInlineSnapshot(`19`); - expect(elementTree.triggerComponentAtIndex(childListRef3, 0)).toMatchInlineSnapshot(`22`); + const childListRef1 = c11.__elements[0]; + const childListRef2 = c21.__elements[0]; + const childListRef3 = c31.__elements[0]; + expect(elementTree.triggerComponentAtIndex(childListRef1, 0)).toMatchInlineSnapshot(`20`); + expect(elementTree.triggerComponentAtIndex(childListRef2, 0)).toMatchInlineSnapshot(`23`); + expect(elementTree.triggerComponentAtIndex(childListRef3, 0)).toMatchInlineSnapshot(`26`); expect(elementTree).toMatchInlineSnapshot(` " @@ -3921,124 +4176,132 @@ describe('nested list', () => { text="s1" /> - - - - - - + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - + ] + } + > + + + + + + + + + + " `); }); @@ -4047,15 +4310,21 @@ describe('nested list', () => { const s1 = __SNAPSHOT__( s1 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const s2 = __SNAPSHOT__( s2 - {HOLE} + {HOLE} , ); + const s21 = __SNAPSHOT__( + {HOLE}, + ); const s3 = __SNAPSHOT__( s3 @@ -4063,62 +4332,68 @@ describe('nested list', () => { ); const a = new SnapshotInstance(s); - const b = new SnapshotInstance(s1); a.insertBefore(b); + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); b.ensureElements(); - const parentListRef = b.__elements[3]; + const parentListRef = b1.__elements[0]; const c1 = new SnapshotInstance(s2); + const c11 = new SnapshotInstance(s21); + c1.insertBefore(c11); const c2 = new SnapshotInstance(s2); + const c21 = new SnapshotInstance(s21); + c2.insertBefore(c21); const c3 = new SnapshotInstance(s2); - - b.insertBefore(c1); - b.insertBefore(c2); - b.insertBefore(c3); + const c31 = new SnapshotInstance(s21); + c3.insertBefore(c31); + b1.insertBefore(c1); + b1.insertBefore(c2); + b1.insertBefore(c3); const d1 = new SnapshotInstance(s3); - c1.insertBefore(d1); + c11.insertBefore(d1); const d2 = new SnapshotInstance(s3); - c2.insertBefore(d2); + c21.insertBefore(d2); const d3 = new SnapshotInstance(s3); - c3.insertBefore(d3); + c31.insertBefore(d3); __pendingListUpdates.flush(); expect(__pendingListUpdates.values).toMatchInlineSnapshot(` { - "-6": [ + "-10": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_74", + "type": "__snapshot_a94a8_test_89", }, ], "removeAction": [], "updateAction": [], }, ], - "-7": [ + "-12": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_74", + "type": "__snapshot_a94a8_test_89", }, ], "removeAction": [], "updateAction": [], }, ], - "-8": [ + "-14": [ { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_74", + "type": "__snapshot_a94a8_test_89", }, ], "removeAction": [], @@ -4145,15 +4420,15 @@ describe('nested list', () => { "insertAction": [ { "position": 0, - "type": "__snapshot_a94a8_test_73", + "type": "__snapshot_a94a8_test_87", }, { "position": 1, - "type": "__snapshot_a94a8_test_73", + "type": "__snapshot_a94a8_test_87", }, { "position": 2, - "type": "__snapshot_a94a8_test_73", + "type": "__snapshot_a94a8_test_87", }, ], "removeAction": [], @@ -4168,22 +4443,24 @@ describe('nested list', () => { text="s2" /> - + + + @@ -4191,22 +4468,24 @@ describe('nested list', () => { text="s2" /> - + + + @@ -4214,22 +4493,24 @@ describe('nested list', () => { text="s2" /> - + + + @@ -4237,24 +4518,26 @@ describe('nested list', () => { text="s2" /> - + + + `); @@ -4264,15 +4547,21 @@ describe('nested list', () => { const s1 = __SNAPSHOT__( s1 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const s2 = __SNAPSHOT__( s2 - {HOLE} + {HOLE} , ); + const s21 = __SNAPSHOT__( + {HOLE}, + ); const s3 = __SNAPSHOT__( s3 @@ -4284,22 +4573,30 @@ describe('nested list', () => { const b = new SnapshotInstance(s1); a.insertBefore(b); b.ensureElements(); + const b1 = new SnapshotInstance(s11); + b.insertBefore(b1); const c1 = new SnapshotInstance(s2); + const c11 = new SnapshotInstance(s21); + c1.insertBefore(c11); const c2 = new SnapshotInstance(s2); + const c21 = new SnapshotInstance(s21); + c2.insertBefore(c21); const c3 = new SnapshotInstance(s2); - b.insertBefore(c1); - b.insertBefore(c2); - b.insertBefore(c3); + const c31 = new SnapshotInstance(s21); + c3.insertBefore(c31); + b1.insertBefore(c1); + b1.insertBefore(c2); + b1.insertBefore(c3); const d1 = new SnapshotInstance(s3); - c1.insertBefore(d1); + c11.insertBefore(d1); const d2 = new SnapshotInstance(s3); - c2.insertBefore(d2); + c21.insertBefore(d2); const d3 = new SnapshotInstance(s3); - c3.insertBefore(d3); + c31.insertBefore(d3); expect(Object.keys(__pendingListUpdates.values).length).toBe(4); @@ -4319,14 +4616,19 @@ describe('update-list-info profile', () => { const s1 = __SNAPSHOT__( 111 - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); it('flush & hydrate', () => { const b = new SnapshotInstance(s1); b.ensureElements(); - const root = b.__element_root; + const b1 = new SnapshotInstance(s11); + b1.__slotIndex = 0; + b.insertBefore(b1); const s3 = __SNAPSHOT__( @@ -4335,20 +4637,29 @@ describe('update-list-info profile', () => { ); const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); + d2.__slotIndex = 0; const d3 = new SnapshotInstance(s3); - b.insertBefore(d1); - b.insertBefore(d2); - b.insertBefore(d3); + d3.__slotIndex = 0; + + b1.insertBefore(d1); + b1.insertBefore(d2); + b1.insertBefore(d3); __pendingListUpdates.flush(); const bb = new SnapshotInstance(s1); + const bb1 = new SnapshotInstance(s11); + bb1.__slotIndex = 0; + bb.insertBefore(bb1); { const d1 = new SnapshotInstance(s3); + d1.__slotIndex = 0; const d2 = new SnapshotInstance(s3); - bb.insertBefore(d1); - bb.insertBefore(d2); + d2.__slotIndex = 0; + bb1.insertBefore(d1); + bb1.insertBefore(d2); } hydrate(b, bb); @@ -4360,8 +4671,8 @@ describe('update-list-info profile', () => { "ReactLynx::listFlush::updateListInfo", { "args": { - "list id": "3", - "update list info": "{"insertAction":[{"position":0,"type":"__snapshot_a94a8_test_79"},{"position":1,"type":"__snapshot_a94a8_test_79"},{"position":2,"type":"__snapshot_a94a8_test_79"}],"removeAction":[],"updateAction":[]}", + "list id": "4", + "update list info": "{"insertAction":[{"position":0,"type":"__snapshot_a94a8_test_97"},{"position":1,"type":"__snapshot_a94a8_test_97"},{"position":2,"type":"__snapshot_a94a8_test_97"}],"removeAction":[],"updateAction":[]}", }, }, ], @@ -4369,7 +4680,7 @@ describe('update-list-info profile', () => { "ReactLynx::listHydrate::updateListInfo", { "args": { - "list id": "3", + "list id": "4", "update list info": "{"insertAction":[],"removeAction":[2],"updateAction":[]}", }, }, @@ -4384,19 +4695,25 @@ describe('clear __UpdateListCallbacks', () => { const s1 = __SNAPSHOT__( test - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const a = new SnapshotInstance(s1); a.ensureElements(); + const a1 = new SnapshotInstance(s11); + a1.__slotIndex = 0; + a.insertBefore(a1); expect(lynx.getNative().addEventListener).toHaveBeenCalledWith( '__DestroyLifetime', expect.any(Function), ); - const listElement = a.__elements[3]; + const listElement = a1.__elements[0]; expect(listElement.componentAtIndex).not.toBeNull(); expect(listElement.enqueueComponent).not.toBeNull(); expect(listElement.componentAtIndexes).not.toBeNull(); @@ -4417,22 +4734,28 @@ describe('clear __UpdateListCallbacks', () => { const s1 = __SNAPSHOT__( test - {HOLE} + {HOLE} , ); + const s11 = __SNAPSHOT__( + {HOLE}, + ); const root = new SnapshotInstance(s0); root.ensureElements(); const a = new SnapshotInstance(s1); root.insertBefore(a); + const a1 = new SnapshotInstance(s11); + a1.__slotIndex = 0; + a.insertBefore(a1); expect(lynx.getNative().addEventListener).toHaveBeenCalledWith( '__DestroyLifetime', expect.any(Function), ); - const listElement = a.__elements[3]; + const listElement = a1.__elements[0]; expect(listElement.componentAtIndex).not.toBeNull(); expect(listElement.enqueueComponent).not.toBeNull(); expect(listElement.componentAtIndexes).not.toBeNull(); diff --git a/packages/react/runtime/__test__/lynx/suspense.test.jsx b/packages/react/runtime/__test__/lynx/suspense.test.jsx index 0ec90aff46..2f777ceef5 100644 --- a/packages/react/runtime/__test__/lynx/suspense.test.jsx +++ b/packages/react/runtime/__test__/lynx/suspense.test.jsx @@ -277,12 +277,14 @@ describe('suspense', () => { "childId": 3, "op": "InsertBefore", "parentId": 2, + "slotIndex": 0, }, { "beforeId": null, "childId": 2, "op": "InsertBefore", "parentId": -1, + "slotIndex": 0, }, { "childId": -3, @@ -299,12 +301,14 @@ describe('suspense', () => { "childId": 7, "op": "InsertBefore", "parentId": 3, + "slotIndex": 0, }, { "beforeId": null, "childId": 2, "op": "InsertBefore", "parentId": -1, + "slotIndex": 0, }, ] `); diff --git a/packages/react/runtime/__test__/lynx/timing.test.jsx b/packages/react/runtime/__test__/lynx/timing.test.jsx index 0dcd9c5151..599a23b817 100644 --- a/packages/react/runtime/__test__/lynx/timing.test.jsx +++ b/packages/react/runtime/__test__/lynx/timing.test.jsx @@ -98,7 +98,7 @@ describe('setState timing api', () => { expect(mtCallbacks[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":3,"snapshotPatch":[0,"__snapshot_a94a8_test_2",3,0,null,4,3,4,0,1,1,3,4,null,1,-2,3,null]}],"flushOptions":{"__lynx_timing_flag":"__lynx_timing_actual_fmp"}}", + "data": "{"patchList":[{"id":3,"snapshotPatch":[0,"__snapshot_a94a8_test_2",3,0,null,4,3,4,0,1,1,3,4,null,0,1,-2,3,null,0]}],"flushOptions":{"__lynx_timing_flag":"__lynx_timing_actual_fmp"}}", "patchOptions": { "flowIds": [ 666, @@ -180,7 +180,7 @@ describe('attribute timing api', () => { await waitSchedule(); expect(mtCallbacks[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":6,"snapshotPatch":[0,"__snapshot_a94a8_test_4",3,4,3,[{"__ltf":"__lynx_timing_actual_fmp"}],0,null,4,3,4,0,1,1,3,4,null,1,-2,3,null]}]}", + "data": "{"patchList":[{"id":6,"snapshotPatch":[0,"__snapshot_a94a8_test_4",3,4,3,[{"__ltf":"__lynx_timing_actual_fmp"}],0,null,4,3,4,0,1,1,3,4,null,0,1,-2,3,null,0]}]}", "patchOptions": { "flowIds": [ 666, @@ -372,7 +372,7 @@ describe('attribute timing api', () => { `); expect(mtCallbacks[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":9,"snapshotPatch":[0,"__snapshot_a94a8_test_6",3,4,3,[{"__ltf":"__lynx_timing_actual_fmp"}],0,null,4,3,4,0,1,1,3,4,null,1,-2,3,null]}]}", + "data": "{"patchList":[{"id":9,"snapshotPatch":[0,"__snapshot_a94a8_test_6",3,4,3,[{"__ltf":"__lynx_timing_actual_fmp"}],0,null,4,3,4,0,1,1,3,4,null,0,1,-2,3,null,0]}]}", "patchOptions": { "flowIds": [ 666, @@ -716,7 +716,7 @@ describe('attribute timing api', () => { await waitSchedule(); expect(mtCallbacks[0][1]).toMatchInlineSnapshot(` { - "data": "{"patchList":[{"id":17,"snapshotPatch":[0,"__snapshot_a94a8_test_15",3,4,3,[{"xxx":333,"__lynx_timing_flag":"__lynx_timing_actual_fmp"}],0,null,4,3,4,0,1,1,3,4,null,1,-2,3,null]}]}", + "data": "{"patchList":[{"id":17,"snapshotPatch":[0,"__snapshot_a94a8_test_12",3,4,3,[{"xxx":333,"__lynx_timing_flag":"__lynx_timing_actual_fmp"}],0,null,4,3,4,0,1,1,3,4,null,0,1,-2,3,null,0]}]}", "patchOptions": { "flowIds": [ 666, diff --git a/packages/react/runtime/__test__/preact.test.jsx b/packages/react/runtime/__test__/preact.test.jsx index f8641f9e83..4ffe5094a1 100644 --- a/packages/react/runtime/__test__/preact.test.jsx +++ b/packages/react/runtime/__test__/preact.test.jsx @@ -165,12 +165,8 @@ describe('document', () => { expect(scratch).toMatchInlineSnapshot(` <__snapshot_a94a8_test_2> - <__snapshot_a94a8_test_3> - "Hello" - - <__snapshot_a94a8_test_4> - "World" - + "Hello" + "World" `); @@ -180,8 +176,6 @@ describe('document', () => { -6, -7, -8, - -9, - -10, ] `); }); @@ -348,11 +342,11 @@ describe('document - background', () => { ); expect(scratch).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_14 + <__snapshot_a94a8_test_10 0="world" > "Hello" - + `); @@ -374,14 +368,10 @@ describe('document - background', () => { ); expect(scratch).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_15> - <__snapshot_a94a8_test_16> - "Hello" - - <__snapshot_a94a8_test_17> - "World" - - + <__snapshot_a94a8_test_11> + "Hello" + "World" + `); expect([...backgroundSnapshotInstanceManager.values.keys()]).toMatchInlineSnapshot(` @@ -392,8 +382,6 @@ describe('document - background', () => { 4, 5, 6, - 7, - 8, ] `); expect(globalBackgroundSnapshotInstancesToRemove).toMatchInlineSnapshot(` @@ -409,17 +397,17 @@ describe('document - background', () => { render(jsx('Hello!'), scratch); expect(scratch).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_18> + <__snapshot_a94a8_test_12> "Hello!" - + `); render(jsx('Hello!!'), scratch); expect(scratch).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_18> + <__snapshot_a94a8_test_12> "Hello!!" - + `); }); @@ -476,7 +464,7 @@ describe('document - background', () => { render(jsx('Hello'), scratch); expect(scratch).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_25 + <__snapshot_a94a8_test_17 0="Hello" /> @@ -484,7 +472,7 @@ describe('document - background', () => { render(jsx(null), scratch); expect(scratch).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_25 + <__snapshot_a94a8_test_17 0={null} /> @@ -499,7 +487,7 @@ describe('document - background', () => { render(jsx(vi.fn()), scratch); expect(scratch).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_25 + <__snapshot_a94a8_test_17 0={[MockFunction spy]} /> @@ -509,7 +497,7 @@ describe('document - background', () => { render(jsx(vi.fn()), scratch); expect(scratch).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_25 + <__snapshot_a94a8_test_17 0={[MockFunction spy]} /> @@ -594,16 +582,16 @@ describe('document - background', () => { await Promise.resolve(); expect(scratch).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_27> + <__snapshot_a94a8_test_19> "foo" - + `); await Promise.resolve(); expect(scratch).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_26 + <__snapshot_a94a8_test_18 0="bar" /> @@ -642,26 +630,26 @@ describe('document - dual-runtime', () => { BackgroundSnapshotInstance.prototype.toJSON = backgroundSnapshotInstanceToJSON; expect(backgroundRoot).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_29> - <__snapshot_a94a8_test_30> + <__snapshot_a94a8_test_21> + <__snapshot_a94a8_test_22> "W" - - <__snapshot_a94a8_test_30> + + <__snapshot_a94a8_test_22> "0" - - <__snapshot_a94a8_test_30> + + <__snapshot_a94a8_test_22> "r" - - <__snapshot_a94a8_test_30> + + <__snapshot_a94a8_test_22> "l" - - <__snapshot_a94a8_test_30> + + <__snapshot_a94a8_test_22> "d" - - <__snapshot_a94a8_test_30> + + <__snapshot_a94a8_test_22> "_" - - + + `); delete BackgroundSnapshotInstance.prototype.toJSON; @@ -669,36 +657,38 @@ describe('document - dual-runtime', () => { expect(hydrate(JSON.parse(JSON.stringify(root)), backgroundRoot)).toMatchInlineSnapshot(` [ 3, - -88, + -64, "k", "0", 3, - -89, + -65, 0, "0", 0, - "__snapshot_a94a8_test_30", - 117, + "__snapshot_a94a8_test_22", + 91, 3, - 117, + 91, "k", "_", 0, null, - 118, + 92, 4, - 118, + 92, [ "_", ], 1, - 117, - 118, + 91, + 92, undefined, + 0, 1, - -85, - 117, + -61, + 91, undefined, + 0, ] `); @@ -708,33 +698,35 @@ describe('document - dual-runtime', () => { expect(takeGlobalSnapshotPatch()).toMatchInlineSnapshot(` [ 2, - -85, - -88, + -61, + -64, 2, - -85, - 117, + -61, + 91, 0, - "__snapshot_a94a8_test_30", - 119, + "__snapshot_a94a8_test_22", + 93, 3, - 119, + 93, "k", "1", 0, null, - 120, + 94, 3, - 120, + 94, 0, "1", 1, - 119, - 120, + 93, + 94, undefined, + 0, 1, - -85, - 119, - -90, + -61, + 93, + -66, + 0, ] `); @@ -767,7 +759,7 @@ describe('document - dual-runtime', () => { BackgroundSnapshotInstance.prototype.toJSON = backgroundSnapshotInstanceToJSON; expect(backgroundRoot).toMatchInlineSnapshot(` - <__snapshot_a94a8_test_31 /> + <__snapshot_a94a8_test_23 /> `); delete BackgroundSnapshotInstance.prototype.toJSON; diff --git a/packages/react/runtime/__test__/renderToOpcodes.test.jsx b/packages/react/runtime/__test__/renderToOpcodes.test.jsx index 7de60a1ace..4a616a5e63 100644 --- a/packages/react/runtime/__test__/renderToOpcodes.test.jsx +++ b/packages/react/runtime/__test__/renderToOpcodes.test.jsx @@ -51,8 +51,10 @@ describe('renderToString', () => { [ 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -4, @@ -69,6 +71,7 @@ describe('renderToString', () => { "a", ], }, + 0, 2, "values", [ @@ -77,6 +80,7 @@ describe('renderToString', () => { 3, [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -4, @@ -87,6 +91,7 @@ describe('renderToString', () => { }, "hello world", ], + 0, 1, ] `); @@ -112,10 +117,13 @@ describe('renderToString', () => { [ 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -5, @@ -137,9 +145,12 @@ describe('renderToString', () => { "values": undefined, }, 0, + 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -5, @@ -154,9 +165,11 @@ describe('renderToString', () => { "type": "__snapshot_a94a8_test_2", "values": undefined, }, + 0, 3, [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -5, @@ -167,6 +180,7 @@ describe('renderToString', () => { }, 1000, ], + 0, 1, 1, ] @@ -195,10 +209,13 @@ describe('renderToString', () => { [ 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -4, @@ -220,9 +237,12 @@ describe('renderToString', () => { "values": undefined, }, 0, + 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -4, @@ -237,9 +257,11 @@ describe('renderToString', () => { "type": "__snapshot_a94a8_test_4", "values": undefined, }, + 0, 3, [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -4, @@ -250,6 +272,7 @@ describe('renderToString', () => { }, 1, ], + 0, 1, 1, ] @@ -272,6 +295,7 @@ describe('renderToString', () => { [ 0, { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -2, @@ -281,6 +305,7 @@ describe('renderToString', () => { "hello world", ], }, + 0, 2, "values", [ @@ -306,8 +331,10 @@ describe('renderToString', () => { [ 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -3, @@ -322,9 +349,11 @@ describe('renderToString', () => { "type": "__snapshot_a94a8_test_7", "values": undefined, }, + 0, 3, [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -3, @@ -335,6 +364,7 @@ describe('renderToString', () => { }, "111", ], + 0, 1, ] `); @@ -356,8 +386,10 @@ describe('renderToString', () => { [ 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -3, @@ -372,9 +404,11 @@ describe('renderToString', () => { "type": "__snapshot_a94a8_test_8", "values": undefined, }, + 0, 3, [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -3, @@ -385,6 +419,7 @@ describe('renderToString', () => { }, "111", ], + 0, 1, ] `); @@ -404,8 +439,10 @@ describe('renderToString', () => { [ 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -3, @@ -420,9 +457,11 @@ describe('renderToString', () => { "type": "__snapshot_a94a8_test_9", "values": undefined, }, + 0, 3, [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -3, @@ -433,6 +472,7 @@ describe('renderToString', () => { }, "111", ], + 0, 1, ] `); @@ -451,10 +491,13 @@ describe('renderToString', () => { [ 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -4, @@ -476,9 +519,12 @@ describe('renderToString', () => { "values": undefined, }, 0, + 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -4, @@ -493,9 +539,11 @@ describe('renderToString', () => { "type": "__snapshot_a94a8_test_11", "values": undefined, }, + 0, 3, [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -4, @@ -506,6 +554,7 @@ describe('renderToString', () => { }, 11111, ], + 0, 1, 1, ] @@ -524,10 +573,13 @@ describe('renderToString', () => { [ 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -7, @@ -549,9 +601,12 @@ describe('renderToString', () => { "values": undefined, }, 0, + 0, { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -7, @@ -566,9 +621,11 @@ describe('renderToString', () => { "type": "__snapshot_a94a8_test_13", "values": undefined, }, + 0, 3, [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -7, @@ -579,6 +636,7 @@ describe('renderToString', () => { }, 12345, ], + 0, 1, 1, ] @@ -672,10 +730,12 @@ describe('renderToString', () => { , ); - expect(rendered.length).toBe(3); + expect(rendered.length).toBe(4); expect(rendered[0]).toStrictEqual(0); expect(rendered[1].type).toStrictEqual(fallbackJsx.type); - expect(rendered[2]).toStrictEqual(1); + // slotIndex + expect(rendered[2]).toStrictEqual(0); + expect(rendered[3]).toStrictEqual(1); }); it('should render fallback when suspended component is not a direct child', async () => { @@ -693,10 +753,12 @@ describe('renderToString', () => { , ); - expect(rendered.length).toBe(3); + expect(rendered.length).toBe(4); expect(rendered[0]).toStrictEqual(0); expect(rendered[1].type).toStrictEqual(fallbackJsx.type); - expect(rendered[2]).toStrictEqual(1); + // slotIndex + expect(rendered[2]).toStrictEqual(0); + expect(rendered[3]).toStrictEqual(1); }); it('should render a fallback that is a Fragment', async () => { @@ -723,7 +785,7 @@ describe('renderToString', () => { ); expect(rendered[1].type).toStrictEqual(fallbackJsx1.type); - expect(rendered[6].type).toStrictEqual(fallbackJsx2.type); + expect(rendered[8].type).toStrictEqual(fallbackJsx2.type); }); it('should render outer fallback when nested child suspends', async () => { @@ -748,10 +810,12 @@ describe('renderToString', () => { , ); - expect(rendered.length).toBe(3); + expect(rendered.length).toBe(4); expect(rendered[0]).toStrictEqual(0); expect(rendered[1].type).toStrictEqual(fallbackJsx1.type); - expect(rendered[2]).toStrictEqual(1); + // slotIndex + expect(rendered[2]).toStrictEqual(0); + expect(rendered[3]).toStrictEqual(1); }); it('should render inner fallback and resolved content when outer suspense is resolved', async () => { @@ -781,10 +845,10 @@ describe('renderToString', () => { , ); - expect(rendered.length).toBe(9); + expect(rendered.length).toBe(12); expect(rendered[1].type).toStrictEqual(resolvedJsx1.type); - expect(rendered[4].type).toStrictEqual(fallbackJsx2.type); - expect(rendered[7].type).toStrictEqual(resolvedJsx2.type); + expect(rendered[5].type).toStrictEqual(fallbackJsx2.type); + expect(rendered[9].type).toStrictEqual(resolvedJsx2.type); }); it('should render text with resolved suspense', async () => { @@ -802,10 +866,12 @@ describe('renderToString', () => { , ); - expect(rendered.length).toBe(3); + expect(rendered.length).toBe(4); expect(rendered[0]).toStrictEqual(0); expect(rendered[1].type).toStrictEqual(resolvedJsx.type); - expect(rendered[2]).toStrictEqual(1); + // slotIndex + expect(rendered[2]).toStrictEqual(0); + expect(rendered[3]).toStrictEqual(1); }); it('should render text with nested suspense', async () => { @@ -845,12 +911,83 @@ describe('renderToString', () => { , ); - expect(rendered.length).toBe(18); - expect(rendered[3].type).toStrictEqual(resolvedJsx1.type); - expect(rendered[6].type).toStrictEqual(resolvedJsx2.type); - expect(rendered[9].type).toStrictEqual(resolvedJsx3.type); - expect(rendered[12].type).toStrictEqual(resolvedJsx4.type); - expect(rendered[15].type).toStrictEqual(resolvedJsx5.type); + expect(rendered.length).toBe(24); + expect(rendered[4].type).toStrictEqual(resolvedJsx1.type); + expect(rendered[8].type).toStrictEqual(resolvedJsx2.type); + expect(rendered[12].type).toStrictEqual(resolvedJsx3.type); + expect(rendered[16].type).toStrictEqual(resolvedJsx4.type); + expect(rendered[20].type).toStrictEqual(resolvedJsx5.type); + }); + + it('should only render text when it is not empty', () => { + expect(renderToString( + + + {''} + Static Text + , + )).toMatchInlineSnapshot(` + [ + 0, + { + "__slotIndex": 0, + "children": undefined, + "extraProps": undefined, + "id": -2, + "type": "__snapshot_a94a8_test_47", + "values": undefined, + }, + 0, + 1, + ] + `); + expect(renderToString( + + + {'Dynamic Text'} + Static Text + , + )).toMatchInlineSnapshot(` + [ + 0, + { + "__slotIndex": 0, + "children": [ + { + "__slotIndex": 0, + "children": undefined, + "extraProps": undefined, + "id": -4, + "type": null, + "values": [ + "Dynamic Text", + ], + }, + ], + "extraProps": undefined, + "id": -3, + "type": "__snapshot_a94a8_test_48", + "values": undefined, + }, + 0, + 3, + [ + { + "__slotIndex": 0, + "children": undefined, + "extraProps": undefined, + "id": -4, + "type": null, + "values": [ + "Dynamic Text", + ], + }, + "Dynamic Text", + ], + 0, + 1, + ] + `); }); }); @@ -1188,7 +1325,8 @@ describe('renderOpcodesInto', () => { `); - const [vnodeA, vnodeB, vnodeC, vnodeC2, vnodeD] = scratch.__firstChild.props.children; + debugger; + const [vnodeA, vnodeB, vnodeC, vnodeC2, vnodeD] = scratch.__firstChild.props.$0; expect(vnodeA).not.toHaveProperty('__elements'); expect(vnodeA).not.toHaveProperty('__element_root'); @@ -1200,7 +1338,7 @@ describe('renderOpcodesInto', () => { expect(vnodeD).not.toHaveProperty('__element_root'); { - const componentVNodeC = vnodeC2.props.children; + const componentVNodeC = vnodeC2.props.$0; expect(componentVNodeC.type).toBe(Fragment); expect(componentVNodeC.props.children).toHaveLength(4); // FIXME(hzy): there is still a cycle reference @@ -1211,8 +1349,8 @@ describe('renderOpcodesInto', () => { }); } - expect(vnodeD.props.children).toHaveLength(4); - vnodeD.props.children.forEach((vnode) => { + expect(vnodeD.props.$0).toHaveLength(4); + vnodeD.props.$0.forEach((vnode) => { expect(vnode).not.toHaveProperty('__elements'); expect(vnode).not.toHaveProperty('__element_root'); }); @@ -1237,12 +1375,14 @@ describe('createElement', () => { [ 0, { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -41, - "type": "__snapshot_a94a8_test_74", + "type": "__snapshot_a94a8_test_76", "values": undefined, }, + 0, 1, ] `); @@ -1251,12 +1391,14 @@ describe('createElement', () => { [ 0, { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -42, - "type": "__snapshot_a94a8_test_74", + "type": "__snapshot_a94a8_test_76", "values": undefined, }, + 0, 1, ] `); @@ -1273,12 +1415,14 @@ describe('createElement', () => { [ 0, { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -43, - "type": "__snapshot_a94a8_test_75", + "type": "__snapshot_a94a8_test_77", "values": undefined, }, + 0, 1, ] `); @@ -1287,12 +1431,14 @@ describe('createElement', () => { [ 0, { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": -44, - "type": "__snapshot_a94a8_test_75", + "type": "__snapshot_a94a8_test_77", "values": undefined, }, + 0, 1, ] `); diff --git a/packages/react/runtime/__test__/snapshot/dynamicPartType.test.jsx b/packages/react/runtime/__test__/snapshot/dynamicPartType.test.jsx new file mode 100644 index 0000000000..7702fe53fe --- /dev/null +++ b/packages/react/runtime/__test__/snapshot/dynamicPartType.test.jsx @@ -0,0 +1,689 @@ +/** @jsxImportSource ../../lepus */ + +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import * as ReactLynx from '../../src/internal'; +import { setupPage, SnapshotInstance, backgroundSnapshotInstanceManager } from '../../src/snapshot'; +import { hydrate } from '../../src/renderToOpcodes/hydrate'; +import { BackgroundSnapshotInstance, hydrate as backgroundHydrate } from '../../src/snapshot/backgroundSnapshot'; +import { __pendingListUpdates } from '../../src/list/pendingListUpdates'; +import { elementTree } from '../utils/nativeMethod'; +import { prettyFormatSnapshotPatch } from '../../src/debug/formatPatch'; +import renderToString from '../../src/renderToOpcodes'; +import { clearListGlobal, gRecycleMap, gSignMap } from '../../src/list/list'; +import { jsx } from '../../lepus/jsx-runtime'; +import { globalEnvManager } from '../utils/envManager'; + +const HOLE = null; + +afterEach(() => { + elementTree.clear(); + clearListGlobal(); +}); + +describe('legacy DynamicPartType should work', () => { + it('DynamicPartType.Slot', () => { + // Input: + // + // Hello, ReactLynx, {hello1} + // {hello2} + // + const s0 = ReactLynx.createSnapshot( + 's0', + function() { + const pageId = ReactLynx.__pageId; + const el = __CreateView(pageId); + const el1 = __CreateText(pageId); + __AppendElement(el, el1); + const el2 = __CreateRawText('Hello, ReactLynx, '); + __AppendElement(el1, el2); + const el3 = __CreateWrapperElement(pageId); + __AppendElement(el1, el3); + const el4 = __CreateWrapperElement(pageId); + __AppendElement(el, el4); + return [ + el, + el1, + el2, + el3, + el4, + ]; + }, + null, + [ + [ + ReactLynx.__DynamicPartSlot, + 3, + ], + [ + ReactLynx.__DynamicPartSlot, + 4, + ], + ], + undefined, + globDynamicComponentEntry, + null, + ); + + const s1 = __SNAPSHOT__(hello1); + const s2 = __SNAPSHOT__(hello2); + + const a = new SnapshotInstance(s0); + a.ensureElements(); + const root = a.__element_root; + + const b1 = new SnapshotInstance('wrapper'); + const b2 = new SnapshotInstance(s1); + b1.insertBefore(b2); + + const c1 = new SnapshotInstance('wrapper'); + const c2 = new SnapshotInstance(s2); + c1.insertBefore(c2); + + a.insertBefore(b1); + a.insertBefore(c1); + + expect(root).toMatchInlineSnapshot(` + + + + + + + + + + + + + + + + `); + + const aa = new BackgroundSnapshotInstance(s0); + { + const s1 = __SNAPSHOT__(hello3); + const s2 = __SNAPSHOT__(hello4); + + const b1 = new BackgroundSnapshotInstance('wrapper'); + const b2 = new BackgroundSnapshotInstance(s1); + b1.insertBefore(b2); + const c1 = new BackgroundSnapshotInstance('wrapper'); + const c2 = new BackgroundSnapshotInstance(s2); + c1.insertBefore(c2); + + aa.insertBefore(b1); + aa.insertBefore(c1); + + expect(prettyFormatSnapshotPatch(backgroundHydrate(JSON.parse(JSON.stringify(a)), aa))).toMatchInlineSnapshot(` + [ + { + "childId": -11, + "op": "RemoveChild", + "parentId": -10, + }, + { + "id": 3, + "op": "CreateElement", + "type": "__snapshot_a94a8_test_3", + }, + { + "beforeId": undefined, + "childId": 3, + "op": "InsertBefore", + "parentId": -10, + "slotIndex": undefined, + }, + { + "childId": -13, + "op": "RemoveChild", + "parentId": -12, + }, + { + "id": 5, + "op": "CreateElement", + "type": "__snapshot_a94a8_test_4", + }, + { + "beforeId": undefined, + "childId": 5, + "op": "InsertBefore", + "parentId": -12, + "slotIndex": undefined, + }, + ] + `); + backgroundSnapshotInstanceManager.values.forEach((v, k) => { + expect(k).toEqual(v.__id); + }); + } + + { + const slotType = a.__snapshot_def.slot[0][0]; + // some supported slot type + a.__snapshot_def.slot[0][0] = -1; + expect(() => backgroundHydrate(JSON.parse(JSON.stringify(a)), aa)).toThrowErrorMatchingInlineSnapshot( + `[Error: Unexpected slot type: -1]`, + ); + a.__snapshot_def.slot[0][0] = slotType; + } + + { + const slotType = aa.__snapshot_def.slot[0][0]; + // some supported slot type + aa.__snapshot_def.slot[0][0] = -1; + expect(() => backgroundHydrate(JSON.parse(JSON.stringify(a)), aa)).toThrowErrorMatchingInlineSnapshot( + `[Error: Unexpected slot type: -1]`, + ); + aa.__snapshot_def.slot[0][0] = slotType; + } + }); + it('DynamicPartType.Slot with list-item hydrate', () => { + const s0 = __SNAPSHOT__({HOLE}); + const b = new SnapshotInstance(s0); + b.ensureElements(); + const listRef = b.__element_root; + + // Input: + // + // {HOLE}!{HOLE} + // + const s1 = ReactLynx.createSnapshot( + 's1', + function() { + const pageId = ReactLynx.__pageId; + const el = __CreateElement('list-item', pageId); + const el1 = __CreateWrapperElement(pageId); + __AppendElement(el, el1); + const el2 = __CreateRawText('!'); + __AppendElement(el, el2); + const el3 = __CreateWrapperElement(pageId); + __AppendElement(el, el3); + return [ + el, + el1, + el2, + el3, + ]; + }, + [ + (snapshot, index, oldValue) => ReactLynx.updateListItemPlatformInfo(snapshot, index, oldValue, 0), + ], + [ + [ + ReactLynx.__DynamicPartSlot, + 1, + ], + [ + ReactLynx.__DynamicPartSlot, + 3, + ], + ], + void 0, + globDynamicComponentEntry, + null, + ); + + const slot = __SNAPSHOT__({HOLE}); + + const c0 = new SnapshotInstance(s1); + const c1 = new SnapshotInstance(s1); + b.insertBefore(c0); + b.insertBefore(c1); + + const c0_d0 = new SnapshotInstance(slot); + const c0_d1 = new SnapshotInstance(slot); + c0.insertBefore(c0_d0); + c0.insertBefore(c0_d1); + + const c1_d0 = new SnapshotInstance(slot); + const c1_d1 = new SnapshotInstance(slot); + c1.insertBefore(c1_d0); + c1.insertBefore(c1_d1); + + __pendingListUpdates.flush(); + + const component = []; + + { + component[0] = elementTree.triggerComponentAtIndex(listRef, 0); + elementTree.triggerEnqueueComponent(listRef, component[0]); + const slotType = c0.__snapshot_def.slot[0][0]; + // some supported slot type + c0.__snapshot_def.slot[0][0] = -1; + expect(() => elementTree.triggerComponentAtIndex(listRef, 1)).toThrowErrorMatchingInlineSnapshot( + `[Error: Unexpected slot type: -1]`, + ); + c0.__snapshot_def.slot[0][0] = slotType; + } + + component[0] = elementTree.triggerComponentAtIndex(listRef, 0); + elementTree.triggerEnqueueComponent(listRef, component[0]); + component[1] = elementTree.triggerComponentAtIndex(listRef, 1); + expect(component[1]).toBe(component[0]); + elementTree.triggerEnqueueComponent(listRef, component[1]); + component[1] = elementTree.triggerComponentAtIndex(listRef, 0); + expect(component[0]).toBe(component[1]); + + expect(listRef).toMatchInlineSnapshot(` + + + + + + + + + + + + + `); + }); + describe('DynamicPartType.Children', () => { + // Input: + // + // {HOLE} + // + const s0 = ReactLynx.createSnapshot( + 's0', + function() { + const pageId = ReactLynx.__pageId; + const el = __CreateView(pageId); + const el1 = __CreateText(pageId); + __AppendElement(el, el1); + return [ + el, + el1, + ]; + }, + null, + [ + [ + ReactLynx.__DynamicPartChildren, + 1, + ], + ], + undefined, + globDynamicComponentEntry, + null, + ); + globalEnvManager.switchToMainThread(); + + it('with single text child', () => { + setupPage(__CreatePage('0', 0)); + const scratch = document.createElement('root'); + scratch.ensureElements(); + + const opcodes = renderToString( + jsx(s0, { + children: 'hello', + }), + null, + scratch, + ); + + expect(opcodes).toMatchInlineSnapshot(` + [ + 0, + { + "__slotIndex": 0, + "children": [ + { + "__slotIndex": 0, + "children": undefined, + "extraProps": undefined, + "id": -27, + "type": null, + "values": [ + "hello", + ], + }, + ], + "extraProps": undefined, + "id": -26, + "type": "__Card__:s0", + "values": undefined, + }, + 0, + 3, + [ + { + "__slotIndex": 0, + "children": undefined, + "extraProps": undefined, + "id": -27, + "type": null, + "values": [ + "hello", + ], + }, + "hello", + ], + 0, + 1, + ] + `); + + expect(scratch.__element_root).toMatchInlineSnapshot(` + + + + + + + + + + `); + }); + + it('with single children', () => { + setupPage(__CreatePage('0', 0)); + const scratch = document.createElement('root'); + scratch.ensureElements(); + + const opcodes = renderToString( + jsx(s0, { + children: children, + }), + null, + scratch, + ); + + expect(opcodes).toMatchInlineSnapshot(` + [ + 0, + { + "__slotIndex": 0, + "children": [ + { + "__slotIndex": 0, + "children": undefined, + "extraProps": undefined, + "id": -29, + "type": "__snapshot_a94a8_test_7", + "values": undefined, + }, + ], + "extraProps": undefined, + "id": -30, + "type": "__Card__:s0", + "values": undefined, + }, + 0, + 0, + { + "__slotIndex": 0, + "children": undefined, + "extraProps": undefined, + "id": -29, + "type": "__snapshot_a94a8_test_7", + "values": undefined, + }, + 0, + 1, + 1, + ] + `); + + expect(scratch.__element_root).toMatchInlineSnapshot(` + + + + + + + + + + + + `); + }); + }); +}); + +describe('DynamicPartType v2 should work', () => { + const slotV2Host = ReactLynx.createSnapshot( + 'slot_v2_host', + function() { + const pageId = ReactLynx.__pageId; + const el = __CreateView(pageId); + return [el]; + }, + null, + [ + [ + ReactLynx.__DynamicPartSlotV2, + 0, + ], + ], + undefined, + globDynamicComponentEntry, + null, + ); + + const listSlotV2Host = ReactLynx.createSnapshot( + 'list_slot_v2_host', + function() { + const pageId = ReactLynx.__pageId; + const el = __CreateElement('list', pageId); + return [el]; + }, + null, + [ + [ + ReactLynx.__DynamicPartListSlotV2, + 0, + ], + ], + undefined, + globDynamicComponentEntry, + null, + ); + + const slotTextA = __SNAPSHOT__(A); + const slotTextB = __SNAPSHOT__(B); + const listItemA = __SNAPSHOT__( + + A + , + ); + const listItemB = __SNAPSHOT__( + + B + , + ); + + it('renderToString should treat named children props as slot children', () => { + const scratch = document.createElement('root'); + scratch.ensureElements(); + + const opcodes = renderToString( + jsx(slotV2Host, { + $0: named child, + }), + null, + scratch, + ); + + expect(opcodes).toEqual([ + 0, + expect.objectContaining({ + type: '__Card__:slot_v2_host', + }), + 0, + 0, + expect.objectContaining({ + type: expect.stringMatching(/^__snapshot_/), + }), + 0, + 1, + 1, + ]); + }); + + it('hydrate should diff SlotV2 children by slot index', () => { + setupPage(__CreatePage('0', 0)); + + const before = new SnapshotInstance(slotV2Host); + before.ensureElements(); + + const beforeChild = new SnapshotInstance(slotTextA); + beforeChild.__slotIndex = 0; + before.insertBefore(beforeChild); + + expect(before.__element_root).toMatchInlineSnapshot(` + + + + + + `); + + const after = new SnapshotInstance(slotV2Host); + const afterChild = new SnapshotInstance(slotTextB); + afterChild.__slotIndex = 0; + after.insertBefore(afterChild); + + hydrate(before, after); + + expect(before.__element_root).toMatchInlineSnapshot(` + + + + + + `); + }); + + it('background hydrate should diff SlotV2 children by slot index', () => { + setupPage(__CreatePage('0', 0)); + + const before = new SnapshotInstance(slotV2Host); + const beforeChild = new SnapshotInstance(slotTextA); + beforeChild.__slotIndex = 0; + before.insertBefore(beforeChild); + + const after = new BackgroundSnapshotInstance(slotV2Host); + const afterChild = new BackgroundSnapshotInstance(slotTextB); + afterChild.__slotIndex = 0; + after.insertBefore(afterChild); + + const patch = prettyFormatSnapshotPatch( + backgroundHydrate(JSON.parse(JSON.stringify(before)), after), + ); + + expect(patch).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + op: 'RemoveChild', + parentId: before.__id, + childId: before.childNodes[0].__id, + }), + expect.objectContaining({ + op: 'CreateElement', + type: slotTextB, + }), + expect.objectContaining({ + op: 'InsertBefore', + parentId: before.__id, + beforeId: undefined, + slotIndex: 0, + }), + ]), + ); + }); + + it('hydrate should diff ListSlotV2 children by slot index', () => { + setupPage(__CreatePage('0', 0)); + + const before = new SnapshotInstance(listSlotV2Host); + before.ensureElements(); + + const listID = __GetElementUniqueID(before.__element_root); + gSignMap[listID] = new Map(); + gRecycleMap[listID] = new Map(); + + const beforeChild = new SnapshotInstance(listItemA); + beforeChild.__slotIndex = 0; + beforeChild.__listItemPlatformInfo = { 'item-key': 0 }; + before.insertBefore(beforeChild); + + const after = new SnapshotInstance(listSlotV2Host); + const afterChild = new SnapshotInstance(listItemB); + afterChild.__slotIndex = 0; + afterChild.__listItemPlatformInfo = { 'item-key': 0 }; + after.insertBefore(afterChild); + + hydrate(before, after); + + expect(__GetAttributeByName(before.__element_root, 'update-list-info')).toEqual([ + { + insertAction: [ + { + position: 0, + type: listItemB, + 'item-key': 0, + }, + ], + removeAction: [0], + updateAction: [], + }, + ]); + }); +}); diff --git a/packages/react/runtime/__test__/snapshot/event.test.jsx b/packages/react/runtime/__test__/snapshot/event.test.jsx index 2d0f9c3939..00b171250c 100644 --- a/packages/react/runtime/__test__/snapshot/event.test.jsx +++ b/packages/react/runtime/__test__/snapshot/event.test.jsx @@ -320,6 +320,7 @@ describe('eventUpdate', () => { -2, 3, undefined, + 0, ] `); globalEnvManager.switchToMainThread(); @@ -948,6 +949,7 @@ describe('event in spread', () => { -2, 3, undefined, + 0, ] `); globalEnvManager.switchToMainThread(); @@ -1154,7 +1156,7 @@ describe('event when firstScreenSyncTiming is jsReady', () => { "-5": -8, "-6": -9, }, - "root": "{"id":-7,"type":"root","children":[{"id":-8,"type":"__snapshot_a94a8_test_12","children":[{"id":-9,"type":"__snapshot_a94a8_test_11","values":["-9:0:"]}]}]}", + "root": "{"id":-7,"type":"root","children":[{"id":-8,"type":"__snapshot_a94a8_test_12","children":[{"id":-9,"type":"__snapshot_a94a8_test_11","values":["-9:0:"],"__slotIndex":0}],"__slotIndex":0}]}", }, ], ] @@ -1263,7 +1265,7 @@ describe('call `root.render()` async', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_14","children":[{"id":-3,"type":"__snapshot_a94a8_test_13","values":["-3:0:"]}]}]}", + "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_14","children":[{"id":-3,"type":"__snapshot_a94a8_test_13","values":["-3:0:"],"__slotIndex":0}],"__slotIndex":0}]}", }, ], ] diff --git a/packages/react/runtime/__test__/snapshot/ref.test.jsx b/packages/react/runtime/__test__/snapshot/ref.test.jsx index b4027b873d..222fa622c7 100644 --- a/packages/react/runtime/__test__/snapshot/ref.test.jsx +++ b/packages/react/runtime/__test__/snapshot/ref.test.jsx @@ -117,7 +117,7 @@ describe('element ref', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_3","values":["react-ref--2-0","react-ref--2-1"]}]}", + "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_3","values":["react-ref--2-0","react-ref--2-1"],"__slotIndex":0}]}", }, ], ] @@ -202,7 +202,7 @@ describe('element ref', () => { render(, __root); expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1].data).toMatchInlineSnapshot( - `"{"patchList":[{"id":3,"snapshotPatch":[0,"__snapshot_a94a8_test_4",2,4,2,[1,1],1,-1,2,null]}]}"`, + `"{"patchList":[{"id":3,"snapshotPatch":[0,"__snapshot_a94a8_test_4",2,4,2,[1,1],1,-1,2,null,0]}]}"`, ); } @@ -496,7 +496,7 @@ describe('element ref', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_9","values":["react-ref--2-0","react-ref--2-1","react-ref--2-2"]}]}", + "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_9","values":["react-ref--2-0","react-ref--2-1","react-ref--2-2"],"__slotIndex":0}]}", }, ], ] @@ -903,7 +903,7 @@ describe('element ref', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_17"}]}", + "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_17","__slotIndex":0}]}", }, ], ] @@ -938,7 +938,7 @@ describe('element ref', () => { lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]); expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1].data).toMatchInlineSnapshot( - `"{"patchList":[{"snapshotPatch":[0,"__snapshot_a94a8_test_18",3,4,3,[1],1,-2,3,null],"id":2}]}"`, + `"{"patchList":[{"snapshotPatch":[0,"__snapshot_a94a8_test_18",3,4,3,[1],1,-2,3,null,0],"id":2}]}"`, ); // rLynxChange @@ -1015,7 +1015,7 @@ describe('element ref in spread', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_19","values":[{},{"ref":"react-ref--2-1"},{}]}]}", + "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a94a8_test_19","values":[{},{"ref":"react-ref--2-1"},{}],"__slotIndex":0}]}", }, ], ], @@ -1960,7 +1960,7 @@ describe('ui operations', () => { render(, __root); expect(lynx.getNativeApp().callLepusMethod.mock.calls[0][1].data).toMatchInlineSnapshot( - `"{"patchList":[{"id":3,"snapshotPatch":[0,"__snapshot_a94a8_test_30",3,4,3,[1],1,-2,3,null]}]}"`, + `"{"patchList":[{"id":3,"snapshotPatch":[0,"__snapshot_a94a8_test_30",3,4,3,[1],1,-2,3,null,0]}]}"`, ); expect(lynx.createSelectorQuery().constructor.execLog.mock.calls).toMatchInlineSnapshot(` [ diff --git a/packages/react/runtime/__test__/snapshotPatch.test.jsx b/packages/react/runtime/__test__/snapshotPatch.test.jsx index c225cbe258..22b03bbb93 100644 --- a/packages/react/runtime/__test__/snapshotPatch.test.jsx +++ b/packages/react/runtime/__test__/snapshotPatch.test.jsx @@ -153,6 +153,7 @@ describe('insertBefore', () => { 2, 3, undefined, + undefined, ] `); @@ -203,10 +204,12 @@ describe('insertBefore', () => { 2, 4, undefined, + undefined, 1, 2, 3, 4, + undefined, ] `); @@ -252,14 +255,17 @@ describe('insertBefore', () => { 2, 100, null, + 0, SnapshotOperation.InsertBefore, 100, 2, null, + 1, SnapshotOperation.InsertBefore, 4, 100, null, + 2, ); expect(patch).toMatchInlineSnapshot(` [ @@ -273,14 +279,17 @@ describe('insertBefore', () => { 2, 100, null, + 0, 1, 100, 2, null, 1, + 1, 4, 100, null, + 2, ] `); @@ -1529,7 +1538,7 @@ describe('list', () => { bsi2.setAttribute('values', ['test']); bsi1.insertBefore(bsi2); patch = takeGlobalSnapshotPatch(); - expect(patch.length).toMatchInlineSnapshot(`10`); + expect(patch.length).toMatchInlineSnapshot(`11`); snapshotPatchApply(patch); expect(si1.__element_root).toMatchInlineSnapshot(` diff --git a/packages/react/runtime/__test__/ssr.test.jsx b/packages/react/runtime/__test__/ssr.test.jsx index f43ace6235..07bbffcb2f 100644 --- a/packages/react/runtime/__test__/ssr.test.jsx +++ b/packages/react/runtime/__test__/ssr.test.jsx @@ -60,6 +60,7 @@ describe('ssr', () => { }, ], ], + 0, 1, ], } @@ -113,6 +114,7 @@ describe('ssr', () => { ], ], 0, + 0, [ "__snapshot_a94a8_test_3", -3, @@ -132,6 +134,7 @@ describe('ssr', () => { ], ], 0, + 0, [ "__snapshot_a94a8_test_4", -4, @@ -142,6 +145,7 @@ describe('ssr', () => { ], ], 0, + 0, [ "__snapshot_a94a8_test_5", -5, @@ -163,6 +167,7 @@ describe('ssr', () => { }, ], ], + 0, 1, 1, 1, @@ -203,6 +208,7 @@ describe('ssr', () => { }, ], ], + 0, 2, "values", [ @@ -254,6 +260,7 @@ describe('ssr', () => { }, ], ], + 0, 2, "values", [ @@ -314,6 +321,7 @@ describe('ssr', () => { ], ], 0, + 0, [ "__snapshot_a94a8_test_9", -3, @@ -333,6 +341,7 @@ describe('ssr', () => { ], ], 0, + 0, [ "__snapshot_a94a8_test_10", -4, @@ -342,6 +351,7 @@ describe('ssr', () => { }, ], ], + 0, 1, 1, 1, diff --git a/packages/react/runtime/__test__/utils/debug.js b/packages/react/runtime/__test__/utils/debug.js index 199ed81ae2..f8b4aa6125 100644 --- a/packages/react/runtime/__test__/utils/debug.js +++ b/packages/react/runtime/__test__/utils/debug.js @@ -30,6 +30,7 @@ function backgroundSnapshotInstanceToJSON() { props: Object.fromEntries( this.__values?.map((v, i) => [`${i}`, v]) ?? [], ), + __slotIndex: this.__slotIndex, }; Object.defineProperty(json, '$$typeof', { value: Symbol.for('react.test.json'), diff --git a/packages/react/runtime/lazy/internal.js b/packages/react/runtime/lazy/internal.js index 8093ba94c9..5d0d3b475c 100644 --- a/packages/react/runtime/lazy/internal.js +++ b/packages/react/runtime/lazy/internal.js @@ -19,7 +19,10 @@ export const { __DynamicPartChildren, __DynamicPartChildren_0, __DynamicPartListChildren, + __DynamicPartListSlotV2, __DynamicPartSlot, + __DynamicPartSlotV2, + __DynamicPartSlotV2_0, __DynamicPartMultiChildren, __dynamicImport, __page, diff --git a/packages/react/runtime/src/document.ts b/packages/react/runtime/src/document.ts index 50ff8678c0..c0ed053b4c 100644 --- a/packages/react/runtime/src/document.ts +++ b/packages/react/runtime/src/document.ts @@ -33,14 +33,14 @@ const document: SnapshotDocumentAdapter = {} as SnapshotDocumentAdapter; * Sets up the document interface for the background thread. * All DOM operations are intercepted to create {@link BackgroundSnapshotInstance}. */ -function setupBackgroundDocument(): void { - document.createElement = function(type: string) { +function setupBackgroundDocument(_document: SnapshotDocumentAdapter = document): void { + _document.createElement = function(type: string) { return new BackgroundSnapshotInstance(type); }; - document.createElementNS = function(_ns: string, type: string) { + _document.createElementNS = function(_ns: string, type: string, _is?: string) { return new BackgroundSnapshotInstance(type); }; - document.createTextNode = function(text: string) { + _document.createTextNode = function(text: string) { const i = new BackgroundSnapshotInstance(null as unknown as string); i.setAttribute(0, text); Object.defineProperty(i, 'data', { @@ -56,14 +56,16 @@ function setupBackgroundDocument(): void { * Sets up the document interface for the main thread. * All DOM operations are intercepted to create {@link SnapshotInstance}. */ -function setupDocument(): void { - document.createElement = function(type: string) { - return new SnapshotInstance(type); +function setupDocument(_document: SnapshotDocumentAdapter = document): void { + _document.createElement = function(type: string) { + const si = new SnapshotInstance(type); + return si; }; - document.createElementNS = function(_ns: string, type: string) { - return new SnapshotInstance(type); + _document.createElementNS = function(_ns: string, type: string, _is?: string) { + const si = new SnapshotInstance(type); + return si; }; - document.createTextNode = function(text: string) { + _document.createTextNode = function(text: string) { const i = new SnapshotInstance(null as unknown as string); i.setAttribute(0, text); Object.defineProperty(i, 'data', { diff --git a/packages/react/runtime/src/internal.ts b/packages/react/runtime/src/internal.ts index 043b77c151..7c4decbad4 100644 --- a/packages/react/runtime/src/internal.ts +++ b/packages/react/runtime/src/internal.ts @@ -36,6 +36,11 @@ export const __DynamicPartChildren: DynamicPartType = DynamicPartType.Children; export const __DynamicPartListChildren: DynamicPartType = DynamicPartType.ListChildren; export { __DynamicPartChildren_0 } from './snapshot/dynamicPartType.js'; +// v2 slot +export const __DynamicPartSlotV2: DynamicPartType = DynamicPartType.SlotV2; +export const __DynamicPartListSlotV2: DynamicPartType = DynamicPartType.ListSlotV2; +export const __DynamicPartSlotV2_0: [DynamicPartType, number][] = [[DynamicPartType.SlotV2, 0]]; + export { updateSpread } from './snapshot/spread.js'; export { updateEvent } from './snapshot/event.js'; export { updateRef, transformRef } from './snapshot/ref.js'; diff --git a/packages/react/runtime/src/lifecycle/patch/snapshotPatch.ts b/packages/react/runtime/src/lifecycle/patch/snapshotPatch.ts index 00ac03e2b8..42081385bc 100644 --- a/packages/react/runtime/src/lifecycle/patch/snapshotPatch.ts +++ b/packages/react/runtime/src/lifecycle/patch/snapshotPatch.ts @@ -21,10 +21,18 @@ export const SnapshotOperation = { } as const; export const SnapshotOperationParams: Record = /* @__PURE__ */ { - [SnapshotOperation.CreateElement]: { name: 'CreateElement', params: ['type', /* string */ 'id' /* number */] }, + [SnapshotOperation.CreateElement]: { + name: 'CreateElement', + params: ['type', /* string */ 'id' /* number */], + }, [SnapshotOperation.InsertBefore]: { name: 'InsertBefore', - params: ['parentId', /* number */ 'childId', /* number */ 'beforeId' /* number | undefined */], + params: [ + 'parentId', + /* number */ 'childId', + /* number */ 'beforeId', + /* number | undefined */ 'slotIndex', /* number | undefined */ + ], }, [SnapshotOperation.RemoveChild]: { name: 'RemoveChild', params: ['parentId', /* number */ 'childId' /* number */] }, [SnapshotOperation.SetAttribute]: { diff --git a/packages/react/runtime/src/lifecycle/patch/snapshotPatchApply.ts b/packages/react/runtime/src/lifecycle/patch/snapshotPatchApply.ts index 62565c47cc..ff205a6e0d 100644 --- a/packages/react/runtime/src/lifecycle/patch/snapshotPatchApply.ts +++ b/packages/react/runtime/src/lifecycle/patch/snapshotPatchApply.ts @@ -37,12 +37,14 @@ export function snapshotPatchApply(snapshotPatch: SnapshotPatch): void { const parentId = snapshotPatch[++i] as number; const childId = snapshotPatch[++i] as number; const beforeId = snapshotPatch[++i] as number | undefined; + const __slotIndex = snapshotPatch[++i] as number | undefined; const parent = snapshotInstanceManager.values.get(parentId); const child = snapshotInstanceManager.values.get(childId); const existingNode = snapshotInstanceManager.values.get(beforeId!); if (!parent || !child) { sendCtxNotFoundEventToBackground(parent ? childId : parentId); } else { + child.__slotIndex = __slotIndex; parent.insertBefore(child, existingNode); } break; diff --git a/packages/react/runtime/src/lynx/suspense.ts b/packages/react/runtime/src/lynx/suspense.ts index 49cf8ec111..69a34cac01 100644 --- a/packages/react/runtime/src/lynx/suspense.ts +++ b/packages/react/runtime/src/lynx/suspense.ts @@ -20,30 +20,38 @@ export const Suspense: FunctionComponent<{ children: VNode | VNode[]; fallback: const newChildren = __createElement( 'wrapper', - __MAIN_THREAD__ ? {} : { - ref: (bsi: BackgroundSnapshotInstance) => { - if (bsi) { - childrenRef.current = bsi; - } + __MAIN_THREAD__ + ? { + $0: children, + } + : { + ref: (bsi: BackgroundSnapshotInstance) => { + if (bsi) { + childrenRef.current = bsi; + } + }, + $0: children, }, - }, - children, ); const newFallback = __createElement( 'wrapper', - __MAIN_THREAD__ ? {} : { - ref: (bsi: BackgroundSnapshotInstance) => { - if (bsi && childrenRef.current) { - const i = globalBackgroundSnapshotInstancesToRemove.indexOf(childrenRef.current.__id); - if (i !== -1) { - globalBackgroundSnapshotInstancesToRemove.splice(i, 1); + __MAIN_THREAD__ + ? { + $0: fallback, + } + : { + ref: (bsi: BackgroundSnapshotInstance) => { + if (bsi && childrenRef.current) { + const i = globalBackgroundSnapshotInstancesToRemove.indexOf(childrenRef.current.__id); + if (i !== -1) { + globalBackgroundSnapshotInstancesToRemove.splice(i, 1); + } + childrenRef.current = undefined; } - childrenRef.current = undefined; - } + }, + $0: fallback, }, - }, - fallback, ); return __createElement(PreactSuspense, { fallback: newFallback }, newChildren); diff --git a/packages/react/runtime/src/renderToOpcodes/hydrate.ts b/packages/react/runtime/src/renderToOpcodes/hydrate.ts index cee54eaabe..64a5e3403d 100644 --- a/packages/react/runtime/src/renderToOpcodes/hydrate.ts +++ b/packages/react/runtime/src/renderToOpcodes/hydrate.ts @@ -262,10 +262,18 @@ export function hydrate(before: SnapshotInstance, after: SnapshotInstance, optio hydrate(v1, v2, options); break; } + case DynamicPartType.SlotV2: case DynamicPartType.Children: { + let filteredBeforeChildNodes = beforeChildNodes; + let filteredAfterChildNodes = afterChildNodes; + if (type === DynamicPartType.SlotV2) { + filteredBeforeChildNodes = beforeChildNodes.filter(v => v.__slotIndex === index); + filteredAfterChildNodes = afterChildNodes.filter(v => v.__slotIndex === index); + } + const diffResult = diffArrayLepus( - beforeChildNodes, - afterChildNodes, + filteredBeforeChildNodes, + filteredAfterChildNodes, (a, b) => a.type === b.type, (a, b) => { hydrate(a, b, options); @@ -273,7 +281,7 @@ export function hydrate(before: SnapshotInstance, after: SnapshotInstance, optio false, ); diffArrayAction( - beforeChildNodes, + filteredBeforeChildNodes, diffResult, (node, target) => { node.ensureElements(); @@ -304,7 +312,15 @@ export function hydrate(before: SnapshotInstance, after: SnapshotInstance, optio ); break; } + case DynamicPartType.ListSlotV2: case DynamicPartType.ListChildren: { + let filteredBeforeChildNodes = beforeChildNodes; + let filteredAfterChildNodes = afterChildNodes; + if (type === DynamicPartType.ListSlotV2) { + filteredBeforeChildNodes = beforeChildNodes.filter(v => v.__slotIndex === index); + filteredAfterChildNodes = afterChildNodes.filter(v => v.__slotIndex === index); + } + const removals: number[] = []; const insertions: number[] = []; const updateAction: any[] = []; @@ -314,8 +330,8 @@ export function hydrate(before: SnapshotInstance, after: SnapshotInstance, optio const recycleMap = gRecycleMap[listID]!; const diffResult = diffArrayLepus( - beforeChildNodes, - afterChildNodes, + filteredBeforeChildNodes, + filteredAfterChildNodes, (a, b) => a.type === b.type, (a, b, _oldIndex, newIndex) => { if ( @@ -406,7 +422,10 @@ export function hydrate(before: SnapshotInstance, after: SnapshotInstance, optio if (typeof __PROFILE__ !== 'undefined' && __PROFILE__) { profileEnd(); } + break; } + default: + throw new Error('Unexpected slot type: ' + type); } }); } diff --git a/packages/react/runtime/src/renderToOpcodes/index.ts b/packages/react/runtime/src/renderToOpcodes/index.ts index 4381d53586..451eb450bb 100644 --- a/packages/react/runtime/src/renderToOpcodes/index.ts +++ b/packages/react/runtime/src/renderToOpcodes/index.ts @@ -69,7 +69,7 @@ export function renderToString(vnode: any, context: any, into: SnapshotInstance) _renderToString( vnode, context || EMPTY_OBJ, - false, + 0, undefined, parent, opcodes, @@ -145,7 +145,7 @@ function renderClassComponent(vnode, context) { * Recursively render VNodes to HTML. * @param {VNode|any} vnode * @param {any} context - * @param {boolean} isSvgMode + * @param {number | true} slotIndex * @param {any} selectValue * @param {VNode} parent * @param {any[]} opcodes @@ -155,7 +155,7 @@ function renderClassComponent(vnode, context) { function _renderToString( vnode, context, - isSvgMode, + slotIndex, selectValue, parent, opcodes, @@ -171,7 +171,7 @@ function _renderToString( // Text VNodes: escape as HTML if (vnodeType !== 'object') { if (vnodeType === 'function') return; - renderToTextNode(into, vnode + '', opcodes); + renderToTextNode(into, vnode, opcodes, slotIndex); return; } @@ -185,7 +185,7 @@ function _renderToString( _renderToString( child, context, - isSvgMode, + slotIndex === true ? i : slotIndex, selectValue, parent, opcodes, @@ -275,7 +275,7 @@ function _renderToString( _renderToString( rendered, context, - isSvgMode, + slotIndex, selectValue, vnode, opcodes, @@ -305,7 +305,7 @@ function _renderToString( _renderToString( rendered, context, - isSvgMode, + slotIndex, selectValue, vnode, opcodes, @@ -329,6 +329,7 @@ function _renderToString( } let children; + let hasNamedChildren = false; // hack for runtime test if (process.env['NODE_ENV'] === 'test' && isValidElement(vnode) && typeof vnode.type === 'string') { @@ -339,8 +340,9 @@ function _renderToString( vnode = new SnapshotInstance(type); } if (__ENABLE_SSR__) { - opcodes.push(__OpBegin, vnode); + opcodes.push(__OpBegin, vnode, slotIndex); } + vnode.__slotIndex = slotIndex; into.insertBefore(vnode); for (const name in props) { @@ -359,7 +361,14 @@ function _renderToString( case '__source': continue; - default: {} + default: { + if (name.startsWith('$')) { + children ??= []; + children[+name.slice(1)] = v; + hasNamedChildren = true; + continue; + } + } } // write this attribute to the buffer @@ -374,13 +383,23 @@ function _renderToString( let childrenType = typeof children; if (childrenType === 'string' || childrenType === 'number') { // single text child - renderToTextNode(vnode, children, opcodes); + renderToTextNode(vnode, children, opcodes, slotIndex); } else if (children != null && children !== false && children !== true) { // recurse into this element VNode's children + let _slotIndex = slotIndex; + if (hasNamedChildren) { + // @ts-expect-error children must be an array + if (children.length === 1) { + children = children[0]; + _slotIndex = 0; + } else { + _slotIndex = true; + } + } _renderToString( children, context, - false, + _slotIndex, selectValue, vnode, opcodes, @@ -408,14 +427,15 @@ function doRender(props, state, context) { return this.constructor(props, context); } -function renderToTextNode(into: SnapshotInstance, text: string | number, opcodes: Opcode[]) { +function renderToTextNode(into: SnapshotInstance, text: string | number, opcodes: Opcode[], slotIndex: number) { const textNode = new SnapshotInstance(null); + textNode.__slotIndex = slotIndex; textNode.setAttribute(0, text); into.insertBefore(textNode); if (__ENABLE_SSR__) { // We need store the just created SnapshotInstance, or it will be lost when we leave the function text = [textNode, text]; - opcodes.push(__OpText, text); + opcodes.push(__OpText, text, slotIndex); } } diff --git a/packages/react/runtime/src/renderToOpcodes/opcodes.ts b/packages/react/runtime/src/renderToOpcodes/opcodes.ts index 365e2fc191..2428922b49 100644 --- a/packages/react/runtime/src/renderToOpcodes/opcodes.ts +++ b/packages/react/runtime/src/renderToOpcodes/opcodes.ts @@ -31,10 +31,11 @@ export function ssrHydrateByOpcodes( const [type, __id, elements] = opcodes[i + 1] as SSRSnapshotInstance; top = new SnapshotInstance(type, __id); top.__pendingElements = elements; + top.__slotIndex = opcodes[i + 2]; p.insertBefore(top); stack.push(top); - i += 2; + i += 3; break; } case OpcodeEnd: { @@ -84,11 +85,12 @@ export function ssrHydrateByOpcodes( case OpcodeText: { const [[type, __id, elements], text] = opcodes[i + 1] as [SSRSnapshotInstance, string]; const s = new SnapshotInstance(type, __id); + s.__slotIndex = opcodes[i + 2]; s.setAttribute(0, text); top.insertBefore(s); s.__elements = elements.map(({ ssrID }) => refMap![ssrID]!); s.__element_root = s.__elements[0]; - i += 2; + i += 3; break; } } diff --git a/packages/react/runtime/src/snapshot/backgroundSnapshot.ts b/packages/react/runtime/src/snapshot/backgroundSnapshot.ts index 7c08108f03..b106ca4156 100644 --- a/packages/react/runtime/src/snapshot/backgroundSnapshot.ts +++ b/packages/react/runtime/src/snapshot/backgroundSnapshot.ts @@ -125,6 +125,7 @@ export class BackgroundSnapshotInstance { __values: any[] | undefined; __snapshot_def: Snapshot; __extraProps?: Record | undefined; + __slotIndex?: number | undefined; private __parent: BackgroundSnapshotInstance | null = null; private __firstChild: BackgroundSnapshotInstance | null = null; @@ -169,6 +170,7 @@ export class BackgroundSnapshotInstance { this.__id, node.__id, beforeNode?.__id, + node.__slotIndex, ); } @@ -588,11 +590,20 @@ export function hydrate( helper(v1, v2); break; } + case DynamicPartType.SlotV2: + case DynamicPartType.ListSlotV2: case DynamicPartType.Children: case DynamicPartType.ListChildren: { + let filteredBeforeChildNodes = beforeChildNodes; + let filteredAfterChildNodes = afterChildNodes; + if (type === DynamicPartType.SlotV2 || type === DynamicPartType.ListSlotV2) { + filteredBeforeChildNodes = beforeChildNodes.filter(v => v.__slotIndex === index); + filteredAfterChildNodes = afterChildNodes.filter(v => v.__slotIndex === index); + } + const diffResult = diffArrayLepus( - beforeChildNodes, - afterChildNodes, + filteredBeforeChildNodes, + filteredAfterChildNodes, (a, b) => a.type === b.type, (a, b) => { helper(a, b); @@ -601,7 +612,7 @@ export function hydrate( false, ); diffArrayAction( - beforeChildNodes, + filteredBeforeChildNodes, diffResult, (node, target) => { if (shouldProfile) { @@ -667,6 +678,7 @@ export function hydrate( before.id, node.id, target?.id, + node.__slotIndex, ); } finally { profileEnd(); @@ -677,12 +689,15 @@ export function hydrate( before.id, node.id, target?.id, + node.__slotIndex, ); } }, ); break; } + default: + throw new Error('Unexpected slot type: ' + type); } }); }; @@ -712,6 +727,6 @@ function reconstructInstanceTree(afters: BackgroundSnapshotInstance[], parentId: child.setAttribute(key, extraProps[key]); } reconstructInstanceTree(child.childNodes, id); - __globalSnapshotPatch?.push(SnapshotOperation.InsertBefore, parentId, id, targetId); + __globalSnapshotPatch?.push(SnapshotOperation.InsertBefore, parentId, id, targetId, child.__slotIndex); } } diff --git a/packages/react/runtime/src/snapshot/definition.ts b/packages/react/runtime/src/snapshot/definition.ts index 3eb560a6cf..2d7dc694c1 100644 --- a/packages/react/runtime/src/snapshot/definition.ts +++ b/packages/react/runtime/src/snapshot/definition.ts @@ -33,6 +33,7 @@ export interface Snapshot { slot: [DynamicPartType, number][]; isListHolder?: boolean; + isSlotV2?: boolean; cssId?: number | undefined; entryName?: string | undefined; refAndSpreadIndexes?: number[] | null; @@ -140,8 +141,11 @@ export function createSnapshot( const s: Snapshot = { create, update, slot, cssId, entryName, refAndSpreadIndexes }; snapshotManager.values.set(uniqID, s); - if (slot && slot[0] && slot[0][0] === DynamicPartType.ListChildren) { - s.isListHolder = true; + if (slot && slot[0]) { + const v = slot[0][0]; + if (v === DynamicPartType.ListChildren || v === DynamicPartType.ListSlotV2) { + s.isListHolder = true; + } } return uniqID; } diff --git a/packages/react/runtime/src/snapshot/dynamicPartType.ts b/packages/react/runtime/src/snapshot/dynamicPartType.ts index dd4b296bd5..31dd068807 100644 --- a/packages/react/runtime/src/snapshot/dynamicPartType.ts +++ b/packages/react/runtime/src/snapshot/dynamicPartType.ts @@ -13,6 +13,8 @@ export const DynamicPartType = { Children: 3, // Regular children updates ListChildren: 4, // List/array children updates MultiChildren: 5, // Multiple children updates (compat layer) + SlotV2: 6, // Slot for component children (Preact MultiSlots) + ListSlotV2: 7, // List/array children updates (Preact MultiSlots) } as const; export type DynamicPartType = (typeof DynamicPartType)[keyof typeof DynamicPartType]; diff --git a/packages/react/runtime/src/snapshot/snapshot.ts b/packages/react/runtime/src/snapshot/snapshot.ts index e5b9722a64..1843ab3a2a 100644 --- a/packages/react/runtime/src/snapshot/snapshot.ts +++ b/packages/react/runtime/src/snapshot/snapshot.ts @@ -88,6 +88,7 @@ export class SnapshotInstance { __worklet_ref_set?: Set | Worklet>; __listItemPlatformInfo?: PlatformInfo; __extraProps?: Record | undefined; + __slotIndex?: number | undefined; constructor(public type: string, id?: number) { // Suspense uses 'div' @@ -161,7 +162,7 @@ export class SnapshotInstance { while (child) { child.ensureElements(); - const [type, elementIndex] = slot[index]!; + const [type, elementIndex] = slot[typeof child.__slotIndex === 'number' ? child.__slotIndex : index]!; switch (type) { case DynamicPartType.Slot: { __ReplaceElement(child.__element_root!, elements[elementIndex]!); @@ -181,10 +182,14 @@ export class SnapshotInstance { } /* v8 ignore end */ case DynamicPartType.Children: - case DynamicPartType.ListChildren: { + case DynamicPartType.ListChildren: + case DynamicPartType.SlotV2: + case DynamicPartType.ListSlotV2: { __AppendElement(elements[elementIndex]!, child.__element_root!); break; } + default: + throw new Error('Unexpected slot type: ' + type); } child = child.__nextSibling; @@ -357,18 +362,27 @@ export class SnapshotInstance { } const count = __snapshot_def.slot.length; - if (count === 1) { - const [, elementIndex] = __snapshot_def.slot[0]!; + if ( + count === 1 + || (__snapshot_def.isSlotV2 ??= __snapshot_def.slot.every(([type]) => + type === DynamicPartType.SlotV2 || type === DynamicPartType.ListSlotV2 + )) + ) { + const [, elementIndex] = __snapshot_def.slot[typeof newNode.__slotIndex === 'number' ? newNode.__slotIndex : 0]!; const parent = __elements[elementIndex]!; if (shouldRemove) { __RemoveElement(parent, newNode.__element_root!); } if (existingNode) { - __InsertElementBefore( - parent, - newNode.__element_root!, - existingNode.__element_root, - ); + if (__snapshot_def.isSlotV2 && newNode.__slotIndex! < existingNode.__slotIndex!) { + __AppendElement(parent, newNode.__element_root!); + } else { + __InsertElementBefore( + parent, + newNode.__element_root!, + existingNode.__element_root, + ); + } } else { __AppendElement(parent, newNode.__element_root!); } @@ -412,7 +426,7 @@ export class SnapshotInstance { unref(child, true); if (this.__elements) { - const [, elementIndex] = __snapshot_def.slot[0]!; + const [, elementIndex] = __snapshot_def.slot[typeof child.__slotIndex === 'number' ? child.__slotIndex : 0]!; __RemoveElement(this.__elements[elementIndex]!, child.__element_root!); } @@ -475,6 +489,7 @@ export class SnapshotInstance { values: this.__values, extraProps: this.__extraProps, children: this.__firstChild ? this.childNodes : undefined, + __slotIndex: this.__slotIndex, }; } diff --git a/packages/react/runtime/src/snapshot/types.ts b/packages/react/runtime/src/snapshot/types.ts index 4cae2d1dc8..8e5bfa4a10 100644 --- a/packages/react/runtime/src/snapshot/types.ts +++ b/packages/react/runtime/src/snapshot/types.ts @@ -22,4 +22,5 @@ export interface SerializedSnapshotInstance { values?: any[] | undefined; extraProps?: Record | undefined; children?: SerializedSnapshotInstance[] | undefined; + __slotIndex?: number | undefined; } diff --git a/packages/react/testing-library/src/__tests__/act.test.jsx b/packages/react/testing-library/src/__tests__/act.test.jsx index 48706037d6..23cf0f7f5a 100644 --- a/packages/react/testing-library/src/__tests__/act.test.jsx +++ b/packages/react/testing-library/src/__tests__/act.test.jsx @@ -174,6 +174,7 @@ test('fireEvent triggers useEffect calls', async () => { expect(snapshotInstanceManager.values).toMatchInlineSnapshot(` Map { -1 => { + "__slotIndex": undefined, "children": undefined, "extraProps": undefined, "id": -1, @@ -201,7 +202,7 @@ test('fireEvent triggers useEffect calls', async () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_268b9_test_4",2,4,2,[1],0,null,3,4,3,[0],1,2,3,null,1,-1,2,null],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_268b9_test_4",2,4,2,[1],0,null,3,4,3,[0],1,2,3,null,0,1,-1,2,null,0],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { @@ -221,10 +222,13 @@ test('fireEvent triggers useEffect calls', async () => { expect(snapshotInstanceManager.values).toMatchInlineSnapshot(` Map { -1 => { + "__slotIndex": undefined, "children": [ { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": 3, @@ -248,8 +252,10 @@ test('fireEvent triggers useEffect calls', async () => { "values": undefined, }, 2 => { + "__slotIndex": 0, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": 3, @@ -267,6 +273,7 @@ test('fireEvent triggers useEffect calls', async () => { ], }, 3 => { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": 3, @@ -285,7 +292,7 @@ test('fireEvent triggers useEffect calls', async () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_268b9_test_4",2,4,2,[1],0,null,3,4,3,[0],1,2,3,null,1,-1,2,null],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_268b9_test_4",2,4,2,[1],0,null,3,4,3,[0],1,2,3,null,0,1,-1,2,null,0],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { diff --git a/packages/react/testing-library/src/__tests__/alog.test.jsx b/packages/react/testing-library/src/__tests__/alog.test.jsx index e4d1acce1a..0e3d0bebf0 100644 --- a/packages/react/testing-library/src/__tests__/alog.test.jsx +++ b/packages/react/testing-library/src/__tests__/alog.test.jsx @@ -100,52 +100,40 @@ describe('alog', () => { "[ReactLynxDebug] FiberElement API call #16: __AddEvent(TEXT#2, "catchEvent", "focus", "-2:1:")", ], [ - "[ReactLynxDebug] FiberElement API call #17: __CreateWrapperElement(0) => WRAPPER#6", + "[ReactLynxDebug] FiberElement API call #17: __CreateRawText("") => #text#6", ], [ - "[ReactLynxDebug] FiberElement API call #18: __ReplaceElement(WRAPPER#6, WRAPPER#4)", + "[ReactLynxDebug] FiberElement API call #18: __SetAttribute(#text#6, "text", 0)", ], [ - "[ReactLynxDebug] FiberElement API call #19: __CreateRawText("") => #text#7", + "[ReactLynxDebug] FiberElement API call #19: __AppendElement(WRAPPER#4, #text#6)", ], [ - "[ReactLynxDebug] FiberElement API call #20: __SetAttribute(#text#7, "text", 0)", + "[ReactLynxDebug] FiberElement API call #20: __CreateView(0) => VIEW#7", ], [ - "[ReactLynxDebug] FiberElement API call #21: __AppendElement(WRAPPER#6, #text#7)", + "[ReactLynxDebug] FiberElement API call #21: __CreateRawText("Class Component") => #text#8", ], [ - "[ReactLynxDebug] FiberElement API call #22: __CreateWrapperElement(0) => WRAPPER#8", + "[ReactLynxDebug] FiberElement API call #22: __AppendElement(VIEW#7, #text#8)", ], [ - "[ReactLynxDebug] FiberElement API call #23: __ReplaceElement(WRAPPER#8, WRAPPER#5)", - ], - [ - "[ReactLynxDebug] FiberElement API call #24: __CreateView(0) => VIEW#9", - ], - [ - "[ReactLynxDebug] FiberElement API call #25: __CreateRawText("Class Component") => #text#10", - ], - [ - "[ReactLynxDebug] FiberElement API call #26: __AppendElement(VIEW#9, #text#10)", - ], - [ - "[ReactLynxDebug] FiberElement API call #27: __AppendElement(WRAPPER#8, VIEW#9)", + "[ReactLynxDebug] FiberElement API call #23: __AppendElement(WRAPPER#5, VIEW#7)", ], [ "[MainThread Component Render] name: ClassComponent", ], [ - "[ReactLynxDebug] FiberElement API call #28: __CreateView(0) => VIEW#11", + "[ReactLynxDebug] FiberElement API call #24: __CreateView(0) => VIEW#9", ], [ - "[ReactLynxDebug] FiberElement API call #29: __CreateRawText("Function Component") => #text#12", + "[ReactLynxDebug] FiberElement API call #25: __CreateRawText("Function Component") => #text#10", ], [ - "[ReactLynxDebug] FiberElement API call #30: __AppendElement(VIEW#11, #text#12)", + "[ReactLynxDebug] FiberElement API call #26: __AppendElement(VIEW#9, #text#10)", ], [ - "[ReactLynxDebug] FiberElement API call #31: __AppendElement(WRAPPER#8, VIEW#11)", + "[ReactLynxDebug] FiberElement API call #27: __AppendElement(WRAPPER#5, VIEW#9)", ], [ "[MainThread Component Render] name: FunctionComponent", @@ -154,13 +142,16 @@ describe('alog', () => { "[MainThread Component Render] name: App", ], [ - "[ReactLynxDebug] FiberElement API call #32: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_d6fb6_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-4,\\"type\\":null,\\"values\\":[0]}]},{\\"id\\":-5,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-6,\\"type\\":\\"__snapshot_d6fb6_test_2\\"},{\\"id\\":-7,\\"type\\":\\"__snapshot_d6fb6_test_3\\"}]}]}]}","jsReadyEventIdSwap":{}}])", + "[ReactLynxDebug] FiberElement API call #28: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_d6fb6_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":null,\\"values\\":[0],\\"__slotIndex\\":0},{\\"id\\":-4,\\"type\\":\\"__snapshot_d6fb6_test_2\\",\\"__slotIndex\\":1},{\\"id\\":-5,\\"type\\":\\"__snapshot_d6fb6_test_3\\",\\"__slotIndex\\":1}],\\"__slotIndex\\":0}]}","jsReadyEventIdSwap":{}}])", + ], + [ + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: 4", ], [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: 6", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: 5", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: 7", + "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_d6fb6_test_2, __id: 4", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: 2", @@ -185,32 +176,24 @@ describe('alog', () => { "children": [ { "id": -3, - "type": "wrapper", - "children": [ - { - "id": -4, - "type": null, - "values": [ - 0 - ] - } - ] + "type": null, + "values": [ + 0 + ], + "__slotIndex": 0 + }, + { + "id": -4, + "type": "__snapshot_d6fb6_test_2", + "__slotIndex": 1 }, { "id": -5, - "type": "wrapper", - "children": [ - { - "id": -6, - "type": "__snapshot_d6fb6_test_2" - }, - { - "id": -7, - "type": "__snapshot_d6fb6_test_3" - } - ] + "type": "__snapshot_d6fb6_test_3", + "__slotIndex": 1 } - ] + ], + "__slotIndex": 0 } ] }, @@ -221,31 +204,25 @@ describe('alog', () => { "[ReactLynxDebug] SnapshotInstance tree for first screen hydration: | -1(root): undefined | -2(__snapshot_d6fb6_test_1): ["-2:0:","-2:1:"] - | -3(wrapper): undefined - | -4(null): [0] - | -5(wrapper): undefined - | -6(__snapshot_d6fb6_test_2): undefined - | -7(__snapshot_d6fb6_test_3): undefined", + | -3(null): [0] + | -4(__snapshot_d6fb6_test_2): undefined + | -5(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BackgroundSnapshotInstance tree before hydration: | 1(root): undefined | 2(__snapshot_d6fb6_test_1): [null,null] - | 3(wrapper): undefined - | 4(null): [0] - | 5(wrapper): undefined - | 6(__snapshot_d6fb6_test_2): undefined - | 7(__snapshot_d6fb6_test_3): undefined", + | 3(null): [0] + | 4(__snapshot_d6fb6_test_2): undefined + | 5(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BackgroundSnapshotInstance after hydration: | -1(root): undefined | -2(__snapshot_d6fb6_test_1): [null,null] - | -3(wrapper): undefined - | -4(null): [0] - | -5(wrapper): undefined - | -6(__snapshot_d6fb6_test_2): undefined - | -7(__snapshot_d6fb6_test_3): undefined", + | -3(null): [0] + | -4(__snapshot_d6fb6_test_2): undefined + | -5(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BTS -> MTS updateMainThread: @@ -272,7 +249,7 @@ describe('alog', () => { }", ], [ - "[ReactLynxDebug] FiberElement API call #33: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", + "[ReactLynxDebug] FiberElement API call #29: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], [ "[ReactLynxDebug] BTS received event: @@ -293,10 +270,13 @@ describe('alog', () => { }", ], [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -4", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -5", + ], + [ + "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_d6fb6_test_2, __id: -4", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -311,7 +291,7 @@ describe('alog', () => { "snapshotPatch": [ { "op": "SetAttribute", - "id": -4, + "id": -3, "dynamicPartIndex": 0, "value": 1 } @@ -332,10 +312,10 @@ describe('alog', () => { }", ], [ - "[ReactLynxDebug] FiberElement API call #34: __SetAttribute(#text#7, "text", 1)", + "[ReactLynxDebug] FiberElement API call #30: __SetAttribute(#text#6, "text", 1)", ], [ - "[ReactLynxDebug] FiberElement API call #35: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", + "[ReactLynxDebug] FiberElement API call #31: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], ] `); @@ -390,52 +370,40 @@ describe('alog', () => { "[ReactLynxDebug] FiberElement API call #16: __AddEvent(TEXT#2, "catchEvent", "focus", "-2:1:")", ], [ - "[ReactLynxDebug] FiberElement API call #17: __CreateWrapperElement(0) => WRAPPER#6", - ], - [ - "[ReactLynxDebug] FiberElement API call #18: __ReplaceElement(WRAPPER#6, WRAPPER#4)", + "[ReactLynxDebug] FiberElement API call #17: __CreateRawText("") => #text#6", ], [ - "[ReactLynxDebug] FiberElement API call #19: __CreateRawText("") => #text#7", + "[ReactLynxDebug] FiberElement API call #18: __SetAttribute(#text#6, "text", 0)", ], [ - "[ReactLynxDebug] FiberElement API call #20: __SetAttribute(#text#7, "text", 0)", + "[ReactLynxDebug] FiberElement API call #19: __AppendElement(WRAPPER#4, #text#6)", ], [ - "[ReactLynxDebug] FiberElement API call #21: __AppendElement(WRAPPER#6, #text#7)", + "[ReactLynxDebug] FiberElement API call #20: __CreateView(0) => VIEW#7", ], [ - "[ReactLynxDebug] FiberElement API call #22: __CreateWrapperElement(0) => WRAPPER#8", + "[ReactLynxDebug] FiberElement API call #21: __CreateRawText("Class Component") => #text#8", ], [ - "[ReactLynxDebug] FiberElement API call #23: __ReplaceElement(WRAPPER#8, WRAPPER#5)", - ], - [ - "[ReactLynxDebug] FiberElement API call #24: __CreateView(0) => VIEW#9", - ], - [ - "[ReactLynxDebug] FiberElement API call #25: __CreateRawText("Class Component") => #text#10", - ], - [ - "[ReactLynxDebug] FiberElement API call #26: __AppendElement(VIEW#9, #text#10)", + "[ReactLynxDebug] FiberElement API call #22: __AppendElement(VIEW#7, #text#8)", ], [ - "[ReactLynxDebug] FiberElement API call #27: __AppendElement(WRAPPER#8, VIEW#9)", + "[ReactLynxDebug] FiberElement API call #23: __AppendElement(WRAPPER#5, VIEW#7)", ], [ "[MainThread Component Render] name: ClassComponent", ], [ - "[ReactLynxDebug] FiberElement API call #28: __CreateView(0) => VIEW#11", + "[ReactLynxDebug] FiberElement API call #24: __CreateView(0) => VIEW#9", ], [ - "[ReactLynxDebug] FiberElement API call #29: __CreateRawText("Function Component") => #text#12", + "[ReactLynxDebug] FiberElement API call #25: __CreateRawText("Function Component") => #text#10", ], [ - "[ReactLynxDebug] FiberElement API call #30: __AppendElement(VIEW#11, #text#12)", + "[ReactLynxDebug] FiberElement API call #26: __AppendElement(VIEW#9, #text#10)", ], [ - "[ReactLynxDebug] FiberElement API call #31: __AppendElement(WRAPPER#8, VIEW#11)", + "[ReactLynxDebug] FiberElement API call #27: __AppendElement(WRAPPER#5, VIEW#9)", ], [ "[MainThread Component Render] name: FunctionComponent", @@ -444,13 +412,16 @@ describe('alog', () => { "[MainThread Component Render] name: App", ], [ - "[ReactLynxDebug] FiberElement API call #32: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_d6fb6_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-4,\\"type\\":null,\\"values\\":[0]}]},{\\"id\\":-5,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-6,\\"type\\":\\"__snapshot_d6fb6_test_2\\"},{\\"id\\":-7,\\"type\\":\\"__snapshot_d6fb6_test_3\\"}]}]}]}","jsReadyEventIdSwap":{}}])", + "[ReactLynxDebug] FiberElement API call #28: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_d6fb6_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":null,\\"values\\":[0],\\"__slotIndex\\":0},{\\"id\\":-4,\\"type\\":\\"__snapshot_d6fb6_test_2\\",\\"__slotIndex\\":1},{\\"id\\":-5,\\"type\\":\\"__snapshot_d6fb6_test_3\\",\\"__slotIndex\\":1}],\\"__slotIndex\\":0}]}","jsReadyEventIdSwap":{}}])", ], [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: 6", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: 4", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: 7", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: 5", + ], + [ + "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_d6fb6_test_2, __id: 4", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: 2", @@ -475,32 +446,24 @@ describe('alog', () => { "children": [ { "id": -3, - "type": "wrapper", - "children": [ - { - "id": -4, - "type": null, - "values": [ - 0 - ] - } - ] + "type": null, + "values": [ + 0 + ], + "__slotIndex": 0 + }, + { + "id": -4, + "type": "__snapshot_d6fb6_test_2", + "__slotIndex": 1 }, { "id": -5, - "type": "wrapper", - "children": [ - { - "id": -6, - "type": "__snapshot_d6fb6_test_2" - }, - { - "id": -7, - "type": "__snapshot_d6fb6_test_3" - } - ] + "type": "__snapshot_d6fb6_test_3", + "__slotIndex": 1 } - ] + ], + "__slotIndex": 0 } ] }, @@ -511,31 +474,25 @@ describe('alog', () => { "[ReactLynxDebug] SnapshotInstance tree for first screen hydration: | -1(root): undefined | -2(__snapshot_d6fb6_test_1): ["-2:0:","-2:1:"] - | -3(wrapper): undefined - | -4(null): [0] - | -5(wrapper): undefined - | -6(__snapshot_d6fb6_test_2): undefined - | -7(__snapshot_d6fb6_test_3): undefined", + | -3(null): [0] + | -4(__snapshot_d6fb6_test_2): undefined + | -5(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BackgroundSnapshotInstance tree before hydration: | 1(root): undefined | 2(__snapshot_d6fb6_test_1): [null,null] - | 3(wrapper): undefined - | 4(null): [0] - | 5(wrapper): undefined - | 6(__snapshot_d6fb6_test_2): undefined - | 7(__snapshot_d6fb6_test_3): undefined", + | 3(null): [0] + | 4(__snapshot_d6fb6_test_2): undefined + | 5(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BackgroundSnapshotInstance after hydration: | -1(root): undefined | -2(__snapshot_d6fb6_test_1): [null,null] - | -3(wrapper): undefined - | -4(null): [0] - | -5(wrapper): undefined - | -6(__snapshot_d6fb6_test_2): undefined - | -7(__snapshot_d6fb6_test_3): undefined", + | -3(null): [0] + | -4(__snapshot_d6fb6_test_2): undefined + | -5(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BTS -> MTS updateMainThread: @@ -562,7 +519,7 @@ describe('alog', () => { }", ], [ - "[ReactLynxDebug] FiberElement API call #33: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", + "[ReactLynxDebug] FiberElement API call #29: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], [ "[ReactLynxDebug] BTS received event: @@ -583,10 +540,13 @@ describe('alog', () => { }", ], [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -4", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -5", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", + "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_d6fb6_test_2, __id: -4", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -601,7 +561,7 @@ describe('alog', () => { "snapshotPatch": [ { "op": "SetAttribute", - "id": -4, + "id": -3, "dynamicPartIndex": 0, "value": 1 } @@ -622,10 +582,10 @@ describe('alog', () => { }", ], [ - "[ReactLynxDebug] FiberElement API call #34: __SetAttribute(#text#7, "text", 1)", + "[ReactLynxDebug] FiberElement API call #30: __SetAttribute(#text#6, "text", 1)", ], [ - "[ReactLynxDebug] FiberElement API call #35: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", + "[ReactLynxDebug] FiberElement API call #31: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], ] `); @@ -640,10 +600,13 @@ describe('alog', () => { expect(lynxTestingEnv.mainThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -4", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -5", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", + "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_d6fb6_test_2, __id: -4", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -658,7 +621,7 @@ describe('alog', () => { "snapshotPatch": [ { "op": "SetAttribute", - "id": -4, + "id": -3, "dynamicPartIndex": 0, "value": 0 } @@ -679,20 +642,23 @@ describe('alog', () => { }", ], [ - "[ReactLynxDebug] FiberElement API call #36: __SetAttribute(#text#7, "text", 0)", + "[ReactLynxDebug] FiberElement API call #32: __SetAttribute(#text#6, "text", 0)", ], [ - "[ReactLynxDebug] FiberElement API call #37: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", + "[ReactLynxDebug] FiberElement API call #33: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], ] `); expect(lynxTestingEnv.backgroundThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -4", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -5", + ], + [ + "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_d6fb6_test_2, __id: -4", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -707,7 +673,7 @@ describe('alog', () => { "snapshotPatch": [ { "op": "SetAttribute", - "id": -4, + "id": -3, "dynamicPartIndex": 0, "value": 0 } @@ -728,10 +694,10 @@ describe('alog', () => { }", ], [ - "[ReactLynxDebug] FiberElement API call #36: __SetAttribute(#text#7, "text", 0)", + "[ReactLynxDebug] FiberElement API call #32: __SetAttribute(#text#6, "text", 0)", ], [ - "[ReactLynxDebug] FiberElement API call #37: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", + "[ReactLynxDebug] FiberElement API call #33: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], ] `); @@ -746,10 +712,13 @@ describe('alog', () => { expect(lynxTestingEnv.mainThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -4", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -5", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", + "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_d6fb6_test_2, __id: -4", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -764,7 +733,7 @@ describe('alog', () => { "snapshotPatch": [ { "op": "SetAttribute", - "id": -4, + "id": -3, "dynamicPartIndex": 0, "value": 1 } @@ -785,20 +754,23 @@ describe('alog', () => { }", ], [ - "[ReactLynxDebug] FiberElement API call #38: __SetAttribute(#text#7, "text", 1)", + "[ReactLynxDebug] FiberElement API call #34: __SetAttribute(#text#6, "text", 1)", ], [ - "[ReactLynxDebug] FiberElement API call #39: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", + "[ReactLynxDebug] FiberElement API call #35: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], ] `); expect(lynxTestingEnv.backgroundThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -4", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -5", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", + "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_d6fb6_test_2, __id: -4", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -813,7 +785,7 @@ describe('alog', () => { "snapshotPatch": [ { "op": "SetAttribute", - "id": -4, + "id": -3, "dynamicPartIndex": 0, "value": 1 } @@ -834,10 +806,10 @@ describe('alog', () => { }", ], [ - "[ReactLynxDebug] FiberElement API call #38: __SetAttribute(#text#7, "text", 1)", + "[ReactLynxDebug] FiberElement API call #34: __SetAttribute(#text#6, "text", 1)", ], [ - "[ReactLynxDebug] FiberElement API call #39: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", + "[ReactLynxDebug] FiberElement API call #35: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], ] `); diff --git a/packages/react/testing-library/src/__tests__/end-to-end.test.jsx b/packages/react/testing-library/src/__tests__/end-to-end.test.jsx index dd263864a1..194690c048 100644 --- a/packages/react/testing-library/src/__tests__/end-to-end.test.jsx +++ b/packages/react/testing-library/src/__tests__/end-to-end.test.jsx @@ -42,6 +42,7 @@ test('state change will cause re-render', async () => { expect(snapshotInstanceManager.values).toMatchInlineSnapshot(` Map { -1 => { + "__slotIndex": undefined, "children": undefined, "extraProps": undefined, "id": -1, @@ -62,8 +63,10 @@ test('state change will cause re-render', async () => { expect(snapshotInstanceManager.values).toMatchInlineSnapshot(` Map { -1 => { + "__slotIndex": undefined, "children": [ { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": 2, @@ -77,6 +80,7 @@ test('state change will cause re-render', async () => { "values": undefined, }, 2 => { + "__slotIndex": 0, "children": undefined, "extraProps": undefined, "id": 2, @@ -100,7 +104,7 @@ test('state change will cause re-render', async () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_f46c5_test_1",2,1,-1,2,null],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_f46c5_test_1",2,1,-1,2,null,0],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { @@ -118,7 +122,7 @@ test('state change will cause re-render', async () => { [ "rLynxChange", { - "data": "{"patchList":[{"id":3,"snapshotPatch":[2,-1,2,0,"__snapshot_f46c5_test_2",3,0,null,4,3,4,0,"Hello World",1,3,4,null,1,-1,3,null]}]}", + "data": "{"patchList":[{"id":3,"snapshotPatch":[2,-1,2,0,"__snapshot_f46c5_test_2",3,0,null,4,3,4,0,"Hello World",1,3,4,null,0,1,-1,3,null,0]}]}", "patchOptions": { "pipelineOptions": { "dsl": "reactLynx", @@ -159,6 +163,7 @@ test('it waits for the data to be loaded', async () => { expect(snapshotInstanceManager.values).toMatchInlineSnapshot(` Map { -1 => { + "__slotIndex": undefined, "children": undefined, "extraProps": undefined, "id": -1, diff --git a/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx b/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx index 3e7e8159a4..26f0323215 100644 --- a/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx +++ b/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx @@ -80,6 +80,80 @@ describe('lazy bundle', () => { `); } }); + + it('should render multi-slots lazy component', async () => { + const InternalComponent = lazy(() => import('./LazyComponent')); + + const App = () => { + return ( + + loading 1...}> + + + --- + loading 2...}> + + + + ); + }; + + const { container } = render( + , + ); + + expect(container).toMatchInlineSnapshot(` + + + + + + loading 1... + + + + + --- + + + + + loading 2... + + + + + + `); + + await waitForElementToBeRemoved(() => screen.getByText('loading 1...'), { + timeout: 50_000, + }); + + expect(container).toMatchInlineSnapshot(` + + + + + + Hello from LazyComponent + + + + + --- + + + + + Hello from LazyComponent + + + + + + `); + }); }); describe('Suspense', () => { @@ -139,34 +213,34 @@ describe('Suspense', () => { if (name === 'PreactSuspense') { expect(container).toMatchInlineSnapshot(` - - - + - loading... - - - - `); - } else { - expect(container).toMatchInlineSnapshot(` - - - loading... - - - - `); + + + `); + } else { + expect(container).toMatchInlineSnapshot(` + + + + + loading... + + + + + `); } { @@ -178,24 +252,26 @@ describe('Suspense', () => { { "id": 2, "op": "CreateElement", - "type": "__snapshot_50869_test_3", + "type": "__snapshot_50869_test_6", }, { - "id": 7, + "id": 5, "op": "CreateElement", - "type": "__snapshot_50869_test_4", + "type": "__snapshot_50869_test_7", }, { "beforeId": null, - "childId": 7, + "childId": 5, "op": "InsertBefore", "parentId": 2, + "slotIndex": 0, }, { "beforeId": null, "childId": 2, "op": "InsertBefore", "parentId": -1, + "slotIndex": 0, }, ] `); @@ -205,35 +281,38 @@ describe('Suspense', () => { { "id": 2, "op": "CreateElement", - "type": "__snapshot_50869_test_3", + "type": "__snapshot_50869_test_6", }, { - "id": 8, + "id": 6, "op": "CreateElement", "type": "wrapper", }, { - "id": 9, + "id": 7, "op": "CreateElement", - "type": "__snapshot_50869_test_4", + "type": "__snapshot_50869_test_7", }, { "beforeId": null, - "childId": 9, + "childId": 7, "op": "InsertBefore", - "parentId": 8, + "parentId": 6, + "slotIndex": 0, }, { "beforeId": null, - "childId": 8, + "childId": 6, "op": "InsertBefore", "parentId": 2, + "slotIndex": 0, }, { "beforeId": null, "childId": 2, "op": "InsertBefore", "parentId": -1, + "slotIndex": 0, }, ] `); @@ -331,10 +410,10 @@ describe('Suspense', () => { el4 ]; }", - "type": "__snapshot_50869_test_5", + "type": "__snapshot_50869_test_8", }, { - "__id": 7, + "__id": 5, "create": "function() { const pageId = __vite_ssr_import_1__.__pageId; const el = __CreateText(pageId); @@ -346,7 +425,7 @@ describe('Suspense', () => { el1 ]; }", - "type": "__snapshot_50869_test_4", + "type": "__snapshot_50869_test_7", }, ] `); @@ -354,19 +433,19 @@ describe('Suspense', () => { } else { if (!process.env.RSTEST) { expect(tearDownInstances).toMatchInlineSnapshot(` - [ - { - "__id": 8, - "create": "create () { - /* v8 ignore start */ if (__JS__ && !__DEV__) return []; - /* v8 ignore stop */ return [ - __CreateWrapperElement(__pageId) - ]; - }", - "type": "wrapper", - }, - ] - `); + [ + { + "__id": 6, + "create": "create () { + /* v8 ignore start */ if (__JS__ && !__DEV__) return []; + /* v8 ignore stop */ return [ + __CreateWrapperElement(__pageId) + ]; + }", + "type": "wrapper", + }, + ] + `); } } diff --git a/packages/react/testing-library/src/__tests__/list.test.jsx b/packages/react/testing-library/src/__tests__/list.test.jsx index f8b3bfe6cf..27855a6d81 100644 --- a/packages/react/testing-library/src/__tests__/list.test.jsx +++ b/packages/react/testing-library/src/__tests__/list.test.jsx @@ -47,11 +47,9 @@ describe('list', () => { - - - 0 - - + + 0 + @@ -66,26 +64,22 @@ describe('list', () => { - - - 0 - - + + 0 + - - - 1 - - + + 1 + `); expect(uid0).toMatchInlineSnapshot(`2`); - expect(uid1).toMatchInlineSnapshot(`6`); + expect(uid1).toMatchInlineSnapshot(`5`); elementTree.leaveListItem(list, uid0); expect(__pendingListUpdates.values).toMatchInlineSnapshot(`{}`); expect(container).toMatchInlineSnapshot(` @@ -96,20 +90,16 @@ describe('list', () => { - - - 0 - - + + 0 + - - - 1 - - + + 1 + @@ -126,20 +116,16 @@ describe('list', () => { - - - 2 - - + + 2 + - - - 1 - - + + 1 + @@ -203,7 +189,7 @@ describe('list', () => { > @@ -221,21 +207,19 @@ describe('list', () => { full-span="true" item-key="3" > - - - - 3 - - - 3 - - - - - hello - - - + + + 3 + + + 3 + + + + + hello + + `); @@ -278,21 +262,19 @@ describe('list', () => { full-span="true" item-key="1" > - - - - 1 - - - 1 - - - - - hello - - - + + + 1 + + + 1 + + + + + hello + + `); @@ -303,21 +285,19 @@ describe('list', () => { full-span="true" item-key="1" > - - - - 1 - - - 1 - - - - - hello - - - + + + 1 + + + 1 + + + + + hello + + , "item-key", "1", @@ -327,21 +307,19 @@ describe('list', () => { full-span="true" item-key="1" > - - - - 1 - - - 1 - - - - - hello - - - + + + 1 + + + 1 + + + + + hello + + , "full-span", true, @@ -365,21 +343,19 @@ describe('list', () => { full-span="true" item-key="1" > - - - - 1 - - - 1 - - - - - hello - - - + + + 1 + + + 1 + + + + + hello + + `); expect(flushInfo).toMatchObject({ @@ -392,87 +368,79 @@ describe('list', () => { expect(list).toMatchInlineSnapshot(` - - - - 4 - - - 4 - - - - - hello - - - + + + 4 + + + 4 + + + + + hello + + - - - - 5 - - - 5 - - - - - hello - - - + + + 5 + + + 5 + + + + + hello + + - - - - 2 - - - 2 - - - - - hello - - - + + + 2 + + + 2 + + + + + hello + + - - - - 1 - - - 1 - - - - - hello - - - + + + 1 + + + 1 + + + + + hello + + `); @@ -480,87 +448,79 @@ describe('list', () => { expect(list).toMatchInlineSnapshot(` - - - - 4 - - - 4 - - - - - hello - - - + + + 4 + + + 4 + + + + + hello + + - - - - 5 - - - 5 - - - - - hello - - - + + + 5 + + + 5 + + + + + hello + + - - - - 2 - - - 2 - - - - - hello - - - + + + 2 + + + 2 + + + + + hello + + - - - - 1 - - - 1 - - - - - hello - - - + + + 1 + + + 1 + + + + + hello + + `); @@ -646,7 +606,7 @@ describe('list - deferred should render as normal', () => { `); @@ -670,7 +630,7 @@ describe('list - deferred should render as normal', () => { `); @@ -682,7 +642,7 @@ describe('list - deferred should render as normal', () => { should render as normal', () => { `); @@ -851,7 +811,7 @@ describe('list - deferred should render as normal', () => { `); @@ -871,7 +831,7 @@ describe('list - deferred should render as normal', () => { `); @@ -890,7 +850,7 @@ describe('list - deferred should render as normal', () => { should render as normal', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_d0c07_test_30","children":[{"id":-3,"type":"__snapshot_d0c07_test_31","values":[{"item-key":0}],"children":[{"id":-4,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-5,"type":"__snapshot_d0c07_test_32","children":[{"id":-6,"type":null,"values":[0]}]}]}]},{"id":-7,"type":"__snapshot_d0c07_test_31","values":[{"item-key":1}],"children":[{"id":-8,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-9,"type":"__snapshot_d0c07_test_32","children":[{"id":-10,"type":null,"values":[1]}]}]}]},{"id":-11,"type":"__snapshot_d0c07_test_31","values":[{"item-key":2}],"children":[{"id":-12,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-13,"type":"__snapshot_d0c07_test_32","children":[{"id":-14,"type":null,"values":[2]}]}]}]}]}]}", + "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_d0c07_test_26","children":[{"id":-3,"type":"__snapshot_d0c07_test_27","values":[{"item-key":0}],"children":[{"id":-4,"type":"__snapshot_d0c07_test_25","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-5,"type":"__snapshot_d0c07_test_28","children":[{"id":-6,"type":null,"values":[0],"__slotIndex":0}],"__slotIndex":0}],"__slotIndex":0}],"__slotIndex":0},{"id":-7,"type":"__snapshot_d0c07_test_27","values":[{"item-key":1}],"children":[{"id":-8,"type":"__snapshot_d0c07_test_25","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-9,"type":"__snapshot_d0c07_test_28","children":[{"id":-10,"type":null,"values":[1],"__slotIndex":0}],"__slotIndex":0}],"__slotIndex":0}],"__slotIndex":0},{"id":-11,"type":"__snapshot_d0c07_test_27","values":[{"item-key":2}],"children":[{"id":-12,"type":"__snapshot_d0c07_test_25","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-13,"type":"__snapshot_d0c07_test_28","children":[{"id":-14,"type":null,"values":[2],"__slotIndex":0}],"__slotIndex":0}],"__slotIndex":0}],"__slotIndex":0}],"__slotIndex":0}]}", }, ], ], diff --git a/packages/react/testing-library/src/__tests__/lynx.test.jsx b/packages/react/testing-library/src/__tests__/lynx.test.jsx index 392a35bb88..1495362095 100644 --- a/packages/react/testing-library/src/__tests__/lynx.test.jsx +++ b/packages/react/testing-library/src/__tests__/lynx.test.jsx @@ -41,6 +41,7 @@ describe('lynx global API', () => { 1e10, 1e10, null, + 0, ); args[1].data = JSON.stringify(data); } @@ -69,12 +70,14 @@ describe('lynx global API', () => { "childId": 2, "op": "InsertBefore", "parentId": -1, + "slotIndex": 0, }, { "beforeId": null, "childId": 10000000000, "op": "InsertBefore", "parentId": 10000000000, + "slotIndex": 0, }, ] `); diff --git a/packages/react/testing-library/src/__tests__/render.test.jsx b/packages/react/testing-library/src/__tests__/render.test.jsx index 94acdb4552..8c0fd92c11 100644 --- a/packages/react/testing-library/src/__tests__/render.test.jsx +++ b/packages/react/testing-library/src/__tests__/render.test.jsx @@ -69,10 +69,10 @@ describe('dynamic key in snapshot', () => { expect(container).toMatchInlineSnapshot(` - - + + foo @@ -83,8 +83,8 @@ describe('dynamic key in snapshot', () => { bar - - + + `); @@ -114,10 +114,10 @@ describe('dynamic key in snapshot', () => { Hello - - + + foo @@ -128,8 +128,8 @@ describe('dynamic key in snapshot', () => { bar - - + + `); @@ -155,10 +155,10 @@ describe('dynamic key in snapshot', () => { expect(container).toMatchInlineSnapshot(` - - + + foo @@ -169,8 +169,8 @@ describe('dynamic key in snapshot', () => { bar - - + + Hello @@ -201,20 +201,18 @@ describe('dynamic key in snapshot', () => { - + - - - foo - - - - - bar - - + + foo + - + + + bar + + + `); @@ -239,22 +237,24 @@ describe('dynamic key in snapshot', () => { expect(container).toMatchInlineSnapshot(` - - + + foo - - - bar - - - - + + + + bar + + + + + `); diff --git a/packages/react/testing-library/src/__tests__/renderComponent.test.jsx b/packages/react/testing-library/src/__tests__/renderComponent.test.jsx new file mode 100644 index 0000000000..bbd16c3ecc --- /dev/null +++ b/packages/react/testing-library/src/__tests__/renderComponent.test.jsx @@ -0,0 +1,347 @@ +import { expect } from 'vitest'; +import { Component, useState } from '@lynx-js/react'; + +import { render, act } from '..'; +import { prettyFormatSnapshotPatch } from '../../../runtime/lib/debug/formatPatch'; + +test('setState generates insertBefore operation', async () => { + vi.spyOn(lynxTestingEnv.backgroundThread.lynxCoreInject.tt, 'OnLifecycleEvent'); + const onLifecycleEventCalls = lynxTestingEnv.backgroundThread.lynxCoreInject.tt.OnLifecycleEvent.mock.calls; + vi.spyOn(lynx.getNativeApp(), 'callLepusMethod'); + const callLepusMethodCalls = lynx.getNativeApp().callLepusMethod.mock.calls; + + let setList; + const App = () => { + const [list, _setList] = useState([1, 2, 3, 4]); + setList = _setList; + return ( + + + {list.map(key => ( + + {key} + + ))} + + ); + }; + + render(, { + enableMainThread: true, + enableBackgroundThread: true, + }); + + expect(callLepusMethodCalls[0]).toMatchInlineSnapshot(` + [ + "rLynxChange", + { + "data": "{"patchList":[{"snapshotPatch":[],"id":2}]}", + "patchOptions": { + "isHydration": true, + "pipelineOptions": { + "dsl": "reactLynx", + "needTimestamps": true, + "pipelineID": "pipelineID", + "pipelineOrigin": "reactLynxHydrate", + "stage": "hydrate", + }, + "reloadVersion": 0, + }, + }, + [Function], + ] + `); + { + const snapshotPatch = JSON.parse(callLepusMethodCalls[0][1]['data']).patchList[0].snapshotPatch; + const formattedSnapshotPatch = prettyFormatSnapshotPatch(snapshotPatch); + expect(formattedSnapshotPatch).toMatchInlineSnapshot(`[]`); + } + + expect(elementTree).toMatchInlineSnapshot(` + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + + `); + + act(() => { + setList([1, 3, 2, 4]); + }); + + expect(callLepusMethodCalls[1]).toMatchInlineSnapshot(` + [ + "rLynxChange", + { + "data": "{"patchList":[{"id":3,"snapshotPatch":[1,-2,-5,-9,0]}]}", + "patchOptions": { + "pipelineOptions": { + "dsl": "reactLynx", + "needTimestamps": true, + "pipelineID": "pipelineID", + "pipelineOrigin": "reactLynxHydrate", + "stage": "hydrate", + }, + "reloadVersion": 0, + }, + }, + [Function], + ] + `); + const snapshotPatch = JSON.parse(callLepusMethodCalls[1][1]['data']).patchList[0].snapshotPatch; + const formattedSnapshotPatch = prettyFormatSnapshotPatch(snapshotPatch); + expect(formattedSnapshotPatch).toMatchInlineSnapshot(` + [ + { + "beforeId": -9, + "childId": -5, + "op": "InsertBefore", + "parentId": -2, + "slotIndex": 0, + }, + ] + `); + + expect(elementTree).toMatchInlineSnapshot(` + + + + + + + 1 + + + + + 3 + + + + + 2 + + + + + 4 + + + + + + `); +}); + +test('setState triggered renderComponent should have correct slotIndex', async () => { + vi.spyOn(lynxTestingEnv.backgroundThread.lynxCoreInject.tt, 'OnLifecycleEvent'); + const onLifecycleEventCalls = lynxTestingEnv.backgroundThread.lynxCoreInject.tt.OnLifecycleEvent.mock.calls; + vi.spyOn(lynx.getNativeApp(), 'callLepusMethod'); + const callLepusMethodCalls = lynx.getNativeApp().callLepusMethod.mock.calls; + + const Parent = ({ children }) => { + const text = 'parent'; + return ( + + {text} + Split + {children} + + ); + }; + let setCount; + const Child = () => { + const [count, _setCount] = useState(0); + setCount = _setCount; + return ( + count === 0 + ? ( + + {count} + + ) + : ( + + {count} + + ) + ); + }; + const App = () => { + return ( + + + + ); + }; + + render(, { + enableMainThread: true, + enableBackgroundThread: true, + }); + + expect(JSON.stringify(JSON.parse(onLifecycleEventCalls[0][0][1]['root']), null, 2)).toMatchInlineSnapshot(` + "{ + "id": -1, + "type": "root", + "children": [ + { + "id": -2, + "type": "__snapshot_289e0_test_3", + "children": [ + { + "id": -3, + "type": null, + "values": [ + "parent" + ], + "__slotIndex": 0 + }, + { + "id": -4, + "type": "__snapshot_289e0_test_4", + "children": [ + { + "id": -5, + "type": null, + "values": [ + 0 + ], + "__slotIndex": 0 + } + ], + "__slotIndex": 1 + } + ], + "__slotIndex": 0 + } + ] + }" + `); + + expect(elementTree).toMatchInlineSnapshot(` + + + + parent + + + Split + + + + + 0 + + + + + + `); + + act(() => { + setCount(1); + }); + + expect(callLepusMethodCalls[1]).toMatchInlineSnapshot(` + [ + "rLynxChange", + { + "data": "{"patchList":[{"id":3,"snapshotPatch":[2,-2,-4,0,"__snapshot_289e0_test_5",6,0,null,7,3,7,0,1,1,6,7,null,0,1,-2,6,null,1]}]}", + "patchOptions": { + "pipelineOptions": { + "dsl": "reactLynx", + "needTimestamps": true, + "pipelineID": "pipelineID", + "pipelineOrigin": "reactLynxHydrate", + "stage": "hydrate", + }, + "reloadVersion": 0, + }, + }, + [Function], + ] + `); + const snapshotPatch = JSON.parse(callLepusMethodCalls[1][1]['data']).patchList[0].snapshotPatch; + const formattedSnapshotPatch = prettyFormatSnapshotPatch(snapshotPatch); + expect(formattedSnapshotPatch).toMatchInlineSnapshot(` + [ + { + "childId": -4, + "op": "RemoveChild", + "parentId": -2, + }, + { + "id": 6, + "op": "CreateElement", + "type": "__snapshot_289e0_test_5", + }, + { + "id": 7, + "op": "CreateElement", + "type": null, + }, + { + "dynamicPartIndex": 0, + "id": 7, + "op": "SetAttribute", + "value": 1, + }, + { + "beforeId": null, + "childId": 7, + "op": "InsertBefore", + "parentId": 6, + "slotIndex": 0, + }, + { + "beforeId": null, + "childId": 6, + "op": "InsertBefore", + "parentId": -2, + "slotIndex": 1, + }, + ] + `); + expect(elementTree).toMatchInlineSnapshot(` + + + + parent + + + Split + + + + + 1 + + + + + + `); +}); diff --git a/packages/react/testing-library/src/__tests__/setState-jsx.test.jsx b/packages/react/testing-library/src/__tests__/setState-jsx.test.jsx new file mode 100644 index 0000000000..d41ef78b82 --- /dev/null +++ b/packages/react/testing-library/src/__tests__/setState-jsx.test.jsx @@ -0,0 +1,276 @@ +import { expect } from 'vitest'; +import { Component, useState } from '@lynx-js/react'; + +import { fireEvent, render, act } from '..'; +import { prettyFormatSnapshotPatch } from '../../../runtime/lib/debug/formatPatch'; + +test('setState changes jsx', async () => { + vi.spyOn(lynxTestingEnv.backgroundThread.lynxCoreInject.tt, 'OnLifecycleEvent'); + const onLifecycleEventCalls = lynxTestingEnv.backgroundThread.lynxCoreInject.tt.OnLifecycleEvent.mock.calls; + vi.spyOn(lynx.getNativeApp(), 'callLepusMethod'); + const callLepusMethodCalls = lynx.getNativeApp().callLepusMethod.mock.calls; + + const jsx0 = Hello 0; + const jsx1 = Hello 1; + const jsx2 = Hello 2; + + const Comp = () => { + const [text0, setText0] = useState(jsx0); + const [text1, setText1] = useState(jsx1); + const handleTap = () => { + setText0(jsx1); + setText1(jsx0); + }; + return ( + + {text0} + --- + {[0, 1, 2].map((i) => text1)} + --- + {jsx2} + + ); + }; + + const { container, findByTestId } = render(); + + expect(container).toMatchInlineSnapshot(` + + + + + Hello 0 + + + + --- + + + + Hello 1 + + + Hello 1 + + + Hello 1 + + + + --- + + + + Hello 2 + + + + + `); + + const view = await findByTestId('view'); + fireEvent.tap(view); + + { + const snapshotPatch = JSON.parse(callLepusMethodCalls[0][1]['data']).patchList[0].snapshotPatch; + const formattedSnapshotPatch = prettyFormatSnapshotPatch(snapshotPatch); + expect(formattedSnapshotPatch).toMatchInlineSnapshot(` + [ + { + "id": 2, + "op": "CreateElement", + "type": "__snapshot_c1928_test_4", + }, + { + "id": 2, + "op": "SetAttributes", + "values": [ + 1, + ], + }, + { + "id": 3, + "op": "CreateElement", + "type": "__snapshot_c1928_test_1", + }, + { + "beforeId": null, + "childId": 3, + "op": "InsertBefore", + "parentId": 2, + "slotIndex": 0, + }, + { + "id": 4, + "op": "CreateElement", + "type": "__snapshot_c1928_test_2", + }, + { + "beforeId": null, + "childId": 4, + "op": "InsertBefore", + "parentId": 2, + "slotIndex": 1, + }, + { + "id": 5, + "op": "CreateElement", + "type": "__snapshot_c1928_test_2", + }, + { + "beforeId": null, + "childId": 5, + "op": "InsertBefore", + "parentId": 2, + "slotIndex": 1, + }, + { + "id": 6, + "op": "CreateElement", + "type": "__snapshot_c1928_test_2", + }, + { + "beforeId": null, + "childId": 6, + "op": "InsertBefore", + "parentId": 2, + "slotIndex": 1, + }, + { + "id": 7, + "op": "CreateElement", + "type": "__snapshot_c1928_test_3", + }, + { + "beforeId": null, + "childId": 7, + "op": "InsertBefore", + "parentId": 2, + "slotIndex": 2, + }, + { + "beforeId": null, + "childId": 2, + "op": "InsertBefore", + "parentId": -1, + "slotIndex": 0, + }, + ] + `); + } + + { + const snapshotPatch = JSON.parse(callLepusMethodCalls[1][1]['data']).patchList[0].snapshotPatch; + const formattedSnapshotPatch = prettyFormatSnapshotPatch(snapshotPatch); + expect(formattedSnapshotPatch).toMatchInlineSnapshot(` + [ + { + "childId": 3, + "op": "RemoveChild", + "parentId": 2, + }, + { + "id": 8, + "op": "CreateElement", + "type": "__snapshot_c1928_test_2", + }, + { + "beforeId": null, + "childId": 8, + "op": "InsertBefore", + "parentId": 2, + "slotIndex": 0, + }, + { + "childId": 4, + "op": "RemoveChild", + "parentId": 2, + }, + { + "childId": 5, + "op": "RemoveChild", + "parentId": 2, + }, + { + "childId": 6, + "op": "RemoveChild", + "parentId": 2, + }, + { + "id": 9, + "op": "CreateElement", + "type": "__snapshot_c1928_test_1", + }, + { + "beforeId": null, + "childId": 9, + "op": "InsertBefore", + "parentId": 2, + "slotIndex": 1, + }, + { + "id": 10, + "op": "CreateElement", + "type": "__snapshot_c1928_test_1", + }, + { + "beforeId": null, + "childId": 10, + "op": "InsertBefore", + "parentId": 2, + "slotIndex": 1, + }, + { + "id": 11, + "op": "CreateElement", + "type": "__snapshot_c1928_test_1", + }, + { + "beforeId": null, + "childId": 11, + "op": "InsertBefore", + "parentId": 2, + "slotIndex": 1, + }, + ] + `); + } + + expect(container).toMatchInlineSnapshot(` + + + + + Hello 1 + + + + --- + + + + Hello 0 + + + Hello 0 + + + Hello 0 + + + + --- + + + + Hello 2 + + + + + `); +}); diff --git a/packages/react/testing-library/src/__tests__/text.test.jsx b/packages/react/testing-library/src/__tests__/text.test.jsx new file mode 100644 index 0000000000..8caad62936 --- /dev/null +++ b/packages/react/testing-library/src/__tests__/text.test.jsx @@ -0,0 +1,133 @@ +import '@testing-library/jest-dom'; +import { vi } from 'vitest'; +import { render, fireEvent } from '..'; +import { expect } from 'vitest'; +import { useState } from 'preact/hooks'; +import { prettyFormatSnapshotPatch } from '../../../runtime/lib/debug/formatPatch'; + +describe('should only render text when it is not empty', () => { + it('empty text should not be rendered', () => { + vi.spyOn(lynxTestingEnv.backgroundThread.lynxCoreInject.tt, 'OnLifecycleEvent'); + const onLifecycleEventCalls = lynxTestingEnv.backgroundThread.lynxCoreInject.tt.OnLifecycleEvent.mock.calls; + vi.spyOn(lynx.getNativeApp(), 'callLepusMethod'); + const callLepusMethodCalls = lynx.getNativeApp().callLepusMethod.mock.calls; + + const { container } = render( + + + {''} + Static Text + , + { + enableMainThread: true, + enableBackground: true, + }, + ); + + expect(JSON.stringify(JSON.parse(onLifecycleEventCalls[0][0][1]['root']), null, 2)).toMatchInlineSnapshot(` + "{ + "id": -1, + "type": "root", + "children": [ + { + "id": -2, + "type": "__snapshot_89850_test_1", + "__slotIndex": 0 + } + ] + }" + `); + expect(callLepusMethodCalls[0]).toMatchInlineSnapshot(` + [ + "rLynxChange", + { + "data": "{"patchList":[{"snapshotPatch":[],"id":2}]}", + "patchOptions": { + "isHydration": true, + "pipelineOptions": { + "dsl": "reactLynx", + "needTimestamps": true, + "pipelineID": "pipelineID", + "pipelineOrigin": "reactLynxHydrate", + "stage": "hydrate", + }, + "reloadVersion": 0, + }, + }, + [Function], + ] + `); + { + const snapshotPatch = JSON.parse(callLepusMethodCalls[0][1]['data']).patchList[0].snapshotPatch; + const formattedSnapshotPatch = prettyFormatSnapshotPatch(snapshotPatch); + expect(formattedSnapshotPatch.length).toBe(0); + } + }); + it('non-empty text should be rendered', () => { + vi.spyOn(lynxTestingEnv.backgroundThread.lynxCoreInject.tt, 'OnLifecycleEvent'); + const onLifecycleEventCalls = lynxTestingEnv.backgroundThread.lynxCoreInject.tt.OnLifecycleEvent.mock.calls; + vi.spyOn(lynx.getNativeApp(), 'callLepusMethod'); + const callLepusMethodCalls = lynx.getNativeApp().callLepusMethod.mock.calls; + + const { container } = render( + + + {'Dynamic Text'} + Static Text + , + { + enableMainThread: true, + enableBackground: true, + }, + ); + + expect(JSON.stringify(JSON.parse(onLifecycleEventCalls[0][0][1]['root']), null, 2)).toMatchInlineSnapshot(` + "{ + "id": -1, + "type": "root", + "children": [ + { + "id": -2, + "type": "__snapshot_89850_test_2", + "children": [ + { + "id": -3, + "type": null, + "values": [ + "Dynamic Text" + ], + "__slotIndex": 0 + } + ], + "__slotIndex": 0 + } + ] + }" + `); + expect(callLepusMethodCalls[0]).toMatchInlineSnapshot(` + [ + "rLynxChange", + { + "data": "{"patchList":[{"snapshotPatch":[],"id":2}]}", + "patchOptions": { + "isHydration": true, + "pipelineOptions": { + "dsl": "reactLynx", + "needTimestamps": true, + "pipelineID": "pipelineID", + "pipelineOrigin": "reactLynxHydrate", + "stage": "hydrate", + }, + "reloadVersion": 0, + }, + }, + [Function], + ] + `); + { + const snapshotPatch = JSON.parse(callLepusMethodCalls[0][1]['data']).patchList[0].snapshotPatch; + const formattedSnapshotPatch = prettyFormatSnapshotPatch(snapshotPatch); + expect(formattedSnapshotPatch.length).toBe(0); + } + }); +}); diff --git a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js index 778435c1de..42ac2c8450 100644 --- a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js +++ b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js @@ -19,6 +19,7 @@ import { destroyWorklet } from '../../../../runtime/lib/worklet/destroy.js'; import { initApiEnv } from '../../../../runtime/lib/worklet-runtime/api/lynxApi.js'; import { initEventListeners } from '../../../../runtime/lib/worklet-runtime/listeners.js'; import { initWorklet } from '../../../../runtime/lib/worklet-runtime/workletRuntime.js'; +import { setupDocument, setupBackgroundDocument } from '../../../../runtime/lib/document.js'; const { onInjectMainThreadGlobals, @@ -60,27 +61,8 @@ globalThis.onInjectMainThreadGlobals = (target) => { snapshotInstanceManager.nextId = 0; target.__root = new SnapshotInstance('root'); - function setupDocument(document) { - document.createElement = function(type) { - return new SnapshotInstance(type); - }; - document.createElementNS = function(_ns, type) { - return new SnapshotInstance(type); - }; - document.createTextNode = function(text) { - const i = new SnapshotInstance(null); - i.setAttribute(0, text); - Object.defineProperty(i, 'data', { - set(v) { - i.setAttribute(0, v); - }, - }); - return i; - }; - return document; - } - - target._document = setupDocument({}); + target._document = {}; + setupDocument(target._document); target.globalPipelineOptions = undefined; @@ -101,27 +83,8 @@ globalThis.onInjectBackgroundThreadGlobals = (target) => { backgroundSnapshotInstanceManager.nextId = 0; target.__root = new BackgroundSnapshotInstance('root'); - function setupBackgroundDocument(document) { - document.createElement = function(type) { - return new BackgroundSnapshotInstance(type); - }; - document.createElementNS = function(_ns, type) { - return new BackgroundSnapshotInstance(type); - }; - document.createTextNode = function(text) { - const i = new BackgroundSnapshotInstance(null); - i.setAttribute(0, text); - Object.defineProperty(i, 'data', { - set(v) { - i.setAttribute(0, v); - }, - }); - return i; - }; - return document; - } - - target._document = setupBackgroundDocument({}); + target._document = {}; + setupBackgroundDocument(target._document); target.globalPipelineOptions = undefined; // TODO: can we only inject to target(mainThread.globalThis) instead of globalThis? diff --git a/packages/react/transform/__test__/fixture.spec.js b/packages/react/transform/__test__/fixture.spec.js index 50188af9d5..d2deb4c94e 100644 --- a/packages/react/transform/__test__/fixture.spec.js +++ b/packages/react/transform/__test__/fixture.spec.js @@ -231,7 +231,7 @@ describe('jsx', () => { ]; }, [ (snapshot, index, oldValue)=>ReactLynx.updateListItemPlatformInfo(snapshot, index, oldValue, 0) - ], ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); + ], ReactLynx.__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); const __snapshot_da39a_04d8c_1 = "__snapshot_da39a_04d8c_1"; ReactLynx.snapshotCreatorMap[__snapshot_da39a_04d8c_1] = (__snapshot_da39a_04d8c_1)=>ReactLynx.createSnapshot(__snapshot_da39a_04d8c_1, function(snapshotInstance) { const pageId = ReactLynx.__pageId; @@ -241,19 +241,19 @@ describe('jsx', () => { ]; }, null, [ [ - ReactLynx.__DynamicPartListChildren, + ReactLynx.__DynamicPartListSlotV2, 0 ] ], undefined, globDynamicComponentEntry, null, true); /*#__PURE__*/ _jsx(__snapshot_da39a_04d8c_1, { - children: /*#__PURE__*/ _jsx(ReactLynxRuntimeComponents.DeferredListItem, { + $0: /*#__PURE__*/ _jsx(ReactLynxRuntimeComponents.DeferredListItem, { renderListItem: (__c)=>_jsx(__snapshot_da39a_04d8c_2, { values: [ { "item-key": "1" } ], - children: __c + $0: __c }), renderChildren: ()=>[], defer: true @@ -459,7 +459,7 @@ Component, View ]; }, [ (snapshot, index, oldValue)=>ReactLynx.updateSpread(snapshot, index, oldValue, 0) - ], ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, [ + ], ReactLynx.__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, [ 0 ], true); /*#__PURE__*/ ReactLynx1.wrapWithLynxComponent((__c, __spread)=>_jsx(__snapshot_da39a_89b7f_1, { @@ -469,7 +469,7 @@ Component, View __spread: true } ], - children: __c + $0: __c }), _jsx(Comp, { ...s })); diff --git a/packages/react/transform/crates/swc_plugin_list/lib.rs b/packages/react/transform/crates/swc_plugin_list/lib.rs index b9217e91d7..ba15f376a7 100644 --- a/packages/react/transform/crates/swc_plugin_list/lib.rs +++ b/packages/react/transform/crates/swc_plugin_list/lib.rs @@ -8,7 +8,9 @@ use swc_core::{ quote, }; -use swc_plugins_shared::jsx_helpers::{jsx_attr_value, jsx_children_to_expr, jsx_is_list_item}; +use swc_plugins_shared::jsx_helpers::{ + jsx_attr_value, jsx_children_to_expr, jsx_is_list, jsx_is_list_item, +}; pub struct ListVisitor where @@ -70,6 +72,13 @@ where // renderChildren={() => <>...} // /> fn visit_mut_jsx_element(&mut self, n: &mut JSXElement) { + if jsx_is_list(n) { + n.children = vec![JSXElementChild::JSXExprContainer(JSXExprContainer { + expr: JSXExpr::Expr(Box::new(jsx_children_to_expr(n.children.take()))), + span: DUMMY_SP, + })]; + } + n.visit_mut_children_with(self); if jsx_list_item_deferred(n) { @@ -162,6 +171,21 @@ where } } + fn visit_mut_jsx_element_child(&mut self, node: &mut JSXElementChild) { + if let JSXElementChild::JSXElement(jsx_element) = node { + if jsx_is_list(jsx_element) { + jsx_element.visit_mut_with(self); + *node = JSXElementChild::JSXExprContainer(JSXExprContainer { + expr: JSXExpr::Expr(Box::new(Expr::JSXElement(Box::new(*jsx_element.take())))), + span: DUMMY_SP, + }); + return; + } + } + + node.visit_mut_children_with(self); + } + fn visit_mut_module_items(&mut self, n: &mut Vec) { let mut new_items: Vec = vec![]; for item in n.iter_mut() { @@ -193,6 +217,275 @@ mod tests { use swc_plugin_snapshot::napi::{JSXTransformer, JSXTransformerConfig}; use swc_plugins_shared::{target_napi::TransformTarget, transform_mode_napi::TransformMode}; + test!( + module, + Syntax::Es(EsSyntax { + jsx: true, + ..Default::default() + }), + |t| { + ( + visit_mut_pass(ListVisitor::new(Some(t.comments.clone()))), + visit_mut_pass(JSXTransformer::<&SingleThreadedComments>::new( + JSXTransformerConfig { + preserve_jsx: true, + ..Default::default() + }, + None, + TransformMode::Test, + None, + )), + ) + }, + basic_list, + // Input codes + r#" + + + + + + ; + "# + ); + + test!( + module, + Syntax::Es(EsSyntax { + jsx: true, + ..Default::default() + }), + |t| { + ( + visit_mut_pass(ListVisitor::new(Some(t.comments.clone()))), + visit_mut_pass(JSXTransformer::<&SingleThreadedComments>::new( + JSXTransformerConfig { + preserve_jsx: true, + ..Default::default() + }, + None, + TransformMode::Test, + None, + )), + ) + }, + basic_list_with_fragment, + // Input codes + r#" + + + <> + + + + + + ; + "# + ); + + test!( + module, + Syntax::Es(EsSyntax { + jsx: true, + ..Default::default() + }), + |t| { + ( + visit_mut_pass(ListVisitor::new(Some(t.comments.clone()))), + visit_mut_pass(JSXTransformer::<&SingleThreadedComments>::new( + JSXTransformerConfig { + preserve_jsx: true, + ..Default::default() + }, + None, + TransformMode::Test, + None, + )), + ) + }, + basic_list_toplevel, + // Input codes + r#" + + !!! + !!! + + "# + ); + + test!( + module, + Syntax::Es(EsSyntax { + jsx: true, + ..Default::default() + }), + |t| visit_mut_pass(ListVisitor::new(Some(t.comments.clone()))), + should_transform_list_in_view, + r#" + + + + + + ; + "# + ); + + test!( + module, + Syntax::Es(EsSyntax { + jsx: true, + ..Default::default() + }), + |t| { + ( + visit_mut_pass(ListVisitor::new(Some(t.comments.clone()))), + visit_mut_pass(JSXTransformer::<&SingleThreadedComments>::new( + JSXTransformerConfig { + preserve_jsx: false, + target: TransformTarget::MIXED, + ..Default::default() + }, + None, + TransformMode::Development, + None, + )), + ) + }, + should_transform_list_in_view_with_snapshot, + r#" + + + + + + ; + "# + ); + + test!( + module, + Syntax::Es(EsSyntax { + jsx: true, + ..Default::default() + }), + |t| { + ( + visit_mut_pass(ListVisitor::new(Some(t.comments.clone()))), + visit_mut_pass(JSXTransformer::<&SingleThreadedComments>::new( + JSXTransformerConfig { + preserve_jsx: false, + target: TransformTarget::MIXED, + ..Default::default() + }, + None, + TransformMode::Development, + None, + )), + ) + }, + should_transform_list_in_view_with_static_sibling_with_snapshot, + r#" + + + + + + + ; + "# + ); + + test!( + module, + Syntax::Es(EsSyntax { + jsx: true, + ..Default::default() + }), + |t| { visit_mut_pass(ListVisitor::new(Some(t.comments.clone()))) }, + should_transform_list_in_view_with_static_sibling_nested, + r#" + + + + + + + + + + + + + + + + ; + "# + ); + + test!( + module, + Syntax::Es(EsSyntax { + jsx: true, + ..Default::default() + }), + |t| { + ( + visit_mut_pass(ListVisitor::new(Some(t.comments.clone()))), + visit_mut_pass(JSXTransformer::<&SingleThreadedComments>::new( + JSXTransformerConfig { + preserve_jsx: false, + target: TransformTarget::MIXED, + ..Default::default() + }, + None, + TransformMode::Development, + None, + )), + ) + }, + should_transform_list_in_view_with_static_sibling_with_snapshot_nested, + r#" + + + + + + + + + + + + + + + + ; + "# + ); + + test!( + module, + Syntax::Es(EsSyntax { + jsx: true, + ..Default::default() + }), + |t| { visit_mut_pass(ListVisitor::new(Some(t.comments.clone()))) }, + should_transform_list_in_view_with_static_sibling, + r#" + + + + + + + ; + "# + ); + test!( module, Syntax::Es(EsSyntax { diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_list.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/basic_list.js similarity index 68% rename from packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_list.js rename to packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/basic_list.js index 285902f26a..4117eeaa77 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_list.js +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/basic_list.js @@ -18,25 +18,17 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_2] = (__snapshot_da39a_test_2 ]; }, null, [ [ - ReactLynx.__DynamicPartListChildren, + ReactLynx.__DynamicPartListSlotV2, 0 ] ], undefined, globDynamicComponentEntry, null, true); -const __snapshot_da39a_test_4 = "__snapshot_da39a_test_4"; -ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_4] = (__snapshot_da39a_test_4)=>ReactLynx.createSnapshot(__snapshot_da39a_test_4, function() { - const pageId = ReactLynx.__pageId; - const el = __CreateView(pageId); - return [ - el - ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); const __snapshot_da39a_test_1 = "__snapshot_da39a_test_1"; ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1)=>ReactLynx.createSnapshot(__snapshot_da39a_test_1, function() { const pageId = ReactLynx.__pageId; const el = __CreateView(pageId); const el1 = __CreateWrapperElement(pageId); __AppendElement(el, el1); - const el2 = __CreateWrapperElement(pageId); + const el2 = __CreateView(pageId); __AppendElement(el, el2); return [ el, @@ -45,17 +37,17 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 ]; }, null, [ [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 1 ], [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 2 ] ], undefined, globDynamicComponentEntry, null, true); -<__snapshot_da39a_test_1><__snapshot_da39a_test_2><__snapshot_da39a_test_3 values={[ +<__snapshot_da39a_test_1 $0={<__snapshot_da39a_test_2 $0={<__snapshot_da39a_test_3 values={[ { "full-span": true, "reuse-identifier": x } -]}/><__snapshot_da39a_test_4>; +]}/>}/>} $1={}/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_list_toplevel.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/basic_list_toplevel.js similarity index 93% rename from packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_list_toplevel.js rename to packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/basic_list_toplevel.js index 1ba71e9690..98fcab4f0d 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_list_toplevel.js +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/basic_list_toplevel.js @@ -30,11 +30,11 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 ]; }, null, [ [ - ReactLynx.__DynamicPartListChildren, + ReactLynx.__DynamicPartListSlotV2, 0 ] ], undefined, globDynamicComponentEntry, null, true); -<__snapshot_da39a_test_1>{[ +<__snapshot_da39a_test_1 $0={[ <__snapshot_da39a_test_2/>, <__snapshot_da39a_test_3/> -]}; +]}/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_list_with_fragment.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/basic_list_with_fragment.js similarity index 72% rename from packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_list_with_fragment.js rename to packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/basic_list_with_fragment.js index b2827a255c..9c5a79a67e 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_list_with_fragment.js +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/basic_list_with_fragment.js @@ -24,25 +24,17 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_2] = (__snapshot_da39a_test_2 ]; }, null, [ [ - ReactLynx.__DynamicPartListChildren, + ReactLynx.__DynamicPartListSlotV2, 0 ] ], undefined, globDynamicComponentEntry, null, true); -const __snapshot_da39a_test_5 = "__snapshot_da39a_test_5"; -ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_5] = (__snapshot_da39a_test_5)=>ReactLynx.createSnapshot(__snapshot_da39a_test_5, function() { - const pageId = ReactLynx.__pageId; - const el = __CreateView(pageId); - return [ - el - ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); const __snapshot_da39a_test_1 = "__snapshot_da39a_test_1"; ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1)=>ReactLynx.createSnapshot(__snapshot_da39a_test_1, function() { const pageId = ReactLynx.__pageId; const el = __CreateView(pageId); const el1 = __CreateWrapperElement(pageId); __AppendElement(el, el1); - const el2 = __CreateWrapperElement(pageId); + const el2 = __CreateView(pageId); __AppendElement(el, el2); return [ el, @@ -51,15 +43,15 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 ]; }, null, [ [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 1 ], [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 2 ] ], undefined, globDynamicComponentEntry, null, true); -<__snapshot_da39a_test_1><__snapshot_da39a_test_2>{<> +<__snapshot_da39a_test_1 $0={<__snapshot_da39a_test_2 $0={<> <__snapshot_da39a_test_3/> <__snapshot_da39a_test_4/> - }<__snapshot_da39a_test_5>; + }/>} $1={}/>; diff --git a/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view.js new file mode 100644 index 0000000000..cdfa75aa5a --- /dev/null +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view.js @@ -0,0 +1,6 @@ + + {{[ + , + +]}} + ; diff --git a/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_snapshot.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_snapshot.js new file mode 100644 index 0000000000..bd292b254f --- /dev/null +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_snapshot.js @@ -0,0 +1,53 @@ +const __snapshot_da39a_test_3 = "__snapshot_da39a_test_3"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_3] = (__snapshot_da39a_test_3)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_3, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateElement("list-item", pageId); + return [ + el + ]; + }, [ + (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0) + ], null, undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_4 = "__snapshot_da39a_test_4"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_4] = (__snapshot_da39a_test_4)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_4, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateElement("list-item", pageId); + return [ + el + ]; + }, [ + (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0) + ], null, undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_2 = "__snapshot_da39a_test_2"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_2] = (__snapshot_da39a_test_2)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_2, function(snapshotInstance) { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = require('@lynx-js/react/internal').snapshotCreateList(pageId, snapshotInstance, 0); + return [ + el + ]; + }, null, [ + [ + require('@lynx-js/react/internal').__DynamicPartListSlotV2, + 0 + ] + ], undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_1 = "__snapshot_da39a_test_1"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_1, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateView(pageId); + return [ + el + ]; + }, null, require('@lynx-js/react/internal').__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); +<__snapshot_da39a_test_1 $0={<__snapshot_da39a_test_2 $0={[ + <__snapshot_da39a_test_3 key="1" values={[ + { + "item-key": "1" + } + ]}/>, + <__snapshot_da39a_test_4 key="2" values={[ + { + "item-key": "2" + } + ]}/> +]}/>}/>; diff --git a/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling.js new file mode 100644 index 0000000000..e2c9e624f3 --- /dev/null +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling.js @@ -0,0 +1,7 @@ + + {{[ + , + +]}} + + ; diff --git a/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling_nested.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling_nested.js new file mode 100644 index 0000000000..233a18d4a5 --- /dev/null +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling_nested.js @@ -0,0 +1,16 @@ + + {{[ + , + , + + + {{[ + , + + ]}} + + + +]}} + + ; diff --git a/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling_with_snapshot.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling_with_snapshot.js new file mode 100644 index 0000000000..8f30faab26 --- /dev/null +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling_with_snapshot.js @@ -0,0 +1,64 @@ +const __snapshot_da39a_test_3 = "__snapshot_da39a_test_3"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_3] = (__snapshot_da39a_test_3)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_3, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateElement("list-item", pageId); + return [ + el + ]; + }, [ + (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0) + ], null, undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_4 = "__snapshot_da39a_test_4"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_4] = (__snapshot_da39a_test_4)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_4, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateElement("list-item", pageId); + return [ + el + ]; + }, [ + (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0) + ], null, undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_2 = "__snapshot_da39a_test_2"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_2] = (__snapshot_da39a_test_2)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_2, function(snapshotInstance) { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = require('@lynx-js/react/internal').snapshotCreateList(pageId, snapshotInstance, 0); + return [ + el + ]; + }, null, [ + [ + require('@lynx-js/react/internal').__DynamicPartListSlotV2, + 0 + ] + ], undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_1 = "__snapshot_da39a_test_1"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_1, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateView(pageId); + const el1 = __CreateWrapperElement(pageId); + __AppendElement(el, el1); + const el2 = __CreateView(pageId); + __AppendElement(el, el2); + return [ + el, + el1, + el2 + ]; + }, null, [ + [ + require('@lynx-js/react/internal').__DynamicPartSlotV2, + 1 + ] + ], undefined, globDynamicComponentEntry, null, true); +<__snapshot_da39a_test_1 $0={<__snapshot_da39a_test_2 $0={[ + <__snapshot_da39a_test_3 key="1" values={[ + { + "item-key": "1" + } + ]}/>, + <__snapshot_da39a_test_4 key="2" values={[ + { + "item-key": "2" + } + ]}/> +]}/>}/>; diff --git a/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling_with_snapshot_nested.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling_with_snapshot_nested.js new file mode 100644 index 0000000000..797dc123b2 --- /dev/null +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_in_view_with_static_sibling_with_snapshot_nested.js @@ -0,0 +1,137 @@ +const __snapshot_da39a_test_3 = "__snapshot_da39a_test_3"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_3] = (__snapshot_da39a_test_3)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_3, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateElement("list-item", pageId); + return [ + el + ]; + }, [ + (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0) + ], null, undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_4 = "__snapshot_da39a_test_4"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_4] = (__snapshot_da39a_test_4)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_4, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateElement("list-item", pageId); + return [ + el + ]; + }, [ + (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0) + ], null, undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_7 = "__snapshot_da39a_test_7"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_7] = (__snapshot_da39a_test_7)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_7, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateElement("list-item", pageId); + return [ + el + ]; + }, [ + (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0) + ], null, undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_8 = "__snapshot_da39a_test_8"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_8] = (__snapshot_da39a_test_8)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_8, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateElement("list-item", pageId); + return [ + el + ]; + }, [ + (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0) + ], null, undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_6 = "__snapshot_da39a_test_6"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_6] = (__snapshot_da39a_test_6)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_6, function(snapshotInstance) { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = require('@lynx-js/react/internal').snapshotCreateList(pageId, snapshotInstance, 0); + return [ + el + ]; + }, null, [ + [ + require('@lynx-js/react/internal').__DynamicPartListSlotV2, + 0 + ] + ], undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_5 = "__snapshot_da39a_test_5"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_5] = (__snapshot_da39a_test_5)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_5, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateElement("list-item", pageId); + const el1 = __CreateView(pageId); + __AppendElement(el, el1); + const el2 = __CreateWrapperElement(pageId); + __AppendElement(el1, el2); + const el3 = __CreateView(pageId); + __AppendElement(el1, el3); + return [ + el, + el1, + el2, + el3 + ]; + }, [ + (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0) + ], [ + [ + require('@lynx-js/react/internal').__DynamicPartSlotV2, + 2 + ] + ], undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_2 = "__snapshot_da39a_test_2"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_2] = (__snapshot_da39a_test_2)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_2, function(snapshotInstance) { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = require('@lynx-js/react/internal').snapshotCreateList(pageId, snapshotInstance, 0); + return [ + el + ]; + }, null, [ + [ + require('@lynx-js/react/internal').__DynamicPartListSlotV2, + 0 + ] + ], undefined, globDynamicComponentEntry, null, true); +const __snapshot_da39a_test_1 = "__snapshot_da39a_test_1"; +require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_1, function() { + const pageId = require('@lynx-js/react/internal').__pageId; + const el = __CreateView(pageId); + const el1 = __CreateWrapperElement(pageId); + __AppendElement(el, el1); + const el2 = __CreateView(pageId); + __AppendElement(el, el2); + return [ + el, + el1, + el2 + ]; + }, null, [ + [ + require('@lynx-js/react/internal').__DynamicPartSlotV2, + 1 + ] + ], undefined, globDynamicComponentEntry, null, true); +<__snapshot_da39a_test_1 $0={<__snapshot_da39a_test_2 $0={[ + <__snapshot_da39a_test_3 key="1" values={[ + { + "item-key": "1" + } + ]}/>, + <__snapshot_da39a_test_4 key="2" values={[ + { + "item-key": "2" + } + ]}/>, + <__snapshot_da39a_test_5 key="3" values={[ + { + "item-key": "3" + } + ]} $0={<__snapshot_da39a_test_6 $0={[ + <__snapshot_da39a_test_7 key="1" values={[ + { + "item-key": "1" + } + ]}/>, + <__snapshot_da39a_test_8 key="2" values={[ + { + "item-key": "2" + } + ]}/> + ]}/>}/> +]}/>}/>; diff --git a/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_item_deferred_in_list.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_item_deferred_in_list.js index 1d861f3e9c..6eac078e92 100644 --- a/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_item_deferred_in_list.js +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_item_deferred_in_list.js @@ -1,5 +1,5 @@ import * as ReactLynxRuntimeComponents from '@lynx-js/react/runtime-components'; - - {__c}} renderChildren={()=>[]} key="1" defer/> - {__c}} renderChildren={()=>[]} key="2" defer/> - ; +{[ + {__c}} renderChildren={()=>[]} key="1" defer/>, + {__c}} renderChildren={()=>[]} key="2" defer/> +]}; diff --git a/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_item_deferred_with_children_with_snapshot.js b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_item_deferred_with_children_with_snapshot.js index cec749bc24..e02c5a6a6a 100644 --- a/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_item_deferred_with_children_with_snapshot.js +++ b/packages/react/transform/crates/swc_plugin_list/tests/__swc_snapshots__/lib.rs/should_transform_list_item_deferred_with_children_with_snapshot.js @@ -11,7 +11,7 @@ require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_1] = }, [ (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0), (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateEvent(snapshot, index, oldValue, 0, "bindEvent", "tap", '') - ], require('@lynx-js/react/internal').__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); + ], require('@lynx-js/react/internal').__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); const __snapshot_da39a_test_2 = "__snapshot_da39a_test_2"; require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_2] = (__snapshot_da39a_test_2)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_2, function() { const pageId = require('@lynx-js/react/internal').__pageId; @@ -41,7 +41,7 @@ require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_4] = "item-key": "1" }, noop - ]}>{__c}} renderChildren={()=>[ + ]} $0={__c}/>} renderChildren={()=>[ <__snapshot_da39a_test_2/>, <__snapshot_da39a_test_3/>, <__snapshot_da39a_test_4/> @@ -58,7 +58,7 @@ require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_5] = }, [ (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0), (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateEvent(snapshot, index, oldValue, 0, "bindEvent", "tap", '') - ], require('@lynx-js/react/internal').__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); + ], require('@lynx-js/react/internal').__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); const __snapshot_da39a_test_6 = "__snapshot_da39a_test_6"; require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_6] = (__snapshot_da39a_test_6)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_6, function() { const pageId = require('@lynx-js/react/internal').__pageId; @@ -72,7 +72,7 @@ require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_6] = "item-key": "1" }, noop - ]}>{__c}} renderChildren={()=><__snapshot_da39a_test_6/>} key="1" defer/>; + ]} $0={__c}/>} renderChildren={()=><__snapshot_da39a_test_6/>} key="1" defer/>; const __snapshot_da39a_test_7 = "__snapshot_da39a_test_7"; require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_7] = (__snapshot_da39a_test_7)=>require('@lynx-js/react/internal').createSnapshot(__snapshot_da39a_test_7, function() { const pageId = require('@lynx-js/react/internal').__pageId; @@ -85,10 +85,10 @@ require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_7] = }, [ (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateListItemPlatformInfo(snapshot, index, oldValue, 0), (snapshot, index, oldValue)=>require('@lynx-js/react/internal').updateEvent(snapshot, index, oldValue, 0, "bindEvent", "tap", '') - ], require('@lynx-js/react/internal').__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); + ], require('@lynx-js/react/internal').__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); <__snapshot_da39a_test_7 values={[ { "item-key": "1" }, noop - ]}>{__c}} renderChildren={()=>} key="1" defer/>; + ]} $0={__c}/>} renderChildren={()=>} key="1" defer/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/lib.rs b/packages/react/transform/crates/swc_plugin_snapshot/lib.rs index 3a0d93c40e..662eec1295 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/lib.rs +++ b/packages/react/transform/crates/swc_plugin_snapshot/lib.rs @@ -23,7 +23,6 @@ use swc_core::{ }; mod attr_name; -mod slot_marker; #[cfg(feature = "napi")] pub mod napi; @@ -31,7 +30,7 @@ pub mod napi; use swc_plugins_shared::{ css::get_string_inline_style_from_literal, jsx_helpers::{ - jsx_attr_name, jsx_attr_to_prop, jsx_attr_value, jsx_children_to_expr, + jsx_attr_name, jsx_attr_to_prop, jsx_attr_value, jsx_children_to_expr, jsx_has_dynamic_key, jsx_is_children_full_dynamic, jsx_is_custom, jsx_is_list, jsx_is_list_item, jsx_name, jsx_props_to_obj, jsx_text_to_str, transform_jsx_attr_str, }, @@ -40,10 +39,7 @@ use swc_plugins_shared::{ utils::{calc_hash, calc_hash_number}, }; -use self::{ - attr_name::AttrName, - slot_marker::{jsx_is_internal_slot, jsx_unwrap_internal_slot, WrapperMarker}, -}; +use self::attr_name::AttrName; // impl From for Expr { // fn from(value: i32) -> Self { @@ -118,9 +114,8 @@ pub struct UISourceMapRecord { pub enum DynamicPart { Attr(Expr, i32, AttrName), Spread(Expr, i32), - Slot(JSXElement, i32), - Children(Expr, i32), - ListChildren(Expr, i32), + Slot(Expr, i32), + ListSlot(Expr, i32), } pub fn i32_to_expr(i: &i32) -> Expr { @@ -246,8 +241,7 @@ impl DynamicPart { element_index: Expr = i32_to_expr(element_index) ), DynamicPart::Slot(_, _) => Expr::Lit(Lit::Null(Null { span: DUMMY_SP })), - DynamicPart::Children(_, _) => Expr::Lit(Lit::Null(Null { span: DUMMY_SP })), - DynamicPart::ListChildren(_, _) => Expr::Lit(Lit::Null(Null { span: DUMMY_SP })), + DynamicPart::ListSlot(_, _) => Expr::Lit(Lit::Null(Null { span: DUMMY_SP })), }, TransformTarget::JS => Expr::Lit(Lit::Null(Null { span: DUMMY_SP })), } @@ -267,7 +261,6 @@ where static_stmts: Vec>, si_id: Lazy, snapshot_creator: Option, - dynamic_part_count: i32, dynamic_parts: Vec, dynamic_part_visitor: &'a mut V, key: Option, @@ -282,7 +275,6 @@ where { fn new( runtime_id: Expr, - dynamic_part_count: i32, dynamic_part_visitor: &'a mut V, enable_ui_source_map: bool, node_index_fn: F, @@ -296,7 +288,6 @@ where static_stmts: vec![], si_id: Lazy::new(|| private_ident!("snapshotInstance")), snapshot_creator: None, - dynamic_part_count, dynamic_parts: vec![], dynamic_part_visitor, key: None, @@ -450,20 +441,83 @@ where V: VisitMut, F: Fn(Span) -> Expr, { - fn visit_mut_jsx_element(&mut self, n: &mut JSXElement) { - if jsx_is_internal_slot(n) { - if self.dynamic_part_count > 1 { - n.visit_mut_children_with(self.dynamic_part_visitor); - self.dynamic_parts.push(DynamicPart::Slot( - jsx_unwrap_internal_slot(n.take()), - self.element_index, - )); - *n = WRAPPER_NODE_2.clone(); + fn visit_mut_jsx_element_childs(&mut self, n: &mut Vec) { + if n.is_empty() { + return; + } + + // merge dynamic parts together to reduce wrapper node count + + let mut merged_children: Vec = vec![]; + let mut current_chunk: Vec = vec![]; + + for mut child in n.take() { + let should_merge: bool; + match child { + JSXElementChild::JSXText(ref text) => { + if jsx_text_to_str(&text.value).is_empty() { + should_merge = current_chunk.is_empty(); + } else { + should_merge = true; + } + } + JSXElementChild::JSXElement(ref element) => { + should_merge = !jsx_is_custom(element); + } + JSXElementChild::JSXExprContainer(JSXExprContainer { + expr: JSXExpr::Expr(ref _expr), + .. + }) => { + should_merge = false; + } + JSXElementChild::JSXFragment(_) + | JSXElementChild::JSXExprContainer(JSXExprContainer { + expr: JSXExpr::JSXEmptyExpr(_), + .. + }) => { + should_merge = true; + } + JSXElementChild::JSXSpreadChild(_) => { + unreachable!("JSXSpreadChild is not supported yet"); + } + } + + if should_merge { + if !current_chunk.is_empty() { + current_chunk.visit_mut_with(self.dynamic_part_visitor); + self.dynamic_parts.push(DynamicPart::Slot( + jsx_children_to_expr(current_chunk.take()), + self.element_index, + )); + + let mut child = JSXElementChild::JSXElement(Box::new(WRAPPER_NODE_2.clone())); + child.visit_mut_with(self); + merged_children.push(child); + } + + child.visit_mut_with(self); + merged_children.push(child); } else { - *n = jsx_unwrap_internal_slot(n.take()); + current_chunk.push(child); } } + if !current_chunk.is_empty() { + current_chunk.visit_mut_with(self.dynamic_part_visitor); + self.dynamic_parts.push(DynamicPart::Slot( + jsx_children_to_expr(current_chunk.take()), + self.element_index, + )); + + let mut child = JSXElementChild::JSXElement(Box::new(WRAPPER_NODE_2.clone())); + child.visit_mut_with(self); + merged_children.push(child); + } + + *n = merged_children; + } + + fn visit_mut_jsx_element(&mut self, n: &mut JSXElement) { if !jsx_is_custom(n) { match Lazy::::get(&self.page_id) { Some(_) => {} @@ -479,6 +533,23 @@ where let el = private_ident!("el"); self.element_ids.insert(self.element_index, el.clone()); + if (jsx_has_dynamic_key(n)) && self.parent_element.is_some() { + n.visit_mut_with(self.dynamic_part_visitor); + let expr = Expr::JSXElement(Box::new(n.take())); + + if jsx_is_list(n) { + self + .dynamic_parts + .push(DynamicPart::ListSlot(expr, self.element_index)); + } else { + self + .dynamic_parts + .push(DynamicPart::Slot(expr, self.element_index)); + } + + *n = WRAPPER_NODE_2.clone(); + } + let static_stmt = self.static_stmt_from_jsx_element(n, el.clone()); let static_stmt = RefCell::new(static_stmt); self.static_stmts.push(static_stmt.clone()); @@ -912,76 +983,26 @@ where ))); }; - let is_list = jsx_is_list(n); - let is_children_full_dynamic = is_list || jsx_is_children_full_dynamic(n); + let is_children_full_dynamic = jsx_is_children_full_dynamic(n); if !is_children_full_dynamic { self.element_index += 1; let pre_parent_element = self.parent_element.take(); self.parent_element = Some(el.clone()); - // n.children.iter_mut().for_each(|child| match child { - // JSXElementChild::JSXText(_) => { - // child.visit_mut_children_with(self); - // } - // JSXElementChild::JSXElement(_) => { - // child.visit_mut_children_with(self); - // } - // JSXElementChild::JSXFragment(_) => { - // child.visit_mut_children_with(self); - // } - // JSXElementChild::JSXExprContainer(JSXExprContainer { - // expr: JSXExpr::Expr(_expr), - // .. - // }) => { - // unreachable!("should be handled by WrapDynamicPart"); - // } - // JSXElementChild::JSXExprContainer(JSXExprContainer { - // expr: JSXExpr::JSXEmptyExpr(_), - // .. - // }) => { - // // comment, just ignore - // } - // JSXElementChild::JSXSpreadChild(_) => { - // unreachable!("JSXSpreadChild is not supported yet"); - // } - // }); - n.visit_mut_children_with(self); - self.parent_element = pre_parent_element; } else { - if self.dynamic_part_count <= 1 { - n.visit_mut_children_with(self.dynamic_part_visitor); - let children_expr = jsx_children_to_expr(n.children.take()); - if is_list { - self - .dynamic_parts - .push(DynamicPart::ListChildren(children_expr, self.element_index)); - } else { - self - .dynamic_parts - .push(DynamicPart::Children(children_expr, self.element_index)); - } + n.visit_mut_children_with(self.dynamic_part_visitor); + let children_expr = jsx_children_to_expr(n.children.take()); + if jsx_is_list(n) { + self + .dynamic_parts + .push(DynamicPart::ListSlot(children_expr, self.element_index)); } else { - // static_stmt.replace_with(|_| { - // let r = WRAPPER_NODE.clone(); - // let (static_stmt, _) = - // self.static_stmt_from_jsx_element(&r, el.clone()); - // static_stmt - // }); - - // n.map_with_mut(|value| value.fold_with(self.dynamic_part_visitor)); - // if is_list { - // // unreachable!() - // self.dynamic_parts - // .push(DynamicPart::Slot(n.take(), self.element_index)); - // } else { - // self.dynamic_parts - // .push(DynamicPart::Slot(n.take(), self.element_index)); - // } - - unreachable!("should be handled by WrapDynamicPart"); + self + .dynamic_parts + .push(DynamicPart::Slot(children_expr, self.element_index)); } self.element_index += 1; @@ -1042,7 +1063,7 @@ where n.visit_mut_children_with(self.dynamic_part_visitor); if self.parent_element.is_some() { - self.dynamic_parts.push(DynamicPart::Children( + self.dynamic_parts.push(DynamicPart::Slot( Expr::JSXElement(Box::new(n.take())), self.element_index, )); @@ -1284,12 +1305,6 @@ where SyntaxContext::default().apply_mark(Mark::fresh(Mark::root())), ); - let mut wrap_dynamic_part = WrapperMarker { - current_is_children_full_dynamic: false, - dynamic_part_count: 0, - }; - node.visit_mut_with(&mut wrap_dynamic_part); - let target = self.cfg.target; let runtime_id = self.runtime_id.clone(); let filename_hash = self.filename_hash.clone(); @@ -1327,7 +1342,6 @@ where let mut dynamic_part_extractor = DynamicPartExtractor::new( self.runtime_id.clone(), - wrap_dynamic_part.dynamic_part_count, self, self.cfg.enable_ui_source_map, node_index_fn, @@ -1338,7 +1352,7 @@ where let mut snapshot_values: Vec> = vec![]; let mut snapshot_values_has_attr = false; let mut snapshot_attrs: Vec = vec![]; - let mut snapshot_children: Vec = vec![]; + let mut snapshot_children: Vec = vec![]; let mut snapshot_dynamic_part_def: Vec> = vec![]; let mut snapshot_refs_and_spread_index: Vec> = vec![]; let mut snapshot_slot_def: Vec> = vec![]; @@ -1356,205 +1370,106 @@ where .into_iter() .partition(|dynamic_part| match dynamic_part { DynamicPart::Attr(_, _, _) | DynamicPart::Spread(_, _) => true, - DynamicPart::Slot(_, _) | DynamicPart::Children(_, _) | DynamicPart::ListChildren(_, _) => { - false - } + DynamicPart::Slot(_, _) | DynamicPart::ListSlot(_, _) => false, }); - dynamic_part_attr - .into_iter() - .enumerate() - .map(|(index, dynamic_part)| { - ( - JSXAttrName::Ident(IdentName::new(format!("__{index}").into(), DUMMY_SP)), - JSXAttrName::Ident(IdentName::new(format!("_c{index}").into(), DUMMY_SP)), - JSXElementName::Ident(Ident::new( - format!("s{index}").into(), - DUMMY_SP, - SyntaxContext::default(), - )), - dynamic_part, - ) - }) - .map(|(name, child_name, ref jsx_name, dynamic_part)| { - ( - name, - child_name, - JSXOpeningElement { - name: jsx_name.clone(), - span: DUMMY_SP, - attrs: vec![], - self_closing: false, - type_args: None, - }, - JSXClosingElement { - name: jsx_name.clone(), - span: DUMMY_SP, - }, - dynamic_part, - ) - }) - .for_each( - |(_name, _child_name, _jsx_opening, _jsx_closing, dynamic_part)| { - match &dynamic_part { - DynamicPart::Attr(_, _, _) | DynamicPart::Spread(_, _) => { - if let DynamicPart::Attr(_, _, AttrName::Ref) | DynamicPart::Spread(_, _) = - dynamic_part - { - snapshot_refs_and_spread_index.push(Some( - Expr::Lit(Lit::Num(snapshot_dynamic_part_def.len().into())).into(), - )); - } - snapshot_dynamic_part_def.push(Some(ExprOrSpread { - spread: None, - expr: Box::new(dynamic_part.to_updater( - runtime_id.clone(), - target, - snapshot_dynamic_part_def.len() as i32, - )), - })); - } - DynamicPart::Slot(_, _) => {} - DynamicPart::Children(_, _) => {} - DynamicPart::ListChildren(_, _) => {} + dynamic_part_attr.into_iter().for_each(|dynamic_part| { + match &dynamic_part { + DynamicPart::Attr(_, _, _) | DynamicPart::Spread(_, _) => { + if let DynamicPart::Attr(_, _, AttrName::Ref) | DynamicPart::Spread(_, _) = dynamic_part { + snapshot_refs_and_spread_index.push(Some( + Expr::Lit(Lit::Num(snapshot_dynamic_part_def.len().into())).into(), + )); } + snapshot_dynamic_part_def.push(Some(ExprOrSpread { + spread: None, + expr: Box::new(dynamic_part.to_updater( + runtime_id.clone(), + target, + snapshot_dynamic_part_def.len() as i32, + )), + })); + } + DynamicPart::Slot(_, _) => {} + DynamicPart::ListSlot(_, _) => {} + } - match dynamic_part { - DynamicPart::Attr(value, _, attr_name) => { - snapshot_values.push(Some(ExprOrSpread { - spread: None, - expr: Box::new(if let AttrName::Event(_, _) = attr_name { - if target == TransformTarget::LEPUS { - quote!("1" as Expr) - } else { - value - } - } else if let AttrName::Ref = attr_name { - if target == TransformTarget::LEPUS { - quote!("1" as Expr) - } else { - quote!( - "$runtime_id.transformRef($value)" as Expr, - runtime_id: Expr = runtime_id.clone(), - value: Expr = value, - ) - } - } else { - value - }), - })); - snapshot_values_has_attr = true; - // snapshot_attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr { - // span: DUMMY_SP, - // name, - // value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer { - // span: DUMMY_SP, - // expr: JSXExpr::Expr(Box::new(if let AttrName::Event(_, _) = attr_name { - // if target == TransformTarget::LEPUS { - // Expr::Lit(Lit::Null(Null { span: DUMMY_SP })) - // } else { - // value - // } - // } else { - // value - // })), - // })), - // })); - } - DynamicPart::Spread(value, _) => { - snapshot_values.push(Some(ExprOrSpread { - spread: None, - expr: Box::new(value), - })); - snapshot_values_has_attr = true; - // snapshot_attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr { - // span: DUMMY_SP, - // name, - // value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer { - // span: DUMMY_SP, - // expr: JSXExpr::Expr(Box::new(value)), - // })), - // })); - } - DynamicPart::ListChildren(_, _) => {} - DynamicPart::Children(_, _) => {} - DynamicPart::Slot(_, _) => {} - } - }, - ); + match dynamic_part { + DynamicPart::Attr(value, _, attr_name) => { + snapshot_values.push(Some(ExprOrSpread { + spread: None, + expr: Box::new(if let AttrName::Event(_, _) = attr_name { + if target == TransformTarget::LEPUS { + quote!("1" as Expr) + } else { + value + } + } else if let AttrName::Ref = attr_name { + if target == TransformTarget::LEPUS { + quote!("1" as Expr) + } else { + quote!( + "$runtime_id.transformRef($value)" as Expr, + runtime_id: Expr = runtime_id.clone(), + value: Expr = value, + ) + } + } else { + value + }), + })); + snapshot_values_has_attr = true; + } + DynamicPart::Spread(value, _) => { + snapshot_values.push(Some(ExprOrSpread { + spread: None, + expr: Box::new(value), + })); + snapshot_values_has_attr = true; + } + DynamicPart::ListSlot(_, _) => {} + DynamicPart::Slot(_, _) => {} + } + }); let slot_expr = match (dynamic_part_children.len(), dynamic_part_children.first()) { (0, _) => Expr::Lit(Lit::Null(Null { span: DUMMY_SP })), - (1, Some(DynamicPart::Children(expr, 0))) => { - let expr = expr.clone(); - snapshot_children.push(match expr { - Expr::JSXElement(jsx) => JSXElementChild::JSXElement(jsx), - _ => JSXElementChild::JSXExprContainer(JSXExprContainer { - span: DUMMY_SP, - expr: JSXExpr::Expr(Box::new(expr)), - }), - }); - + (1, Some(DynamicPart::Slot(expr, 0))) => { + snapshot_children.push(expr.clone()); quote!( - "$runtime_id.__DynamicPartChildren_0" as Expr, + "$runtime_id.__DynamicPartSlotV2_0" as Expr, runtime_id: Expr = runtime_id.clone(), ) } _ => { - dynamic_part_children.into_iter().for_each(|dynamic_part| { - match dynamic_part { + dynamic_part_children + .into_iter() + .for_each(|dynamic_part| match dynamic_part { DynamicPart::Attr(_, _, _) => {} DynamicPart::Spread(_, _) => {} - DynamicPart::ListChildren(expr, element_index) => { - // snapshot_values.push(None); - snapshot_children.push(match expr { - Expr::JSXElement(jsx) => JSXElementChild::JSXElement(jsx), - _ => JSXElementChild::JSXExprContainer(JSXExprContainer { - span: DUMMY_SP, - expr: JSXExpr::Expr(Box::new(expr)), - }), - }); - snapshot_slot_def.push(Some(ExprOrSpread { - spread: None, - expr: Box::new(quote!( - "[$runtime_id.__DynamicPartListChildren, $element_index]" as Expr, - runtime_id: Expr = runtime_id.clone(), - element_index: Expr = i32_to_expr(&element_index), - )), - })); - } - DynamicPart::Children(expr, element_index) => { - // snapshot_values.push(None); - snapshot_children.push(match expr { - Expr::JSXElement(jsx) => JSXElementChild::JSXElement(jsx), - _ => JSXElementChild::JSXExprContainer(JSXExprContainer { - span: DUMMY_SP, - expr: JSXExpr::Expr(Box::new(expr)), - }), - }); + DynamicPart::ListSlot(expr, element_index) => { + snapshot_children.push(expr); snapshot_slot_def.push(Some(ExprOrSpread { spread: None, expr: Box::new(quote!( - "[$runtime_id.__DynamicPartChildren, $element_index]" as Expr, + "[$runtime_id.__DynamicPartListSlotV2, $element_index]" as Expr, runtime_id: Expr = runtime_id.clone(), element_index: Expr = i32_to_expr(&element_index), )), })); } - DynamicPart::Slot(jsx, element_index) => { - // snapshot_values.push(None); - snapshot_children.push(JSXElementChild::JSXElement(Box::new(jsx))); + DynamicPart::Slot(expr, element_index) => { + snapshot_children.push(expr); snapshot_slot_def.push(Some(ExprOrSpread { spread: None, expr: Box::new(quote!( - "[$runtime_id.__DynamicPartSlot, $element_index]" as Expr, + "[$runtime_id.__DynamicPartSlotV2, $element_index]" as Expr, runtime_id: Expr = runtime_id.clone(), element_index: Expr = i32_to_expr(&element_index), )), })); } - } - }); + }); Expr::Array(ArrayLit { span: DUMMY_SP, @@ -1624,40 +1539,46 @@ where self.current_snapshot_defs.push(entry_snapshot_uid_def); self.current_snapshot_defs.push(snapshot_def); - *node = JSXElement { - span: node.span(), - opening: JSXOpeningElement { - name: JSXElementName::Ident(snapshot_id.clone()), - span: node.span, - attrs: { - if snapshot_values_has_attr { - snapshot_attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr { - span: DUMMY_SP, - name: JSXAttrName::Ident(IdentName::new("values".into(), DUMMY_SP)), - value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer { + *node = + JSXElement { + span: node.span(), + opening: JSXOpeningElement { + name: JSXElementName::Ident(snapshot_id.clone()), + span: node.span, + attrs: { + if snapshot_values_has_attr { + snapshot_attrs.push(JSXAttrOrSpread::JSXAttr(JSXAttr { span: DUMMY_SP, - expr: JSXExpr::Expr(Box::new(Expr::Array(ArrayLit { + name: JSXAttrName::Ident(IdentName::new("values".into(), DUMMY_SP)), + value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer { + span: DUMMY_SP, + expr: JSXExpr::Expr(Box::new(Expr::Array(ArrayLit { + span: DUMMY_SP, + elems: snapshot_values, + }))), + })), + })) + }; + snapshot_attrs.extend(snapshot_children.iter_mut().enumerate().map( + |(index, child)| { + JSXAttrOrSpread::JSXAttr(JSXAttr { span: DUMMY_SP, - elems: snapshot_values, - }))), - })), - })) - }; - snapshot_attrs + name: JSXAttrName::Ident(IdentName::new(format!("${index}").into(), DUMMY_SP)), + value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer { + span: DUMMY_SP, + expr: JSXExpr::Expr(Box::new(child.take())), + })), + }) + }, + )); + snapshot_attrs + }, + self_closing: true, + type_args: None, }, - self_closing: wrap_dynamic_part.dynamic_part_count == 0, - type_args: None, - }, - children: snapshot_children, - closing: if wrap_dynamic_part.dynamic_part_count == 0 { - None - } else { - Some(JSXClosingElement { - name: JSXElementName::Ident(snapshot_id.clone()), - span: DUMMY_SP, - }) - }, - }; + children: vec![], + closing: None, + }; } fn visit_mut_module_items(&mut self, n: &mut Vec) { @@ -2289,96 +2210,6 @@ mod tests { "# ); - test!( - module, - Syntax::Es(EsSyntax { - jsx: true, - ..Default::default() - }), - |t| visit_mut_pass(JSXTransformer::new( - super::JSXTransformerConfig { - preserve_jsx: true, - ..Default::default() - }, - Some(t.comments.clone()), - TransformMode::Test, - Some(t.cm.clone()), - )), - basic_list, - // Input codes - r#" - - - - - - ; - "# - ); - - test!( - module, - Syntax::Es(EsSyntax { - jsx: true, - ..Default::default() - }), - |t| visit_mut_pass(JSXTransformer::new( - super::JSXTransformerConfig { - preserve_jsx: true, - ..Default::default() - }, - Some(t.comments.clone()), - TransformMode::Test, - Some(t.cm.clone()), - )), - basic_list_with_fragment, - // Input codes - r#" - - - <> - - - - - - ; - "# - ); - - test!( - module, - Syntax::Es(EsSyntax { - jsx: true, - ..Default::default() - }), - |_| { - let unresolved_mark = Mark::new(); - let top_level_mark = Mark::new(); - - ( - resolver(unresolved_mark, top_level_mark, true), - visit_mut_pass(JSXTransformer::<&SingleThreadedComments>::new( - super::JSXTransformerConfig { - preserve_jsx: true, - ..Default::default() - }, - None, - TransformMode::Test, - None, - )), - ) - }, - basic_list_toplevel, - // Input codes - r#" - - !!! - !!! - - "# - ); - test!( module, Syntax::Es(EsSyntax { diff --git a/packages/react/transform/crates/swc_plugin_snapshot/slot_marker.rs b/packages/react/transform/crates/swc_plugin_snapshot/slot_marker.rs deleted file mode 100644 index 4078e0b4b6..0000000000 --- a/packages/react/transform/crates/swc_plugin_snapshot/slot_marker.rs +++ /dev/null @@ -1,271 +0,0 @@ -use swc_core::{ - common::{util::take::Take, DUMMY_SP}, - ecma::{ - ast::{JSXExpr, *}, - visit::{VisitMut, VisitMutWith}, - }, -}; - -use super::WRAPPER_NODE_2; - -use swc_plugins_shared::jsx_helpers::{ - jsx_children_to_expr, jsx_has_dynamic_key, jsx_is_children_full_dynamic, jsx_is_custom, - jsx_is_list, jsx_name, jsx_text_to_str, -}; - -pub static INTERNAL_SLOT_STR: &str = "internal-slot"; - -pub fn jsx_is_internal_slot(jsx: &JSXElement) -> bool { - match *jsx_name(jsx.opening.name.clone()) { - Expr::Lit(Lit::Str(s)) => s.value.to_string_lossy().as_ref() == INTERNAL_SLOT_STR, - _ => false, - } -} - -pub fn jsx_unwrap_internal_slot(mut jsx: JSXElement) -> JSXElement { - if jsx_is_internal_slot(&jsx) { - if let Some(JSXElementChild::JSXElement(jsx)) = jsx.children.first_mut() { - return *jsx.take(); - } - } - unreachable!("unwrap_internal_slot"); -} - -fn jsx_wrapped(with: &str, n: JSXElement) -> JSXElement { - JSXElement { - span: DUMMY_SP, - opening: JSXOpeningElement { - span: DUMMY_SP, - name: JSXElementName::Ident(IdentName::new(with.into(), DUMMY_SP).into()), - attrs: vec![], - self_closing: false, - type_args: None, - }, - closing: Some(JSXClosingElement { - span: DUMMY_SP, - name: JSXElementName::Ident(IdentName::new(with.into(), DUMMY_SP).into()), - }), - children: vec![JSXElementChild::JSXElement(Box::new(n))], - } -} - -// Wrap dynamic part with wrapper node (or if it's children is full dynamic, do nothing) -// after this pass, all dynamic part will be wrapped with wrapper node -pub struct WrapperMarker { - pub current_is_children_full_dynamic: bool, - pub dynamic_part_count: i32, -} - -impl VisitMut for WrapperMarker { - fn visit_mut_jsx_element_childs(&mut self, n: &mut Vec) { - if self.current_is_children_full_dynamic { - return; - } - - if n.is_empty() { - return; - } - - // merge dynamic parts together to reduce wrapper node count - - let mut merged_children: Vec = vec![]; - let mut current_chunk: Vec = vec![]; - for mut child in n.take() { - let should_merge: bool; - match child { - JSXElementChild::JSXText(ref text) => { - if jsx_text_to_str(&text.value).is_empty() { - should_merge = current_chunk.is_empty(); - } else { - should_merge = true; - } - } - JSXElementChild::JSXElement(ref element) => { - should_merge = !jsx_is_custom(element); - } - JSXElementChild::JSXExprContainer(JSXExprContainer { - expr: JSXExpr::Expr(ref _expr), - .. - }) => { - should_merge = false; - } - JSXElementChild::JSXFragment(_) - | JSXElementChild::JSXExprContainer(JSXExprContainer { - expr: JSXExpr::JSXEmptyExpr(_), - .. - }) => { - should_merge = true; - } - JSXElementChild::JSXSpreadChild(_) => { - unreachable!("JSXSpreadChild is not supported yet"); - } - } - - if should_merge { - if !current_chunk.is_empty() { - let child = JSXElementChild::JSXElement(Box::new({ - let mut el = WRAPPER_NODE_2.clone(); - el.children = current_chunk.take(); - jsx_wrapped(INTERNAL_SLOT_STR, el) - })); - merged_children.push(child); - self.dynamic_part_count += 1; - } - - child.visit_mut_with(self); - merged_children.push(child); - } else { - current_chunk.push(child); - } - } - - if !current_chunk.is_empty() { - let child = JSXElementChild::JSXElement(Box::new({ - let mut el = WRAPPER_NODE_2.clone(); - el.children = current_chunk.take(); - jsx_wrapped(INTERNAL_SLOT_STR, el) - })); - merged_children.push(child); - self.dynamic_part_count += 1; - } - - *n = merged_children; - } - - fn visit_mut_jsx_element(&mut self, n: &mut JSXElement) { - if jsx_is_custom(n) { - // always ignore top level custom element - } else { - // let is_children_full_static = jsx_is_children_full_static(&n); - // let is_list = jsx_is_list(&n) || !is_children_full_static; - // let is_children_full_dynamic = is_list || jsx_is_children_full_dynamic(&n); - - let is_list = jsx_is_list(n); - let is_children_full_dynamic = is_list || jsx_is_children_full_dynamic(n); - let has_dynamic_key = jsx_has_dynamic_key(n); - - if (is_list || has_dynamic_key) && !n.children.is_empty() { - n.children = vec![JSXElementChild::JSXExprContainer(JSXExprContainer { - span: DUMMY_SP, - expr: JSXExpr::Expr(Box::new(jsx_children_to_expr(n.children.take()))), - })]; - } - - if is_children_full_dynamic { - self.dynamic_part_count += 1; - *n = jsx_wrapped(INTERNAL_SLOT_STR, n.take()); - } - - let pre_is_children_full_dynamic = self.current_is_children_full_dynamic; - self.current_is_children_full_dynamic = is_children_full_dynamic; - n.visit_mut_children_with(self); - self.current_is_children_full_dynamic = pre_is_children_full_dynamic; - } - } -} - -#[cfg(test)] -mod tests { - use swc_core::{ - common::Mark, - ecma::parser::Syntax, - ecma::{parser::EsSyntax, transforms::testing::test}, - ecma::{transforms::base::resolver, visit::visit_mut_pass}, - }; - - test!( - module, - Syntax::Es(EsSyntax { - jsx: true, - ..Default::default() - }), - |_| { - let unresolved_mark = Mark::new(); - let top_level_mark = Mark::new(); - - ( - resolver(unresolved_mark, top_level_mark, true), - visit_mut_pass(super::WrapperMarker { - current_is_children_full_dynamic: false, - dynamic_part_count: 0, - }), - ) - }, - should_wrap_dynamic_part, - r#" - ; // should not handle top-level JSXElement - ; // should not handle top-level JSXElement - ; - ; // children is full dynamic - ; // children is full dynamic - ; // should be wrapped inside wrapper - {1}; - {1}{2}; - {1}2; - ; - A; - {}; - a; - hello; - {hello}; - {hello}; - Hello, ReactLynx, {hello}{hello}; - - !!! - -; - - !!! - {a} -; - "# - ); - - test!( - module, - Syntax::Es(EsSyntax { - jsx: true, - ..Default::default() - }), - |_| { - let unresolved_mark = Mark::new(); - let top_level_mark = Mark::new(); - - ( - resolver(unresolved_mark, top_level_mark, true), - visit_mut_pass(super::WrapperMarker { - current_is_children_full_dynamic: false, - dynamic_part_count: 0, - }), - ) - }, - should_not_wrap_dynamic_part, - r#" - - - -; - - - - - -; - - - {/** foo */} - - - {/** bar */} - -; -// TODO: fix the redundant here - - {[].map(() => null)} - {[].map(() => null)} -; - -; - "# - ); -} diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component.js index 34b8874491..6f96b2aa68 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component.js @@ -6,5 +6,5 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 return [ el ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); -<__snapshot_da39a_test_1>; + }, null, ReactLynx.__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); +<__snapshot_da39a_test_1 $0={}/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling.js index 0b7574bf9b..f8211c9c47 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling.js @@ -17,8 +17,8 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 ]; }, null, [ [ - ReactLynx.__DynamicPartChildren, + ReactLynx.__DynamicPartSlotV2, 3 ] ], undefined, globDynamicComponentEntry, null, true); -<__snapshot_da39a_test_1>; +<__snapshot_da39a_test_1 $0={}/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling_jsx.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling_jsx.js index ae8f527ed0..fe68906189 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling_jsx.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling_jsx.js @@ -18,10 +18,10 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 ]; }, null, [ [ - ReactLynx.__DynamicPartChildren, + ReactLynx.__DynamicPartSlotV2, 3 ] ], undefined, globDynamicComponentEntry, null, true); _jsx(__snapshot_da39a_test_1, { - children: _jsx(A, {}) + $0: _jsx(A, {}) }); diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling_jsx_dev.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling_jsx_dev.js index dd406b7841..36bcc8ec63 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling_jsx_dev.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_component_with_static_sibling_jsx_dev.js @@ -18,12 +18,12 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 ]; }, null, [ [ - ReactLynx.__DynamicPartChildren, + ReactLynx.__DynamicPartSlotV2, 3 ] ], undefined, globDynamicComponentEntry, null, true); _jsxDEV(__snapshot_da39a_test_1, { - children: _jsxDEV(A, {}, void 0, false, { + $0: _jsxDEV(A, {}, void 0, false, { fileName: "input.js", lineNumber: 4, columnNumber: 7 diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_expr_container.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_expr_container.js index 73a85a244e..0e8eb01173 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_expr_container.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_expr_container.js @@ -6,5 +6,5 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 return [ el ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); -<__snapshot_da39a_test_1>{a}; + }, null, ReactLynx.__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); +<__snapshot_da39a_test_1 $0={a}/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_expr_container_with_static_sibling.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_expr_container_with_static_sibling.js index 9b6b4f75f0..a44b9362d9 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_expr_container_with_static_sibling.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/basic_expr_container_with_static_sibling.js @@ -17,8 +17,8 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 ]; }, null, [ [ - ReactLynx.__DynamicPartChildren, + ReactLynx.__DynamicPartSlotV2, 3 ] ], undefined, globDynamicComponentEntry, null, true); -<__snapshot_da39a_test_1>{a}; +<__snapshot_da39a_test_1 $0={a}/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/full_static_children_map_jsx.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/full_static_children_map_jsx.js index e5b5ceb03c..8c7e7cb447 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/full_static_children_map_jsx.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/full_static_children_map_jsx.js @@ -1,30 +1,14 @@ import * as ReactLynx from "@lynx-js/react"; -const __snapshot_da39a_test_2 = "__snapshot_da39a_test_2"; -ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_2] = (__snapshot_da39a_test_2)=>ReactLynx.createSnapshot(__snapshot_da39a_test_2, function() { - const pageId = ReactLynx.__pageId; - const el = __CreateView(pageId); - __SetClasses(el, "child"); - return [ - el - ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); -const __snapshot_da39a_test_3 = "__snapshot_da39a_test_3"; -ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_3] = (__snapshot_da39a_test_3)=>ReactLynx.createSnapshot(__snapshot_da39a_test_3, function() { - const pageId = ReactLynx.__pageId; - const el = __CreateView(pageId); - __SetClasses(el, "child"); - return [ - el - ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); const __snapshot_da39a_test_1 = "__snapshot_da39a_test_1"; ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1)=>ReactLynx.createSnapshot(__snapshot_da39a_test_1, function() { const pageId = ReactLynx.__pageId; const el = __CreateView(pageId); __SetClasses(el, "parent"); - const el1 = __CreateWrapperElement(pageId); + const el1 = __CreateView(pageId); + __SetClasses(el1, "child"); __AppendElement(el, el1); - const el2 = __CreateWrapperElement(pageId); + const el2 = __CreateView(pageId); + __SetClasses(el2, "child"); __AppendElement(el, el2); return [ el, @@ -33,12 +17,12 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 ]; }, null, [ [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 1 ], [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 2 ] ], undefined, globDynamicComponentEntry, null, true); -<__snapshot_da39a_test_1><__snapshot_da39a_test_2>{[].map(()=>null)}<__snapshot_da39a_test_3>{[].map(()=>null)}; +<__snapshot_da39a_test_1 $0={[].map(()=>null)} $1={[].map(()=>null)}/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_component.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_component.js index b41f282c20..56d7b959cf 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_component.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_component.js @@ -6,10 +6,10 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 return [ el ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); + }, null, ReactLynx.__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); - <__snapshot_da39a_test_1>{[ + <__snapshot_da39a_test_1 $0={[ , -]} +]}/> ; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_element.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_element.js index b7cb26752f..0c83173034 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_element.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_element.js @@ -7,10 +7,10 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 return [ el ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); + }, null, ReactLynx.__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); - <__snapshot_da39a_test_1>{[ + <__snapshot_da39a_test_1 $0={[ , -]} +]}/> ; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_element_dev.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_element_dev.js index 40de100ca9..21f3dcf626 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_element_dev.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/page_element_dev.js @@ -6,10 +6,10 @@ require('@lynx-js/react/internal').snapshotCreatorMap[__snapshot_da39a_test_1] = return [ el ]; - }, null, require('@lynx-js/react/internal').__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); + }, null, require('@lynx-js/react/internal').__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); - <__snapshot_da39a_test_1>{[ + <__snapshot_da39a_test_1 $0={[ , -]} +]}/> ; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_create_raw_text_node_for_text_node.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_create_raw_text_node_for_text_node.js index ea2af8df9b..975a77056e 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_create_raw_text_node_for_text_node.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_create_raw_text_node_for_text_node.js @@ -1,12 +1,4 @@ import * as ReactLynx from "@lynx-js/react"; -const __snapshot_da39a_test_2 = "__snapshot_da39a_test_2"; -ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_2] = (__snapshot_da39a_test_2)=>ReactLynx.createSnapshot(__snapshot_da39a_test_2, function() { - const pageId = ReactLynx.__pageId; - const el = __CreateText(pageId); - return [ - el - ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); const __snapshot_da39a_test_1 = "__snapshot_da39a_test_1"; ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1)=>ReactLynx.createSnapshot(__snapshot_da39a_test_1, function() { const pageId = ReactLynx.__pageId; @@ -17,7 +9,7 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 __AppendElement(el1, el2); const el3 = __CreateRawText(", ReactLynx 1"); __AppendElement(el1, el3); - const el4 = __CreateWrapperElement(pageId); + const el4 = __CreateText(pageId); __AppendElement(el, el4); const el5 = __CreateText(pageId); __AppendElement(el, el5); @@ -44,12 +36,12 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 ]; }, null, [ [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 2 ], [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 4 ] ], undefined, globDynamicComponentEntry, null, true); -<__snapshot_da39a_test_1>{hello}<__snapshot_da39a_test_2>{hello}; +<__snapshot_da39a_test_1 $0={hello} $1={hello}/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_inject_implicit_flatten.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_inject_implicit_flatten.js index 224772732b..193189442c 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_inject_implicit_flatten.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_inject_implicit_flatten.js @@ -1,13 +1,4 @@ import * as ReactLynx from "@lynx-js/react"; -const __snapshot_da39a_test_2 = "__snapshot_da39a_test_2"; -ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_2] = (__snapshot_da39a_test_2)=>ReactLynx.createSnapshot(__snapshot_da39a_test_2, function() { - const pageId = ReactLynx.__pageId; - const el = __CreateView(pageId); - __SetClasses(el, 'commdityV1TextVerticalWrapper'); - return [ - el - ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); const __snapshot_da39a_test_1 = "__snapshot_da39a_test_1"; ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1)=>ReactLynx.createSnapshot(__snapshot_da39a_test_1, function() { const pageId = ReactLynx.__pageId; @@ -21,7 +12,8 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 const el3 = __CreateView(pageId); __SetClasses(el3, 'commdityV1TextWrapper'); __AppendElement(el1, el3); - const el4 = __CreateWrapperElement(pageId); + const el4 = __CreateView(pageId); + __SetClasses(el4, 'commdityV1TextVerticalWrapper'); __AppendElement(el3, el4); const el5 = __CreateWrapperElement(pageId); __AppendElement(el3, el5); @@ -44,24 +36,24 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 } ], [ [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 4 ], [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 5 ], [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 6 ] ], undefined, globDynamicComponentEntry, null, true); <__snapshot_da39a_test_1 values={[ id -]}><__snapshot_da39a_test_2>{[ +]} $0={[ , desc -]}{unit} - {unit} - {unit} - ; +]} $1={unit} $2={[ + unit, + unit +]}/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_wrap_dynamic_key.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_wrap_dynamic_key.js index de6143b49c..ea3c1a0ed2 100644 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_wrap_dynamic_key.js +++ b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/lib.rs/should_wrap_dynamic_key.js @@ -6,15 +6,7 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_2] = (__snapshot_da39a_test_2 return [ el ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); -const __snapshot_da39a_test_3 = "__snapshot_da39a_test_3"; -ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_3] = (__snapshot_da39a_test_3)=>ReactLynx.createSnapshot(__snapshot_da39a_test_3, function() { - const pageId = ReactLynx.__pageId; - const el = __CreateText(pageId); - return [ - el - ]; - }, null, ReactLynx.__DynamicPartChildren_0, undefined, globDynamicComponentEntry, null, true); + }, null, ReactLynx.__DynamicPartSlotV2_0, undefined, globDynamicComponentEntry, null, true); const __snapshot_da39a_test_1 = "__snapshot_da39a_test_1"; ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1)=>ReactLynx.createSnapshot(__snapshot_da39a_test_1, function() { const pageId = ReactLynx.__pageId; @@ -27,7 +19,7 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 __AppendElement(el1, el3); const el4 = __CreateWrapperElement(pageId); __AppendElement(el, el4); - const el5 = __CreateWrapperElement(pageId); + const el5 = __CreateText(pageId); __AppendElement(el, el5); return [ el, @@ -39,16 +31,16 @@ ReactLynx.snapshotCreatorMap[__snapshot_da39a_test_1] = (__snapshot_da39a_test_1 ]; }, null, [ [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 3 ], [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 4 ], [ - ReactLynx.__DynamicPartSlot, + ReactLynx.__DynamicPartSlotV2, 5 ] ], undefined, globDynamicComponentEntry, null, true); -<__snapshot_da39a_test_1>{hello}<__snapshot_da39a_test_2 key={hello}>{hello}<__snapshot_da39a_test_3 key="hello">{hello}; +<__snapshot_da39a_test_1 $0={hello} $1={<__snapshot_da39a_test_2 key={hello} $0={hello}/>} $2={hello}/>; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/slot_marker.rs/should_not_wrap_dynamic_part.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/slot_marker.rs/should_not_wrap_dynamic_part.js deleted file mode 100644 index 14ddbea535..0000000000 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/slot_marker.rs/should_not_wrap_dynamic_part.js +++ /dev/null @@ -1,29 +0,0 @@ - - - -; - - - - - -; - - - { /** foo */ } - - - { /** bar */ } - -; -// TODO: fix the redundant here - - {[].map(()=>null)} - {[].map(()=>null)} -; - -; diff --git a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/slot_marker.rs/should_wrap_dynamic_part.js b/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/slot_marker.rs/should_wrap_dynamic_part.js deleted file mode 100644 index 36509cca04..0000000000 --- a/packages/react/transform/crates/swc_plugin_snapshot/tests/__swc_snapshots__/slot_marker.rs/should_wrap_dynamic_part.js +++ /dev/null @@ -1,34 +0,0 @@ -; // should not handle top-level JSXElement -; // should not handle top-level JSXElement -; -; // children is full dynamic -; // children is full dynamic -; // should be wrapped inside wrapper -{1}; -{1}{2}; -{1}2; -{[ - , - -]}; -{[ - A, - -]}; -{}; -{[ - , - -]}a; -{"hello"}; -{hello}; -{hello}; -Hello, ReactLynx, {hello}{hello}; - - !!! - -; - - !!! - {a} -; diff --git a/packages/rspeedy/plugin-react/package.json b/packages/rspeedy/plugin-react/package.json index 52c95c1221..ca43218283 100644 --- a/packages/rspeedy/plugin-react/package.json +++ b/packages/rspeedy/plugin-react/package.json @@ -67,7 +67,7 @@ "typia-rspack-plugin": "2.2.2" }, "peerDependencies": { - "@lynx-js/react": "^0.103.0 || ^0.104.0 || ^0.105.0 || ^0.106.0 || ^0.107.0 || ^0.108.0 || ^0.109.0 || ^0.110.0 || ^0.111.0 || ^0.112.0 || ^0.113.0 || ^0.114.0 || ^0.115.0 || ^0.116.0 || ^0.117.0 || ^0.118.0 || ^0.119.0" + "@lynx-js/react": "^0.103.0 || ^0.104.0 || ^0.105.0 || ^0.106.0 || ^0.107.0 || ^0.108.0 || ^0.109.0 || ^0.110.0 || ^0.111.0 || ^0.112.0 || ^0.113.0 || ^0.114.0 || ^0.115.0 || ^0.116.0 || ^0.117.0 || ^0.118.0 || ^0.119.0 || ^0.120.0" }, "peerDependenciesMeta": { "@lynx-js/react": { diff --git a/packages/testing-library/examples/react-compiler/src/__tests__/hello.test.jsx b/packages/testing-library/examples/react-compiler/src/__tests__/hello.test.jsx index a1100b67c6..fc28c40116 100644 --- a/packages/testing-library/examples/react-compiler/src/__tests__/hello.test.jsx +++ b/packages/testing-library/examples/react-compiler/src/__tests__/hello.test.jsx @@ -30,18 +30,21 @@ test('hello', () => { + Item - Item 1 + 1 + Item - Item 2 + 2 + Item - Item 3 + 3 @@ -62,18 +65,21 @@ test('hello', () => { + Item - Item 1 + 1 + Item - Item 2 + 2 + Item - Item 3 + 3 diff --git a/packages/web-platform/web-core-e2e/server-tests/__snapshots__/server-e2e.test.ts.snap b/packages/web-platform/web-core-e2e/server-tests/__snapshots__/server-e2e.test.ts.snap index 13118992ad..3ec30c69af 100644 --- a/packages/web-platform/web-core-e2e/server-tests/__snapshots__/server-e2e.test.ts.snap +++ b/packages/web-platform/web-core-e2e/server-tests/__snapshots__/server-e2e.test.ts.snap @@ -312,8 +312,9 @@ exports[`executeTemplate should run lepusCode.root from basic-element-x-input-va > " diff --git a/packages/webpack/react-refresh-webpack-plugin/test/hotCases/hook/useContext/__snapshot__/rspack/1.snap.txt b/packages/webpack/react-refresh-webpack-plugin/test/hotCases/hook/useContext/__snapshot__/rspack/1.snap.txt index 3fb3688c92..e071c6a0fc 100644 --- a/packages/webpack/react-refresh-webpack-plugin/test/hotCases/hook/useContext/__snapshot__/rspack/1.snap.txt +++ b/packages/webpack/react-refresh-webpack-plugin/test/hotCases/hook/useContext/__snapshot__/rspack/1.snap.txt @@ -6,7 +6,7 @@ ## Asset Files - Bundle: rspack-bundle.js - Manifest: main.LAST_HASH.hot-update.json, size: 28 -- Update: main.LAST_HASH.hot-update.js, size: 4749 +- Update: main.LAST_HASH.hot-update.js, size: 4739 ## Manifest @@ -65,7 +65,7 @@ const __snapshot_4953c_6688c_1 = "__snapshot_4953c_6688c_1"; ]; }, null, [ [ - (__webpack_require__(/*! @lynx-js/react/internal */ "../../../../../../react/runtime/lib/internal.js")/* .__DynamicPartChildren */.__DynamicPartChildren), + (__webpack_require__(/*! @lynx-js/react/internal */ "../../../../../../react/runtime/lib/internal.js")/* .__DynamicPartSlotV2 */.__DynamicPartSlotV2), 2 ] ], undefined, globDynamicComponentEntry, null, true); @@ -73,7 +73,7 @@ function Inner() { const theme = (0,_lynx_js_react__rspack_import_1.useContext)(Theme); __RenderContent(`after: ${theme}`); return /*#__PURE__*/ (0,_lynx_js_react_jsx_dev_runtime__rspack_import_0.jsxDEV)(__snapshot_4953c_6688c_1, { - children: theme + $0: theme }, void 0, false, { fileName: "/packages/webpack/react-refresh-webpack-plugin/test/hotCases/hook/useContext/app.jsx", lineNumber: 8, diff --git a/packages/webpack/react-refresh-webpack-plugin/test/hotCases/hook/useState/__snapshot__/rspack/1.snap.txt b/packages/webpack/react-refresh-webpack-plugin/test/hotCases/hook/useState/__snapshot__/rspack/1.snap.txt index 38557c0131..c4c5e42b62 100644 --- a/packages/webpack/react-refresh-webpack-plugin/test/hotCases/hook/useState/__snapshot__/rspack/1.snap.txt +++ b/packages/webpack/react-refresh-webpack-plugin/test/hotCases/hook/useState/__snapshot__/rspack/1.snap.txt @@ -6,7 +6,7 @@ ## Asset Files - Bundle: rspack-bundle.js - Manifest: main.LAST_HASH.hot-update.json, size: 28 -- Update: main.LAST_HASH.hot-update.js, size: 4363 +- Update: main.LAST_HASH.hot-update.js, size: 4353 ## Manifest @@ -64,14 +64,14 @@ const __snapshot_4953c_43c2c_1 = "__snapshot_4953c_43c2c_1"; ]; }, null, [ [ - (__webpack_require__(/*! @lynx-js/react/internal */ "../../../../../../react/runtime/lib/internal.js")/* .__DynamicPartChildren */.__DynamicPartChildren), + (__webpack_require__(/*! @lynx-js/react/internal */ "../../../../../../react/runtime/lib/internal.js")/* .__DynamicPartSlotV2 */.__DynamicPartSlotV2), 2 ] ], undefined, globDynamicComponentEntry, null, true); function Inner({ theme }) { __RenderContent(`after: ${theme}`); return /*#__PURE__*/ (0,_lynx_js_react_jsx_dev_runtime__rspack_import_0.jsxDEV)(__snapshot_4953c_43c2c_1, { - children: theme + $0: theme }, void 0, false, { fileName: "/packages/webpack/react-refresh-webpack-plugin/test/hotCases/hook/useState/app.jsx", lineNumber: 5, diff --git a/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/__snapshot__/rspack/1.snap.txt b/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/__snapshot__/rspack/1.snap.txt index 81f32e1756..2edb724d3e 100644 --- a/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/__snapshot__/rspack/1.snap.txt +++ b/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/__snapshot__/rspack/1.snap.txt @@ -6,7 +6,7 @@ ## Asset Files - Bundle: rspack-bundle.js - Manifest: main.LAST_HASH.hot-update.json, size: 28 -- Update: main.LAST_HASH.hot-update.js, size: 3700 +- Update: main.LAST_HASH.hot-update.js, size: 3690 ## Manifest @@ -59,14 +59,14 @@ const __snapshot_4953c_170ee_1 = "__snapshot_4953c_170ee_1"; ]; }, null, [ [ - (__webpack_require__(/*! @lynx-js/react/internal */ "../../../../../../react/runtime/lib/internal.js")/* .__DynamicPartChildren */.__DynamicPartChildren), + (__webpack_require__(/*! @lynx-js/react/internal */ "../../../../../../react/runtime/lib/internal.js")/* .__DynamicPartSlotV2 */.__DynamicPartSlotV2), 1 ] ], undefined, globDynamicComponentEntry, null, true); function App() { const value = "content 2"; return __RenderContent(/*#__PURE__*/ (0,_lynx_js_react_jsx_dev_runtime__rspack_import_0.jsxDEV)(__snapshot_4953c_170ee_1, { - children: value + $0: value }, void 0, false, { fileName: "/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/app.jsx", lineNumber: 3, diff --git a/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/__snapshot__/rspack/2.snap.txt b/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/__snapshot__/rspack/2.snap.txt index 028123f77e..e7397e1bc3 100644 --- a/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/__snapshot__/rspack/2.snap.txt +++ b/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/__snapshot__/rspack/2.snap.txt @@ -6,7 +6,7 @@ ## Asset Files - Bundle: rspack-bundle.js - Manifest: main.LAST_HASH.hot-update.json, size: 28 -- Update: main.LAST_HASH.hot-update.js, size: 3700 +- Update: main.LAST_HASH.hot-update.js, size: 3690 ## Manifest @@ -59,14 +59,14 @@ const __snapshot_4953c_00662_1 = "__snapshot_4953c_00662_1"; ]; }, null, [ [ - (__webpack_require__(/*! @lynx-js/react/internal */ "../../../../../../react/runtime/lib/internal.js")/* .__DynamicPartChildren */.__DynamicPartChildren), + (__webpack_require__(/*! @lynx-js/react/internal */ "../../../../../../react/runtime/lib/internal.js")/* .__DynamicPartSlotV2 */.__DynamicPartSlotV2), 1 ] ], undefined, globDynamicComponentEntry, null, true); function App() { const value = "content 3"; return __RenderContent(/*#__PURE__*/ (0,_lynx_js_react_jsx_dev_runtime__rspack_import_0.jsxDEV)(__snapshot_4953c_00662_1, { - children: value + $0: value }, void 0, false, { fileName: "/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/app.jsx", lineNumber: 3, diff --git a/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/index.jsx b/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/index.jsx index 5e72eacc0a..ce2ebf705f 100644 --- a/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/index.jsx +++ b/packages/webpack/react-refresh-webpack-plugin/test/hotCases/jsx/value/index.jsx @@ -5,7 +5,17 @@ import { App } from "./app.jsx"; const createFn = vi.fn(); vi.stubGlobal("__RenderContent", (vnode) => { - createFn(vnode.props.children); + let { children } = vnode.props; + for (let key in vnode.props) { + if (typeof key === 'string' && key[0] === '$') { + children ??= []; + children[+(key.slice(1))] = vnode.props[key]; + } + } + if (children != null && !Array.isArray(children)) { + children = [children]; + } + createFn(children); return vnode; }); @@ -40,10 +50,10 @@ lynxCoreInject.tt.OnLifecycleEvent([ it("should call renderer", () => new Promise((done) => { - expect(createFn.mock.calls[0][0]).toEqual("content 1"); + expect(createFn.mock.calls[0][0]).toEqual(["content 1"]); NEXT( update(done, true, () => { - expect(createFn.mock.calls[1][0]).toEqual("content 2"); + expect(createFn.mock.calls[1][0]).toEqual(["content 2"]); const { patchList } = JSON.parse( callLepusMethod.mock.calls[1][1].data @@ -62,7 +72,7 @@ it("should call renderer", () => NEXT( update(done, true, () => { - expect(createFn.mock.calls[2][0]).toEqual("content 3"); + expect(createFn.mock.calls[2][0]).toEqual(["content 3"]); const { snapshotPatch } = JSON.parse( callLepusMethod.mock.calls[2][1].data ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae6ccabd4f..f301064c5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -638,8 +638,8 @@ importers: packages/react: dependencies: preact: - specifier: npm:@lynx-js/internal-preact@10.28.4-2d1d67a - version: '@lynx-js/internal-preact@10.28.4-2d1d67a' + specifier: npm:@lynx-js/internal-preact@10.28.4-dfff9aa + version: '@lynx-js/internal-preact@10.28.4-dfff9aa' devDependencies: '@lynx-js/types': specifier: 3.7.0 @@ -2375,8 +2375,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx@7.28.6': - resolution: {integrity: sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==} + '@babel/plugin-transform-react-jsx@7.27.1': + resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -3272,8 +3272,8 @@ packages: '@lynx-js/react': '*' '@lynx-js/types': '*' - '@lynx-js/internal-preact@10.28.4-2d1d67a': - resolution: {integrity: sha512-CA8rFURvLvksg155ONHZHEa0ZIxWnS4H/UP8vp5PF8aNOFd3D3Z+Lq+0IQP5GyE/9x+jMIvbBXaY3JVVknSphQ==} + '@lynx-js/internal-preact@10.28.4-dfff9aa': + resolution: {integrity: sha512-i2xFwaWsmfODVfb9oYurZWCBDlnOdXdP/IsfW/R8i9vLtV6eo7I73U4smBINSNtQfSwpdQHooslEFD94RHVqSw==} '@lynx-js/lynx-core@0.1.3': resolution: {integrity: sha512-uWzKKYJUK4Q09ZRZxWSAFINnmZb9piWPbvWF9SkLn+3snBl9u/BJa4ekPRcKWAhBmpbtxWH1x27fxe3Q3p5l3Q==} @@ -4457,8 +4457,8 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sinclair/typebox@0.34.38': - resolution: {integrity: sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==} + '@sinclair/typebox@0.34.37': + resolution: {integrity: sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==} '@sindresorhus/merge-streams@4.0.0': resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} @@ -5661,9 +5661,9 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@5.2.1: - resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} - engines: {node: '>=18'} + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -11004,11 +11004,11 @@ snapshots: '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 @@ -11042,7 +11042,7 @@ snapshots: '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-option': 7.27.1 '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.29.0) transitivePeerDependencies: @@ -11700,7 +11700,7 @@ snapshots: '@jest/schemas@30.0.5': dependencies: - '@sinclair/typebox': 0.34.38 + '@sinclair/typebox': 0.34.37 '@jest/transform@29.7.0': dependencies: @@ -11887,7 +11887,7 @@ snapshots: '@lynx-js/react': link:packages/react '@lynx-js/types': 3.7.0 - '@lynx-js/internal-preact@10.28.4-2d1d67a': {} + '@lynx-js/internal-preact@10.28.4-dfff9aa': {} '@lynx-js/lynx-core@0.1.3': {} @@ -13460,7 +13460,7 @@ snapshots: '@sinclair/typebox@0.27.8': {} - '@sinclair/typebox@0.34.38': {} + '@sinclair/typebox@0.34.37': {} '@sindresorhus/merge-streams@4.0.0': {} @@ -14164,7 +14164,7 @@ snapshots: '@types/chai': 5.2.3 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 - chai: 5.2.1 + chai: 5.2.0 tinyrainbow: 2.0.0 '@vitest/mocker@3.2.4(vite@5.4.2(@types/node@24.10.13)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.31.6))': @@ -14738,7 +14738,7 @@ snapshots: ccount@2.0.1: {} - chai@5.2.1: + chai@5.2.0: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 @@ -20196,7 +20196,7 @@ snapshots: '@vitest/snapshot': 3.2.4 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 - chai: 5.2.1 + chai: 5.2.0 debug: 4.4.3 expect-type: 1.2.1 magic-string: 0.30.21