From ceda294e133d4cc32e68488393287be6077fd3d5 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Mon, 18 Jul 2022 16:43:09 -0400 Subject: [PATCH] Fixes hydration of Maps and Sets (#3960) --- .changeset/modern-bulldogs-burn.md | 5 ++++ .../fixtures/pass-js/src/components/React.tsx | 12 +++++++++- .../fixtures/pass-js/src/pages/index.astro | 10 +++++++- packages/astro/e2e/pass-js.test.js | 24 ++++++++++++++++++- .../astro/src/runtime/server/serialize.ts | 4 ++-- 5 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 .changeset/modern-bulldogs-burn.md diff --git a/.changeset/modern-bulldogs-burn.md b/.changeset/modern-bulldogs-burn.md new file mode 100644 index 000000000..76c3fd8a4 --- /dev/null +++ b/.changeset/modern-bulldogs-burn.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes hydration of maps/sets diff --git a/packages/astro/e2e/fixtures/pass-js/src/components/React.tsx b/packages/astro/e2e/fixtures/pass-js/src/components/React.tsx index 7302c5443..02023fc9d 100644 --- a/packages/astro/e2e/fixtures/pass-js/src/components/React.tsx +++ b/packages/astro/e2e/fixtures/pass-js/src/components/React.tsx @@ -4,12 +4,15 @@ import { useState } from 'react'; interface Props { obj: BigNestedObject; num: bigint; + arr: any[]; + map: Map; + set: Set; } 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
@@ -24,6 +27,13 @@ export default function Component({ obj, num, arr }: Props) { {num.toString()} {Object.prototype.toString.call(arr)} {arr.join(',')} + {Object.prototype.toString.call(map)} +
    {Array.from(map).map(([key, value]) => ( +
  • {key}: {value}
  • + ))} +
+ {Object.prototype.toString.call(set)} + {Array.from(set).join(',')} ); } diff --git a/packages/astro/e2e/fixtures/pass-js/src/pages/index.astro b/packages/astro/e2e/fixtures/pass-js/src/pages/index.astro index dc029b6fb..be40948d8 100644 --- a/packages/astro/e2e/fixtures/pass-js/src/pages/index.astro +++ b/packages/astro/e2e/fixtures/pass-js/src/pages/index.astro @@ -12,6 +12,14 @@ const obj: BigNestedObject = { } } }; + +const map = new Map(); +map.set('test1', 'test2'); +map.set('test3', 'test4'); + +const set = new Set(); +set.add('test1'); +set.add('test2'); --- @@ -22,7 +30,7 @@ const obj: BigNestedObject = {
- +
diff --git a/packages/astro/e2e/pass-js.test.js b/packages/astro/e2e/pass-js.test.js index 51c4d83de..c3e86888a 100644 --- a/packages/astro/e2e/pass-js.test.js +++ b/packages/astro/e2e/pass-js.test.js @@ -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; @@ -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'); + }); }); diff --git a/packages/astro/src/runtime/server/serialize.ts b/packages/astro/src/runtime/server/serialize.ts index 59ce96d8d..21996a932 100644 --- a/packages/astro/src/runtime/server/serialize.ts +++ b/packages/astro/src/runtime/server/serialize.ts @@ -33,10 +33,10 @@ function convertToSerializedForm(value: any): [ValueOf, any] { return [PROP_TYPE.RegExp, (value as RegExp).source]; } case '[object Map]': { - return [PROP_TYPE.Map, Array.from(value as Map)]; + return [PROP_TYPE.Map, JSON.stringify(serializeArray(Array.from(value as Map)))]; } case '[object Set]': { - return [PROP_TYPE.Set, Array.from(value as Set)]; + return [PROP_TYPE.Set, JSON.stringify(serializeArray(Array.from(value as Set)))]; } case '[object BigInt]': { return [PROP_TYPE.BigInt, (value as bigint).toString()];