Skip to content

Commit

Permalink
Fixes hydration of Maps and Sets (#3960)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewp authored Jul 18, 2022
1 parent 5fde2fd commit ceda294
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-bulldogs-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes hydration of maps/sets
12 changes: 11 additions & 1 deletion packages/astro/e2e/fixtures/pass-js/src/components/React.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import { useState } from 'react';
interface Props {
obj: BigNestedObject;
num: bigint;
arr: any[];
map: Map<string, string>;
set: Set<string>;
}

const isNode = typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]';

/** a counter written in React */
export default function Component({ obj, num, arr }: Props) {
export default function Component({ obj, num, arr, map, set }: Props) {
// We are testing hydration, so don't return anything in the server.
if(isNode) {
return <div></div>
Expand All @@ -24,6 +27,13 @@ export default function Component({ obj, num, arr }: Props) {
<span id="bigint-value">{num.toString()}</span>
<span id="arr-type">{Object.prototype.toString.call(arr)}</span>
<span id="arr-value">{arr.join(',')}</span>
<span id="map-type">{Object.prototype.toString.call(map)}</span>
<ul id="map-items">{Array.from(map).map(([key, value]) => (
<li>{key}: {value}</li>
))}
</ul>
<span id="set-type">{Object.prototype.toString.call(set)}</span>
<span id="set-value">{Array.from(set).join(',')}</span>
</div>
);
}
10 changes: 9 additions & 1 deletion packages/astro/e2e/fixtures/pass-js/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ const obj: BigNestedObject = {
}
}
};
const map = new Map<string,string>();
map.set('test1', 'test2');
map.set('test3', 'test4');
const set = new Set<string>();
set.add('test1');
set.add('test2');
---

<html lang="en">
Expand All @@ -22,7 +30,7 @@ const obj: BigNestedObject = {
</head>
<body>
<main>
<Component client:load obj={obj} num={11n} arr={[0, "foo"]} />
<Component client:load obj={obj} num={11n} arr={[0, "foo"]} map={map} set={set} />
</main>
</body>
</html>
24 changes: 23 additions & 1 deletion packages/astro/e2e/pass-js.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { expect } from '@playwright/test';
import { testFactory } from './test-utils.js';

const test = testFactory({ root: './fixtures/pass-js/' });
const test = testFactory({
root: './fixtures/pass-js/'
});

let devServer;

Expand Down Expand Up @@ -53,4 +55,24 @@ test.describe('Passing JS into client components', () => {
await expect(arrValue, 'is visible').toBeVisible();
await expect(arrValue).toHaveText('0,foo');
});

test('Maps and Sets', async ({ page }) => {
await page.goto('/');

const mapType = page.locator('#map-type');
await expect(mapType, 'is visible').toBeVisible();
await expect(mapType).toHaveText('[object Map]');

const mapValues = page.locator('#map-items li');
expect(await mapValues.count()).toEqual(2);

const texts = await mapValues.allTextContents();
expect(texts).toEqual(['test1: test2', 'test3: test4']);

const setType = page.locator('#set-type');
await expect(setType, 'is visible').toBeVisible();

const setValue = page.locator('#set-value');
await expect(setValue).toHaveText('test1,test2');
});
});
4 changes: 2 additions & 2 deletions packages/astro/src/runtime/server/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ function convertToSerializedForm(value: any): [ValueOf<typeof PROP_TYPE>, any] {
return [PROP_TYPE.RegExp, (value as RegExp).source];
}
case '[object Map]': {
return [PROP_TYPE.Map, Array.from(value as Map<any, any>)];
return [PROP_TYPE.Map, JSON.stringify(serializeArray(Array.from(value as Map<any, any>)))];
}
case '[object Set]': {
return [PROP_TYPE.Set, Array.from(value as Set<any>)];
return [PROP_TYPE.Set, JSON.stringify(serializeArray(Array.from(value as Set<any>)))];
}
case '[object BigInt]': {
return [PROP_TYPE.BigInt, (value as bigint).toString()];
Expand Down

0 comments on commit ceda294

Please sign in to comment.