From b45154f10a55a678c0a31fdde8646ca4455ebbdb Mon Sep 17 00:00:00 2001 From: ph1p Date: Sun, 25 Aug 2024 19:25:22 +0200 Subject: [PATCH] feat: serialize signals in object --- .../src/components/SignalsInArray.jsx | 2 +- .../src/components/SignalsInObject.jsx | 8 +++++++ .../preact-component/src/pages/signals.astro | 2 ++ packages/astro/test/preact-component.test.js | 22 +++++++++++++++-- packages/integrations/preact/src/client.ts | 14 ++++++++--- packages/integrations/preact/src/signals.ts | 24 ++++++++++++------- packages/integrations/preact/src/types.ts | 2 +- 7 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 packages/astro/test/fixtures/preact-component/src/components/SignalsInObject.jsx diff --git a/packages/astro/test/fixtures/preact-component/src/components/SignalsInArray.jsx b/packages/astro/test/fixtures/preact-component/src/components/SignalsInArray.jsx index 07ee24adaafc1..69940f7306a7b 100644 --- a/packages/astro/test/fixtures/preact-component/src/components/SignalsInArray.jsx +++ b/packages/astro/test/fixtures/preact-component/src/components/SignalsInArray.jsx @@ -3,6 +3,6 @@ import { h } from 'preact'; export default ({ signalsArray }) => { return

{signalsArray[0]} {signalsArray[3]}

-

{signalsArray[1].value}{signalsArray[2].value}{signalsArray[4].value}

+

{signalsArray[1].value}-{signalsArray[2].value}-{signalsArray[4].value}

} diff --git a/packages/astro/test/fixtures/preact-component/src/components/SignalsInObject.jsx b/packages/astro/test/fixtures/preact-component/src/components/SignalsInObject.jsx new file mode 100644 index 0000000000000..6187ce8c59a36 --- /dev/null +++ b/packages/astro/test/fixtures/preact-component/src/components/SignalsInObject.jsx @@ -0,0 +1,8 @@ +import { h } from 'preact'; + +export default ({ signalsObject }) => { + return
+

{signalsObject.title}

+

{signalsObject.counter.value}

+
+} diff --git a/packages/astro/test/fixtures/preact-component/src/pages/signals.astro b/packages/astro/test/fixtures/preact-component/src/pages/signals.astro index 16d2efbe0c6e7..37b43a73c7eca 100644 --- a/packages/astro/test/fixtures/preact-component/src/pages/signals.astro +++ b/packages/astro/test/fixtures/preact-component/src/pages/signals.astro @@ -2,6 +2,7 @@ import { signal } from '@preact/signals'; import Signals from '../components/Signals'; import SignalsInArray from '../components/SignalsInArray'; +import SignalsInObject from '../components/SignalsInObject'; const count = signal(1); const secondCount = signal(2); --- @@ -13,5 +14,6 @@ const secondCount = signal(2); + diff --git a/packages/astro/test/preact-component.test.js b/packages/astro/test/preact-component.test.js index 8c84bf8e6c278..a7000d4722f7a 100644 --- a/packages/astro/test/preact-component.test.js +++ b/packages/astro/test/preact-component.test.js @@ -119,7 +119,25 @@ describe('Preact component', () => { ], }); - assert.equal($('.preact-signal-array h1').text(), "I'm not a signal 12345"); - assert.equal($('.preact-signal-array p').text(), '112'); + assert.equal(element.find('h1').text(), "I'm not a signal 12345"); + assert.equal(element.find('p').text(), '1-1-2'); + }); + + it('Can use signals in object', async () => { + const html = await fixture.readFile('/signals/index.html'); + const $ = cheerio.load(html); + const element = $('.preact-signal-object'); + assert.equal(element.length, 1); + + const sigs1Raw = $($('astro-island')[3]).attr('data-preact-signals'); + + const sigs1 = JSON.parse(sigs1Raw); + + assert.deepEqual(sigs1, { + signalsObject: [['p0', 'counter']], + }); + + assert.equal(element.find('h1').text(), "I am a title"); + assert.equal(element.find('p').text(), '1'); }); }); diff --git a/packages/integrations/preact/src/client.ts b/packages/integrations/preact/src/client.ts index 39ad1f0f6cf23..fa20a99283ecc 100644 --- a/packages/integrations/preact/src/client.ts +++ b/packages/integrations/preact/src/client.ts @@ -23,13 +23,21 @@ export default (element: HTMLElement) => ); for (const [propName, signalId] of Object.entries(signals)) { if (Array.isArray(signalId)) { - signalId.forEach(([id, indexInPropsArray]) => { - const [valueOfSignal, indexInProps] = props[propName][indexInPropsArray]; + signalId.forEach(([id, indexOrKeyInProps]) => { + const mapValue = props[propName][indexOrKeyInProps]; + let valueOfSignal = mapValue; + + // not an property key + if(typeof indexOrKeyInProps !== 'string') { + valueOfSignal = mapValue[0]; + indexOrKeyInProps = mapValue[1]; + } + if (!sharedSignalMap.has(id)) { const signalValue = signal(valueOfSignal); sharedSignalMap.set(id, signalValue); } - props[propName][indexInProps] = sharedSignalMap.get(id); + props[propName][indexOrKeyInProps] = sharedSignalMap.get(id); }); } else { if (!sharedSignalMap.has(signalId)) { diff --git a/packages/integrations/preact/src/signals.ts b/packages/integrations/preact/src/signals.ts index 89eaabd568dfa..c690f0bd614e8 100644 --- a/packages/integrations/preact/src/signals.ts +++ b/packages/integrations/preact/src/signals.ts @@ -28,18 +28,24 @@ export function serializeSignals( map: PropNameToSignalMap, ) { // Check for signals - const signals: Record = {}; + const signals: Record = {}; for (const [key, value] of Object.entries(props)) { - if (Array.isArray(value)) { - value.forEach((signal, index) => { - // find signals in array. The index is important! + const isPropArray = Array.isArray(value); + const isPropObject = !isSignal(value) && typeof props[key] === 'object' && !isPropArray; + + if (isPropObject || isPropArray) { + const values = isPropObject ? Object.keys(props[key]) : value; + values.forEach((valueKey: number | string, valueIndex: number) => { + const signal = isPropObject ? props[key][valueKey] : valueKey; if (isSignal(signal)) { - props[key] = props[key].map((v: SignalLike, i: number) => - i === index ? [signal.peek(), i] : v, - ); - map.set(key, [...((map.get(key) || []) as []), [signal, index]]); + const keyOrIndex = isPropObject ? valueKey.toString() : valueIndex; + + props[key] = isPropObject + ? Object.assign({}, props[key], { [keyOrIndex]: signal.peek() }) + : props[key].map((v: SignalLike, i: number) => (i === valueIndex ? signal.peek() : v)); + map.set(key, [...((map.get(key) || []) as []), [signal, keyOrIndex]]); - signals[key] = [...((signals[key] || []) as []), [getSignalId(ctx, signal), index]]; + signals[key] = [...((signals[key] || []) as []), [getSignalId(ctx, signal), keyOrIndex]]; } }); } else if (isSignal(value)) { diff --git a/packages/integrations/preact/src/types.ts b/packages/integrations/preact/src/types.ts index 03f09cb4538e1..ba17d3aa8cd92 100644 --- a/packages/integrations/preact/src/types.ts +++ b/packages/integrations/preact/src/types.ts @@ -7,7 +7,7 @@ export type SignalLike = { peek(): any; }; -export type PropNameToSignalMap = Map; +export type PropNameToSignalMap = Map; export type AstroPreactAttrs = { ['data-preact-signals']?: string;