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

Turn on Remix v3_singleFetch future flag #2708

Merged
merged 25 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
92bac68
skeleton template changes
wizardlyhel Jan 13, 2025
d95a318
add comment and clean up
wizardlyhel Jan 14, 2025
c699fee
Remove json and defer and update docs
wizardlyhel Jan 14, 2025
d3664c0
missed a couple flags in examples
wizardlyhel Jan 15, 2025
daf69be
Revert the setupVite update
wizardlyhel Jan 15, 2025
666d502
Fix typescript error
wizardlyhel Jan 15, 2025
40130cd
formet
wizardlyhel Jan 15, 2025
dda1d5f
add future flag to preview doc
wizardlyhel Jan 15, 2025
7e79643
Add test for redirect uri
wizardlyhel Jan 15, 2025
f7b45a2
Update templates/skeleton/app/routes/search.tsx
wizardlyhel Jan 15, 2025
9166141
revert other flag change
wizardlyhel Jan 15, 2025
34beed2
Add changeset
wizardlyhel Jan 17, 2025
025b6a2
update changeset
wizardlyhel Jan 20, 2025
a7e346e
Merge branch 'main' into hl-single-fetch
wizardlyhel Feb 5, 2025
52d32c1
Fix test
wizardlyhel Feb 5, 2025
939794d
Merge branch 'main' into hl-single-fetch
wizardlyhel Feb 5, 2025
fa2841d
Make sure headers are not lost
wizardlyhel Feb 6, 2025
d25ac46
Update instructions
wizardlyhel Feb 6, 2025
a429919
missed a bit of code diff
wizardlyhel Feb 6, 2025
70ced4a
Fix headers on legacy example
wizardlyhel Feb 6, 2025
8d9474f
add headers export to multipass
wizardlyhel Feb 6, 2025
0a2e75d
Fix error when navigating to checkout
wizardlyhel Feb 6, 2025
e9c7e5c
update upgrade instructions
wizardlyhel Feb 6, 2025
4035530
Merge branch 'main' into hl-single-fetch
wizardlyhel Feb 7, 2025
c48e524
add node import resolver
wizardlyhel Feb 7, 2025
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
173 changes: 173 additions & 0 deletions .changeset/plenty-cycles-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
---
'skeleton': patch
---

Turn on Remix `v3_singleFetch` future flag

Remix single fetch migration quick guide: https://remix.run/docs/en/main/start/future-flags#v3_singlefetch
Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single-fetch

**Note:** If you have any routes that appends (or looks for) a search param named `_data`, make sure to rename it to something else.

1. In your `vite.config.ts`, add the single fetch future flag.

```diff
+ declare module "@remix-run/server-runtime" {
+ interface Future {
+ v3_singleFetch: true;
+ }
+ }

export default defineConfig({
plugins: [
hydrogen(),
oxygen(),
remix({
presets: [hydrogen.preset()],
future: {
v3_fetcherPersist: true,
v3_relativeSplatPath: true,
v3_throwAbortReason: true,
v3_lazyRouteDiscovery: true,
+ v3_singleFetch: true,
},
}),
tsconfigPaths(),
],
```

2. In your `entry.server.tsx`, add `nonce` to the `<RemixServer>`.

```diff
const body = await renderToReadableStream(
<NonceProvider>
<RemixServer
context={remixContext}
url={request.url}
+ nonce={nonce}
/>
</NonceProvider>,
```

3. Update `cart.tsx` to add a headers export and update to `data` import usage.

```diff
import {
- json,
+ data,
type LoaderFunctionArgs,
type ActionFunctionArgs,
type HeadersFunction
} from '@shopify/remix-oxygen';
+ export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders;

export async function action({request, context}: ActionFunctionArgs) {
...
- return json(
+ return data(
{
cart: cartResult,
errors,
warnings,
analytics: {
cartId,
},
},
{status, headers},
);
}

export async function loader({context}: LoaderFunctionArgs) {
const {cart} = context;
- return json(await cart.get());
+ return await cart.get();
}
```

4. Deprecate `json` and `defer` import usage from `@shopify/remix-oxygen`.

Remove `json()`/`defer()` in favor of raw objects.

Single Fetch supports JSON objects and Promises out of the box, so you can return the raw data from your loader/action functions:

```diff
- import {json} from "@shopify/remix-oxygen";

export async function loader({}: LoaderFunctionArgs) {
let tasks = await fetchTasks();
- return json(tasks);
+ return tasks;
}
```

```diff
- import {defer} from "@shopify/remix-oxygen";

export async function loader({}: LoaderFunctionArgs) {
let lazyStuff = fetchLazyStuff();
let tasks = await fetchTasks();
- return defer({ tasks, lazyStuff });
+ return { tasks, lazyStuff };
}
```

If you were using the second parameter of json/defer to set a custom status or headers on your response, you can continue doing so via the new data API:

```diff
- import {json} from "@shopify/remix-oxygen";
+ import {data, type HeadersFunction} from "@shopify/remix-oxygen";

+ /**
+ * If your loader or action is returning a response with headers,
+ * make sure to export a headers function that merges your headers
+ * on your route. Otherwise, your headers may be lost.
+ * Remix doc: https://remix.run/docs/en/main/route/headers
+ **/
+ export const headers: HeadersFunction = ({loaderHeaders}) => loaderHeaders;

export async function loader({}: LoaderFunctionArgs) {
let tasks = await fetchTasks();
- return json(tasks, {
+ return data(tasks, {
headers: {
"Cache-Control": "public, max-age=604800"
}
});
}
```

5. If you are using legacy customer account flow or multipass, there are a couple more files that requires updating:

In `root.tsx` and `routes/account.tsx`, add a `headers` export for `loaderHeaders`.

```diff
+ export const headers: HeadersFunction = ({loaderHeaders}) => loaderHeaders;
```

In `routes/account_.register.tsx`, add a `headers` export for `actionHeaders`.

```diff
+ export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders;
```

6. If you are using multipass, in `routes/account_.login.multipass.tsx`

a. export a `headers` export

```diff
+ export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders;
```

b. update the success response wrapper to `Response.json`

```diff
// success, return token, url
- return json(
+ return Response.json(
{data: {...data, error: null}},
{
status: 200,
headers: getCorsHeaders(origin),
},
);
```
7 changes: 7 additions & 0 deletions .changeset/purple-buses-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@shopify/remix-oxygen': patch
'@shopify/hydrogen': patch
'@shopify/cli-hydrogen': patch
---

Turn on Remix `v3_singleFetch` future flag
6 changes: 2 additions & 4 deletions docs/preview/app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {json, type LinksFunction} from '@remix-run/node';
import {type LinksFunction} from '@remix-run/node';
import {
Links,
Meta,
Expand Down Expand Up @@ -26,9 +26,7 @@ export async function loader() {
}
}

return json({
data,
});
return {data};
}

export default function App() {
Expand Down
2 changes: 1 addition & 1 deletion docs/preview/app/routes/$doc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter';
import {oneDark} from 'react-syntax-highlighter/dist/cjs/styles/prism/index.js';

export async function loader({params}: LoaderFunctionArgs) {
return json({doc: params.doc});
return {doc: params.doc};
}

function getDefinition(definitions: any, type: string) {
Expand Down
1 change: 1 addition & 0 deletions docs/preview/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default defineConfig({
v3_fetcherPersist: false,
v3_relativeSplatPath: false,
v3_throwAbortReason: false,
v3_singleFetch: true,
},
}),
tsconfigPaths(),
Expand Down
2 changes: 1 addition & 1 deletion docs/shopify-dev/analytics-setup/js/app/entry-server.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default async function handleRequest(

const body = await renderToReadableStream(
<NonceProvider>
<RemixServer context={remixContext} url={request.url} />
<RemixServer context={remixContext} url={request.url} nonce={nonce} />
</NonceProvider>,
{
nonce,
Expand Down
5 changes: 2 additions & 3 deletions docs/shopify-dev/analytics-setup/js/app/root.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
Analytics,
// [END import]
} from '@shopify/hydrogen';
import {defer} from '@shopify/remix-oxygen';
import {
Links,
Meta,
Expand Down Expand Up @@ -83,7 +82,7 @@ export async function loader(args) {
const {storefront, env} = args.context;
// [END env]

return defer({
return {
...deferredData,
...criticalData,
publicStoreDomain: env.PUBLIC_STORE_DOMAIN,
Expand All @@ -103,7 +102,7 @@ export async function loader(args) {
language: args.context.storefront.i18n.language,
},
// [END consent]
});
};
}

/**
Expand Down
12 changes: 9 additions & 3 deletions docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useLoaderData} from '@remix-run/react';
import {CartForm, Analytics} from '@shopify/hydrogen';
import {json} from '@shopify/remix-oxygen';
import {data} from '@shopify/remix-oxygen';
import {CartMain} from '~/components/CartMain';

/**
Expand All @@ -10,6 +10,11 @@ export const meta = () => {
return [{title: `Hydrogen | Cart`}];
};

/**
* @type {HeadersFunction}
*/
export const headers = ({actionHeaders}) => actionHeaders;

/**
* @param {ActionFunctionArgs}
*/
Expand Down Expand Up @@ -81,7 +86,7 @@ export async function action({request, context}) {
headers.set('Location', redirectTo);
}

return json(
return data(
{
cart: cartResult,
errors,
Expand All @@ -99,7 +104,7 @@ export async function action({request, context}) {
*/
export async function loader({context}) {
const {cart} = context;
return json(await cart.get());
return await cart.get();
}

export default function Cart() {
Expand All @@ -118,6 +123,7 @@ export default function Cart() {
}

/** @template T @typedef {import('@remix-run/react').MetaFunction<T>} MetaFunction */
/** @template T @typedef {import('@remix-run/react').HeadersFunction<T>} HeadersFunction */
/** @typedef {import('@shopify/hydrogen').CartQueryDataReturn} CartQueryDataReturn */
/** @typedef {import('@shopify/remix-oxygen').LoaderFunctionArgs} LoaderFunctionArgs */
/** @typedef {import('@shopify/remix-oxygen').SerializeFrom<typeof action>} ActionReturnData */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export async function loader(args) {
// Await the critical data required to render initial state of the page
const criticalData = await loadCriticalData(args);

return defer({...deferredData, ...criticalData});
return {...deferredData, ...criticalData};
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export async function loader(args) {
// Await the critical data required to render initial state of the page
const criticalData = await loadCriticalData(args);

return defer({...deferredData, ...criticalData});
return {...deferredData, ...criticalData};
}

/**
Expand Down
3 changes: 1 addition & 2 deletions docs/shopify-dev/analytics-setup/js/app/routes/search.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {json} from '@shopify/remix-oxygen';
import {useLoaderData} from '@remix-run/react';
import {getPaginationVariables, Analytics} from '@shopify/hydrogen';
import {SearchForm} from '~/components/SearchForm';
Expand Down Expand Up @@ -27,7 +26,7 @@ export async function loader({request, context}) {
return {term: '', result: null, error: error.message};
});

return json(await searchPromise);
return await searchPromise;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion docs/shopify-dev/analytics-setup/ts/app/entry-server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default async function handleRequest(

const body = await renderToReadableStream(
<NonceProvider>
<RemixServer context={remixContext} url={request.url} />
<RemixServer context={remixContext} url={request.url} nonce={nonce} />
</NonceProvider>,
{
nonce,
Expand Down
6 changes: 3 additions & 3 deletions docs/shopify-dev/analytics-setup/ts/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
Analytics,
// [END import]
} from '@shopify/hydrogen';
import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {
Links,
Meta,
Expand Down Expand Up @@ -83,7 +83,7 @@ export async function loader(args: LoaderFunctionArgs) {
// Await the critical data required to render initial state of the page
const criticalData = await loadCriticalData(args);

return defer({
return {
...deferredData,
...criticalData,
publicStoreDomain: env.PUBLIC_STORE_DOMAIN,
Expand All @@ -103,7 +103,7 @@ export async function loader(args: LoaderFunctionArgs) {
language: storefront.i18n.language,
},
// [END consent]
});
};
}

/**
Expand Down
8 changes: 5 additions & 3 deletions docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import {type MetaFunction, useLoaderData} from '@remix-run/react';
import type {CartQueryDataReturn} from '@shopify/hydrogen';
import {CartForm, Analytics} from '@shopify/hydrogen';
import {json, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen';
import {data, type LoaderFunctionArgs, type ActionFunctionArgs, type HeadersFunction} from '@shopify/remix-oxygen';
import {CartMain} from '~/components/CartMain';

export const meta: MetaFunction = () => {
return [{title: `Hydrogen | Cart`}];
};

export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders;

export async function action({request, context}: ActionFunctionArgs) {
const {cart} = context;

Expand Down Expand Up @@ -80,7 +82,7 @@ export async function action({request, context}: ActionFunctionArgs) {
headers.set('Location', redirectTo);
}

return json(
return data(
{
cart: cartResult,
errors,
Expand All @@ -95,7 +97,7 @@ export async function action({request, context}: ActionFunctionArgs) {

export async function loader({context}: LoaderFunctionArgs) {
const {cart} = context;
return json(await cart.get());
return await cart.get();
}

export default function Cart() {
Expand Down
Loading