Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consume RSC response in the server #463

Merged
merged 94 commits into from
Jan 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
57bb5f0
wip: Create custom flight renderer with local version of react-server
frandiox Dec 1, 2021
7ed7a73
wip: mock client manifest and add module reference to client components
frandiox Dec 1, 2021
e1eba5f
wip: hack RSC resolution
frandiox Dec 1, 2021
a119de6
wip: wrap component in Proxy to access original properties
frandiox Dec 2, 2021
46c9e06
refactor: rename RSC server files
frandiox Dec 2, 2021
31a1d58
wip: add official RSC hydrator
frandiox Dec 2, 2021
0bada32
fix: Rename response.readRoot and remove explicit hydration
frandiox Dec 3, 2021
a498942
wip: remove RR and Helmet providers from the server
frandiox Dec 3, 2021
4bdac0f
wip: Add test app
frandiox Dec 3, 2021
7406bf2
wip: remove hydration providers
frandiox Dec 3, 2021
81e4548
wip: add renderToReadableStream for RSC
frandiox Dec 3, 2021
2640a8c
refactor: cleanup custom RSC code
frandiox Dec 3, 2021
46dea47
refactor: move and rename files
frandiox Dec 3, 2021
f7f6b51
wip: fix test app
frandiox Dec 3, 2021
2019a6e
refactor: cleanup custom RSC code
frandiox Dec 3, 2021
fc7c30d
refactor: do not pass named boolean in RSC
frandiox Dec 3, 2021
593e6e6
refactor: simplify ClientMarker
frandiox Dec 3, 2021
548b227
test: fix ClientMarker tests
frandiox Dec 3, 2021
b5c1674
fix: delay throwing error when client component is missing
frandiox Dec 3, 2021
6c56da7
refactor: simplify import globs code
frandiox Dec 9, 2021
8c30c09
feat: provide request object to rendering tree
frandiox Dec 21, 2021
2837bef
wip: example of useServerRequest
frandiox Dec 21, 2021
00462a9
feat: inline RSC response in the SSR HTML response
frandiox Dec 21, 2021
ff72d26
fix: add default value to the SSR provider
frandiox Jan 6, 2022
c8e5efb
feat: stream rsc in script tags
frandiox Jan 6, 2022
4a84948
feat: update the starter template to work
blittle Jan 6, 2022
67a11a5
fix: lint errors
blittle Jan 6, 2022
83854c8
refactor: move code around and cleanup
frandiox Jan 7, 2022
2105049
Merge branch 'fd-vite-rsc' into fd-vite-rsc-request-provider
frandiox Jan 7, 2022
c8382dd
chore: upgrade Vite to 2.7.0
frandiox Jan 7, 2022
131a99a
chore: add react-server-dom-vite as vendor
frandiox Jan 7, 2022
6e9cbc7
feat: replace local react-server and react-client with react-server-d…
frandiox Jan 7, 2022
2fa783f
fix: react-server-dom-vite allow exporting hooks from client componen…
frandiox Jan 7, 2022
977d8db
Merge branch 'main' into fd-vite-rsc
blittle Jan 7, 2022
01a534d
fix: move the server logging locations
blittle Jan 7, 2022
cd59155
feat: Implement ReadableStream branch in SSR and RSC
frandiox Jan 7, 2022
67bc790
test: fix playground apps
frandiox Jan 10, 2022
f2fd900
fix: avoid importing undefined exports depending on the running envir…
frandiox Jan 10, 2022
52bdbfe
feat: Buffer RSC response if it cannot be streamed
frandiox Jan 10, 2022
b032e80
fix: tests
blittle Jan 10, 2022
a4d09e5
fix: e2e test
blittle Jan 10, 2022
a4cf4f8
fix: linting
blittle Jan 10, 2022
e106036
Merge branch 'main' into fd-vite-rsc
blittle Jan 10, 2022
c2ac384
fix: rename test helper
blittle Jan 10, 2022
f0b7b3e
fix: update hydrogen template files
blittle Jan 10, 2022
ff0088f
feat: remove react-ssr-prepass. RSC already makes ReadableStream requ…
frandiox Jan 11, 2022
ad17ac8
refactor: simplify hydration calls
frandiox Jan 11, 2022
a60a557
Merge branch 'fd-vite-rsc' of https://github.com/Shopify/hydrogen int…
frandiox Jan 11, 2022
8650188
feat: remove old dependencies
frandiox Jan 11, 2022
7163866
feat: enable tree shaking in worker build
frandiox Jan 11, 2022
bcc4bfc
fix: Release stream lock before piping
frandiox Jan 11, 2022
3bf918c
chore: fix formatting
frandiox Jan 11, 2022
a89af2c
refactor: move code to entry-server to be consistent
frandiox Jan 11, 2022
b32f0f7
refactor: remove HydrationWriter in favor of Node native PassThrough
frandiox Jan 11, 2022
247296e
Merge branch 'fd-vite-rsc' into fd-vite-rsc-request-provider
frandiox Jan 12, 2022
0f25305
fix: move constant to a separate file to avoid importing app logic in…
frandiox Jan 12, 2022
026eb4b
refactor: remove old code
frandiox Jan 12, 2022
990fb66
Merge branch 'fd-vite-rsc' into fd-vite-rsc-request-provider
frandiox Jan 12, 2022
e42b297
fix: maybe fix tests for windows
frandiox Jan 12, 2022
cd1c9b7
Merge branch 'main' into fd-vite-rsc
frandiox Jan 12, 2022
737944b
fix: stream import in workers
frandiox Jan 12, 2022
6bfb3b9
Merge branch 'fd-vite-rsc' into fd-vite-rsc-request-provider
frandiox Jan 12, 2022
b7043ce
fix: flush RSC right after writing head
frandiox Jan 12, 2022
04f20a1
feat: replace RenderCacheProvider with new ServerRequestProvider cache
frandiox Jan 12, 2022
bd04b70
refactor: cleanup
frandiox Jan 12, 2022
6f1e8e7
fix: suspense breaking hydration
frandiox Jan 12, 2022
c56d374
fix: normalize RSC chunks
frandiox Jan 12, 2022
2548619
Merge pull request #390 from Shopify/fd-vite-rsc-request-provider
frandiox Jan 12, 2022
b7fb5fc
refactor: simplify customBody check
frandiox Jan 13, 2022
a185166
feat: minor tree-shaking improvements
frandiox Jan 13, 2022
da7ddb6
fix: enable browser hydration
frandiox Jan 13, 2022
3862b4d
fix: replace TransformStream with ReadableStream to support Firefox
frandiox Jan 13, 2022
0d7e878
refactor: cleanup and rename variables
frandiox Jan 13, 2022
9c373fd
feat: Make ReadableStream globally available
frandiox Jan 13, 2022
3806f8b
wip: consume RSC response in server
frandiox Jan 13, 2022
593d1db
wip: do not close stream connection
frandiox Jan 13, 2022
f7adeb8
chore: Merge branch 'experimental' into fd-vite-rsc-consume-response
jplhomer Jan 19, 2022
2ea3f3a
wip: fix reentrant error with ReadableStreams in RSC
jplhomer Jan 19, 2022
fccfd91
fix: import Node RSC logic dynamically
frandiox Jan 20, 2022
8f52e4a
fix: rename Cache.client.js to avoid bundling it in workers build
frandiox Jan 20, 2022
d5aef68
fix: add temporary monkey patch for React Fizz in workers build
frandiox Jan 20, 2022
d3d64c2
feat: Combine SSR and RSC responses in Node
frandiox Jan 20, 2022
3192734
fix: write RSC before ending response when not streaming
frandiox Jan 20, 2022
e17da24
feat: Combine SSR and RSC responses in Worker
frandiox Jan 20, 2022
f8b0fc7
feat: Use ReadableStream in CFW and stream if supported
frandiox Jan 20, 2022
1474e15
fix: add another temporary monkey patch for React Fizz/Flight in work…
frandiox Jan 20, 2022
d50ffc6
fix: close writable on redirects
frandiox Jan 20, 2022
4c95e02
fix: Fix hydration during dev by using more Suspense boundaries
jplhomer Jan 20, 2022
c3290b2
fix: add back response.socket listener
frandiox Jan 21, 2022
6138b03
feat: support script nonce and shorten access to __flight
frandiox Jan 21, 2022
ec4d678
feat: avoid writing fractional chunks in SSR
frandiox Jan 21, 2022
48836f3
fix: initialize flight container in buffered rendering
frandiox Jan 21, 2022
8bf6c7b
fix: ensure flight container is added in Node
frandiox Jan 21, 2022
c60ed19
refactor: `readable` to `ssrReadable` for clarity
jplhomer Jan 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 31 additions & 23 deletions packages/dev/src/components/Welcome.server.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {useShopQuery, flattenConnection, Link} from '@shopify/hydrogen';
import gql from 'graphql-tag';
import {Suspense} from 'react';

function ExternalIcon() {
return (
Expand Down Expand Up @@ -31,7 +32,20 @@ function DocsButton({url, label}) {
);
}

function StorefrontInfo({shopName, totalProducts, totalCollections}) {
function BoxFallback() {
return (
<div className="bg-white p-12 shadow-xl rounded-xl text-gray-900 h-60"></div>
);
}

function StorefrontInfo() {
const {data} = useShopQuery({query: QUERY});
const shopName = data ? data.shop.name : '';
const products = data && flattenConnection(data.products);
const collections = data && flattenConnection(data.collections);
const totalProducts = products && products.length;
const totalCollections = collections && collections.length;

const pluralize = (count, noun, suffix = 's') =>
`${count} ${noun}${count === 1 ? '' : suffix}`;
return (
Expand Down Expand Up @@ -71,7 +85,14 @@ function StorefrontInfo({shopName, totalProducts, totalCollections}) {
);
}

function TemplateLinks({firstProductPath, firstCollectionPath}) {
function TemplateLinks() {
const {data} = useShopQuery({query: QUERY});
const products = data && flattenConnection(data.products);
const collections = data && flattenConnection(data.collections);

const firstProduct = products && products.length ? products[0].handle : '';
const firstCollection = collections[0] ? collections[0].handle : '';

return (
<div className="bg-white p-12 md:p-12 shadow-xl rounded-xl text-gray-900">
<p className="text-md font-medium uppercase mb-4">
Expand All @@ -80,15 +101,15 @@ function TemplateLinks({firstProductPath, firstCollectionPath}) {
<ul>
<li className="mb-4">
<Link
to={`/collections/${firstCollectionPath}`}
to={`/collections/${firstCollection}`}
className="text-md font-medium text-blue-700 hover:underline"
>
Collection template
</Link>
</li>
<li className="mb-4">
<Link
to={`/products/${firstProductPath}`}
to={`/products/${firstProduct}`}
className="text-md font-medium text-blue-700 hover:underline"
>
Product template
Expand All @@ -111,16 +132,6 @@ function TemplateLinks({firstProductPath, firstCollectionPath}) {
* A server component that displays the content on the homepage of the Hydrogen app
*/
export default function Welcome() {
const {data} = useShopQuery({query: QUERY});
const shopName = data ? data.shop.name : '';
const products = data && flattenConnection(data.products);
const collections = data && flattenConnection(data.collections);

const firstProduct = products && products.length ? products[0].handle : '';
const totalProducts = products && products.length;
const firstCollection = collections[0] ? collections[0].handle : '';
const totalCollections = collections && collections.length;

return (
<div className="text-gray-900 pt-16 rounded-[40px] my-16 px-4 xl:px-12 bg-gradient-to-b from-white -mx-4 xl:-mx-12">
<div className="text-center mb-16">
Expand All @@ -143,15 +154,12 @@ export default function Welcome() {
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-16">
<StorefrontInfo
shopName={shopName}
totalProducts={totalProducts}
totalCollections={totalCollections}
/>
<TemplateLinks
firstProductPath={firstProduct}
firstCollectionPath={firstCollection}
/>
<Suspense fallback={<BoxFallback />}>
<StorefrontInfo />
</Suspense>
<Suspense fallback={<BoxFallback />}>
<TemplateLinks />
</Suspense>
Comment on lines +157 to +162
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This was the culprit causing issues in Vite dev server for Hydration, on the homepage only. I refactored this to use multiple Suspense boundaries, etc.

Now it breaks in Workers 😭 seeing quite a bit of this with react-helmet-async:

TypeError: Cannot read property 'add' of undefined
    at e2.r2.init (/Users/joshlarson/src/github.com/Shopify/hydrogen/node_modules/react-helmet-async/lib/index.module.js:1:10719)
    at e2.r2.render (/Users/joshlarson/src/github.com/Shopify/hydrogen/node_modules/react-helmet-async/lib/index.module.js:1:10742)
    at Fc$1 (/Users/joshlarson/src/github.com/Shopify/hydrogen/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:63:111)

Copy link
Contributor Author

@frandiox frandiox Jan 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

Now it breaks in Workers 😭 seeing quite a bit of this with react-helmet-async:

The only way I can reproduce this is:

  1. Start dev server in port 3000
  2. Open tab
  3. Close dev server and start Miniflare
  4. Let the previous tab reload

If I start from a new tab or just refresh, it seems to work well. I've also deployed it to CFW and works 🤔

Edit: ahh, it is happening randomly in the development server as well...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could look into this

</div>
</div>
);
Expand Down
148 changes: 88 additions & 60 deletions packages/dev/src/pages/index.server.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,94 @@ import Layout from '../components/Layout.server';
import FeaturedCollection from '../components/FeaturedCollection';
import ProductCard from '../components/ProductCard';
import Welcome from '../components/Welcome.server';
import {Suspense} from 'react';

export default function Index({country = {isoCode: 'US'}}) {
return (
<Layout hero={<GradientBackground />}>
<div className="relative mb-12">
<Welcome />
<Suspense fallback={<BoxFallback />}>
<FeaturedProductsBox country={country} />
</Suspense>
<Suspense fallback={<BoxFallback />}>
<FeaturedCollectionBox country={country} />
</Suspense>
</div>
</Layout>
);
}

function BoxFallback() {
return <div className="bg-white p-12 shadow-xl rounded-xl mb-10 h-40"></div>;
}

function FeaturedProductsBox({country}) {
const {data} = useShopQuery({
query: QUERY,
variables: {
country: country.isoCode,
},
});

const collections = data ? flattenConnection(data.collections) : [];
const featuredProductsCollection = collections[0];
const featuredProducts = featuredProductsCollection
? flattenConnection(featuredProductsCollection.products)
: null;

return (
<div className="bg-white p-12 shadow-xl rounded-xl mb-10">
{featuredProductsCollection ? (
<>
<div className="flex justify-between items-center mb-8 text-md font-medium">
<span className="text-black uppercase">
{featuredProductsCollection.title}
</span>
<span className="hidden md:inline-flex">
<Link
to={`/collections/${featuredProductsCollection.handle}`}
className="text-blue-600 hover:underline"
>
Shop all
</Link>
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-8">
{featuredProducts.map((product) => (
<div key={product.id}>
<ProductCard product={product} />
</div>
))}
</div>
<div className="md:hidden text-center">
<Link
to={`/collections/${featuredProductsCollection.handle}`}
className="text-blue-600"
>
Shop all
</Link>
</div>
</>
) : null}
</div>
);
}

function FeaturedCollectionBox({country}) {
const {data} = useShopQuery({
query: QUERY,
variables: {
country: country.isoCode,
},
});

const collections = data ? flattenConnection(data.collections) : [];
const featuredCollection =
collections && collections.length > 1 ? collections[1] : collections[0];

return <FeaturedCollection collection={featuredCollection} />;
}

function GradientBackground() {
return (
Expand Down Expand Up @@ -67,66 +155,6 @@ function GradientBackground() {
);
}

export default function Index({country = {isoCode: 'US'}}) {
const {data} = useShopQuery({
query: QUERY,
variables: {
country: country.isoCode,
},
});

const collections = data ? flattenConnection(data.collections) : [];
const featuredProductsCollection = collections[0];
const featuredProducts = featuredProductsCollection
? flattenConnection(featuredProductsCollection.products)
: null;
const featuredCollection =
collections && collections.length > 1 ? collections[1] : collections[0];

return (
<Layout hero={<GradientBackground />}>
<div className="relative mb-12">
<Welcome />
<div className="bg-white p-12 shadow-xl rounded-xl mb-10">
{featuredProductsCollection ? (
<>
<div className="flex justify-between items-center mb-8 text-md font-medium">
<span className="text-black uppercase">
{featuredProductsCollection.title}
</span>
<span className="hidden md:inline-flex">
<Link
to={`/collections/${featuredProductsCollection.handle}`}
className="text-blue-600 hover:underline"
>
Shop all
</Link>
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-8">
{featuredProducts.map((product) => (
<div key={product.id}>
<ProductCard product={product} />
</div>
))}
</div>
<div className="md:hidden text-center">
<Link
to={`/collections/${featuredCollection.handle}`}
className="text-blue-600"
>
Shop all
</Link>
</div>
</>
) : null}
</div>
<FeaturedCollection collection={featuredCollection} />
</div>
</Layout>
);
}

const QUERY = gql`
query indexContent(
$country: CountryCode
Expand Down
2 changes: 1 addition & 1 deletion packages/hydrogen/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"@rollup/plugin-graphql": "^1.0.0",
"@types/connect": "^3.4.34",
"@types/graphql": "^14.5.0",
"@types/node": "^15.12.4",
"@types/node": "^16.11.7",
"@types/node-fetch": "^2.5.9",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/hydrogen/src/entry-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {createRoot} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import type {ClientHandler, ShopifyConfig} from './types';
import {ErrorBoundary} from 'react-error-boundary';
import {useServerResponse} from './framework/Hydration/Cache.client';
import {useServerResponse} from './framework/Hydration/rsc';
import {
ServerStateProvider,
ServerStateRouter,
Expand Down
Loading