From 92bac68182334108bc8ecb380cc6bf876457c70a Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Mon, 13 Jan 2025 14:29:13 -0800 Subject: [PATCH 01/22] skeleton template changes --- packages/hydrogen/src/customer/customer.ts | 3 +- packages/remix-oxygen/src/index.ts | 1 + templates/skeleton/app/entry.server.tsx | 2 +- .../skeleton/app/routes/account.addresses.tsx | 34 +++++++++---------- .../app/routes/account.orders.$id.tsx | 6 ++-- .../app/routes/account.orders._index.tsx | 4 +-- .../skeleton/app/routes/account.profile.tsx | 12 +++---- templates/skeleton/app/routes/account.tsx | 4 +-- .../skeleton/app/routes/account_.logout.tsx | 4 ++- templates/skeleton/app/routes/cart.tsx | 6 ++-- .../skeleton/app/routes/policies.$handle.tsx | 4 +-- .../skeleton/app/routes/policies._index.tsx | 4 +-- templates/skeleton/app/routes/search.tsx | 5 +-- templates/skeleton/vite.config.ts | 7 ++++ 14 files changed, 54 insertions(+), 42 deletions(-) diff --git a/packages/hydrogen/src/customer/customer.ts b/packages/hydrogen/src/customer/customer.ts index 306e738d17..48bb31086f 100644 --- a/packages/hydrogen/src/customer/customer.ts +++ b/packages/hydrogen/src/customer/customer.ts @@ -59,9 +59,10 @@ function defaultAuthStatusHandler( const {pathname} = new URL(request.url); + // Make sure to remove the `.data` that Remix appends on soft navigation (single-fetch) const redirectTo = defaultLoginUrl + - `?${new URLSearchParams({return_to: pathname}).toString()}`; + `?${new URLSearchParams({return_to: pathname.replace(/\.data$/, '')}).toString()}`; return redirect(redirectTo); } diff --git a/packages/remix-oxygen/src/index.ts b/packages/remix-oxygen/src/index.ts index 80fb8c7160..33dce8b804 100644 --- a/packages/remix-oxygen/src/index.ts +++ b/packages/remix-oxygen/src/index.ts @@ -16,6 +16,7 @@ export { MaxPartSizeExceededError, redirect, redirectDocument, + data, } from '@remix-run/server-runtime'; export type { diff --git a/templates/skeleton/app/entry.server.tsx b/templates/skeleton/app/entry.server.tsx index a0d062d76e..d7c0f4337c 100644 --- a/templates/skeleton/app/entry.server.tsx +++ b/templates/skeleton/app/entry.server.tsx @@ -20,7 +20,7 @@ export default async function handleRequest( const body = await renderToReadableStream( - + , { nonce, diff --git a/templates/skeleton/app/routes/account.addresses.tsx b/templates/skeleton/app/routes/account.addresses.tsx index 448b9bf340..14cd1f3ff5 100644 --- a/templates/skeleton/app/routes/account.addresses.tsx +++ b/templates/skeleton/app/routes/account.addresses.tsx @@ -4,7 +4,7 @@ import type { CustomerFragment, } from 'customer-accountapi.generated'; import { - json, + data, type ActionFunctionArgs, type LoaderFunctionArgs, } from '@shopify/remix-oxygen'; @@ -38,7 +38,7 @@ export const meta: MetaFunction = () => { export async function loader({context}: LoaderFunctionArgs) { await context.customerAccount.handleAuthStatus(); - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { @@ -57,7 +57,7 @@ export async function action({request, context}: ActionFunctionArgs) { // this will ensure redirecting to login never happen for mutatation const isLoggedIn = await customerAccount.isLoggedIn(); if (!isLoggedIn) { - return json( + return data( {error: {[addressId]: 'Unauthorized'}}, { status: 401, @@ -112,21 +112,21 @@ export async function action({request, context}: ActionFunctionArgs) { throw new Error('Customer address create failed.'); } - return json({ + return { error: null, createdAddress: data?.customerAddressCreate?.customerAddress, defaultAddress, - }); + }; } catch (error: unknown) { if (error instanceof Error) { - return json( + return data( {error: {[addressId]: error.message}}, { status: 400, }, ); } - return json( + return data( {error: {[addressId]: error}}, { status: 400, @@ -161,21 +161,21 @@ export async function action({request, context}: ActionFunctionArgs) { throw new Error('Customer address update failed.'); } - return json({ + return { error: null, updatedAddress: address, defaultAddress, - }); + }; } catch (error: unknown) { if (error instanceof Error) { - return json( + return data( {error: {[addressId]: error.message}}, { status: 400, }, ); } - return json( + return data( {error: {[addressId]: error}}, { status: 400, @@ -206,17 +206,17 @@ export async function action({request, context}: ActionFunctionArgs) { throw new Error('Customer address delete failed.'); } - return json({error: null, deletedAddress: addressId}); + return {error: null, deletedAddress: addressId}; } catch (error: unknown) { if (error instanceof Error) { - return json( + return data( {error: {[addressId]: error.message}}, { status: 400, }, ); } - return json( + return data( {error: {[addressId]: error}}, { status: 400, @@ -226,7 +226,7 @@ export async function action({request, context}: ActionFunctionArgs) { } default: { - return json( + return data( {error: {[addressId]: 'Method not allowed'}}, { status: 405, @@ -236,14 +236,14 @@ export async function action({request, context}: ActionFunctionArgs) { } } catch (error: unknown) { if (error instanceof Error) { - return json( + return data( {error: error.message}, { status: 400, }, ); } - return json( + return data( {error}, { status: 400, diff --git a/templates/skeleton/app/routes/account.orders.$id.tsx b/templates/skeleton/app/routes/account.orders.$id.tsx index 6362e5f6b5..f5b3e0e431 100644 --- a/templates/skeleton/app/routes/account.orders.$id.tsx +++ b/templates/skeleton/app/routes/account.orders.$id.tsx @@ -1,4 +1,4 @@ -import {json, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, type MetaFunction} from '@remix-run/react'; import {Money, Image, flattenConnection} from '@shopify/hydrogen'; import type {OrderLineItemFullFragment} from 'customer-accountapi.generated'; @@ -42,13 +42,13 @@ export async function loader({params, context}: LoaderFunctionArgs) { firstDiscount?.__typename === 'PricingPercentageValue' && firstDiscount?.percentage; - return json({ + return { order, lineItems, discountValue, discountPercentage, fulfillmentStatus, - }); + }; } export default function OrderRoute() { diff --git a/templates/skeleton/app/routes/account.orders._index.tsx b/templates/skeleton/app/routes/account.orders._index.tsx index 825a6e2633..ba41d03a2c 100644 --- a/templates/skeleton/app/routes/account.orders._index.tsx +++ b/templates/skeleton/app/routes/account.orders._index.tsx @@ -4,7 +4,7 @@ import { getPaginationVariables, flattenConnection, } from '@shopify/hydrogen'; -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {CUSTOMER_ORDERS_QUERY} from '~/graphql/customer-account/CustomerOrdersQuery'; import type { CustomerOrdersFragment, @@ -34,7 +34,7 @@ export async function loader({request, context}: LoaderFunctionArgs) { throw Error('Customer orders not found'); } - return json({customer: data.customer}); + return {customer: data.customer}; } export default function Orders() { diff --git a/templates/skeleton/app/routes/account.profile.tsx b/templates/skeleton/app/routes/account.profile.tsx index 2068e8fae5..2973758cc5 100644 --- a/templates/skeleton/app/routes/account.profile.tsx +++ b/templates/skeleton/app/routes/account.profile.tsx @@ -2,7 +2,7 @@ import type {CustomerFragment} from 'customer-accountapi.generated'; import type {CustomerUpdateInput} from '@shopify/hydrogen/customer-account-api-types'; import {CUSTOMER_UPDATE_MUTATION} from '~/graphql/customer-account/CustomerUpdateMutation'; import { - json, + data, type ActionFunctionArgs, type LoaderFunctionArgs, } from '@shopify/remix-oxygen'; @@ -26,14 +26,14 @@ export const meta: MetaFunction = () => { export async function loader({context}: LoaderFunctionArgs) { await context.customerAccount.handleAuthStatus(); - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { const {customerAccount} = context; if (request.method !== 'PUT') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } const form = await request.formData(); @@ -68,12 +68,12 @@ export async function action({request, context}: ActionFunctionArgs) { throw new Error('Customer profile update failed.'); } - return json({ + return { error: null, customer: data?.customerUpdate?.customer, - }); + }; } catch (error: any) { - return json( + return data( {error: error.message, customer: null}, { status: 400, diff --git a/templates/skeleton/app/routes/account.tsx b/templates/skeleton/app/routes/account.tsx index 583e62c549..0941d4e0ef 100644 --- a/templates/skeleton/app/routes/account.tsx +++ b/templates/skeleton/app/routes/account.tsx @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data as remixData, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Form, NavLink, Outlet, useLoaderData} from '@remix-run/react'; import {CUSTOMER_DETAILS_QUERY} from '~/graphql/customer-account/CustomerDetailsQuery'; @@ -15,7 +15,7 @@ export async function loader({context}: LoaderFunctionArgs) { throw new Error('Customer not found'); } - return json( + return remixData( {customer: data.customer}, { headers: { diff --git a/templates/skeleton/app/routes/account_.logout.tsx b/templates/skeleton/app/routes/account_.logout.tsx index 36e05e15ee..d89979828d 100644 --- a/templates/skeleton/app/routes/account_.logout.tsx +++ b/templates/skeleton/app/routes/account_.logout.tsx @@ -6,5 +6,7 @@ export async function loader() { } export async function action({context}: ActionFunctionArgs) { - return context.customerAccount.logout(); + return context.customerAccount.logout({ + postLogoutRedirectUri: 'https://seasnail-cuddly-correctly.ngrok-free.app', + }); } diff --git a/templates/skeleton/app/routes/cart.tsx b/templates/skeleton/app/routes/cart.tsx index f477802224..86b3f08888 100644 --- a/templates/skeleton/app/routes/cart.tsx +++ b/templates/skeleton/app/routes/cart.tsx @@ -1,7 +1,7 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {json, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; import {CartMain} from '~/components/CartMain'; export const meta: MetaFunction = () => { @@ -80,7 +80,7 @@ export async function action({request, context}: ActionFunctionArgs) { headers.set('Location', redirectTo); } - return json( + return data( { cart: cartResult, errors, @@ -95,7 +95,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() { diff --git a/templates/skeleton/app/routes/policies.$handle.tsx b/templates/skeleton/app/routes/policies.$handle.tsx index 367d4c431b..941c263217 100644 --- a/templates/skeleton/app/routes/policies.$handle.tsx +++ b/templates/skeleton/app/routes/policies.$handle.tsx @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Link, useLoaderData, type MetaFunction} from '@remix-run/react'; import {type Shop} from '@shopify/hydrogen/storefront-api-types'; @@ -38,7 +38,7 @@ export async function loader({params, context}: LoaderFunctionArgs) { throw new Response('Could not find the policy', {status: 404}); } - return json({policy}); + return {policy}; } export default function Policy() { diff --git a/templates/skeleton/app/routes/policies._index.tsx b/templates/skeleton/app/routes/policies._index.tsx index 243ee1fa52..bc1e43347b 100644 --- a/templates/skeleton/app/routes/policies._index.tsx +++ b/templates/skeleton/app/routes/policies._index.tsx @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, Link} from '@remix-run/react'; export async function loader({context}: LoaderFunctionArgs) { @@ -9,7 +9,7 @@ export async function loader({context}: LoaderFunctionArgs) { throw new Response('No policies found', {status: 404}); } - return json({policies}); + return {policies}; } export default function Policies() { diff --git a/templates/skeleton/app/routes/search.tsx b/templates/skeleton/app/routes/search.tsx index 2e453c221e..1f2aa590eb 100644 --- a/templates/skeleton/app/routes/search.tsx +++ b/templates/skeleton/app/routes/search.tsx @@ -1,5 +1,4 @@ import { - json, type LoaderFunctionArgs, type ActionFunctionArgs, } from '@shopify/remix-oxygen'; @@ -29,7 +28,9 @@ export async function loader({request, context}: LoaderFunctionArgs) { return {term: '', result: null, error: error.message}; }); - return json(await searchPromise); + + + return await searchPromise; } /** diff --git a/templates/skeleton/vite.config.ts b/templates/skeleton/vite.config.ts index 5ee9b626fb..56de3011d7 100644 --- a/templates/skeleton/vite.config.ts +++ b/templates/skeleton/vite.config.ts @@ -4,6 +4,12 @@ import {oxygen} from '@shopify/mini-oxygen/vite'; import {vitePlugin as remix} from '@remix-run/dev'; import tsconfigPaths from 'vite-tsconfig-paths'; +declare module "@remix-run/server-runtime" { + interface Future { + v3_singleFetch: true; + } +} + export default defineConfig({ plugins: [ hydrogen(), @@ -15,6 +21,7 @@ export default defineConfig({ v3_relativeSplatPath: true, v3_throwAbortReason: true, v3_lazyRouteDiscovery: true, + v3_singleFetch: true, }, }), tsconfigPaths(), From d95a3183d42891eb6aa91e16576d4e0029779704 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Tue, 14 Jan 2025 10:53:23 -0800 Subject: [PATCH 02/22] add comment and clean up --- packages/hydrogen/src/customer/customer.ts | 17 +++++++++++++++-- .../skeleton/app/routes/account_.logout.tsx | 4 +--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/hydrogen/src/customer/customer.ts b/packages/hydrogen/src/customer/customer.ts index 48bb31086f..622e865395 100644 --- a/packages/hydrogen/src/customer/customer.ts +++ b/packages/hydrogen/src/customer/customer.ts @@ -59,10 +59,23 @@ function defaultAuthStatusHandler( const {pathname} = new URL(request.url); - // Make sure to remove the `.data` that Remix appends on soft navigation (single-fetch) + /** + * Remix (single-fetch) request objects have different url + * paths when soft navigating. Examples: + * + * /_root.data - home page + * /collections.data - collections page + * + * These url annotations needs to be cleaned up before constructing urls to be passed as + * GET parameters for customer login url + */ + const cleanedPathname = pathname + .replace(/\.data$/, '') + .replace(/^\/_root$/, '/'); + const redirectTo = defaultLoginUrl + - `?${new URLSearchParams({return_to: pathname.replace(/\.data$/, '')}).toString()}`; + `?${new URLSearchParams({return_to: cleanedPathname}).toString()}`; return redirect(redirectTo); } diff --git a/templates/skeleton/app/routes/account_.logout.tsx b/templates/skeleton/app/routes/account_.logout.tsx index d89979828d..36e05e15ee 100644 --- a/templates/skeleton/app/routes/account_.logout.tsx +++ b/templates/skeleton/app/routes/account_.logout.tsx @@ -6,7 +6,5 @@ export async function loader() { } export async function action({context}: ActionFunctionArgs) { - return context.customerAccount.logout({ - postLogoutRedirectUri: 'https://seasnail-cuddly-correctly.ngrok-free.app', - }); + return context.customerAccount.logout(); } From c699fee774f08c7faf7b4c6f9ad8fd065f898ac9 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Tue, 14 Jan 2025 15:57:12 -0800 Subject: [PATCH 03/22] Remove json and defer and update docs --- docs/preview/app/root.tsx | 6 ++-- docs/preview/app/routes/$doc.tsx | 2 +- .../analytics-setup/js/app/entry-server.jsx | 2 +- .../analytics-setup/js/app/root.jsx | 5 ++-- .../analytics-setup/js/app/routes/cart.jsx | 6 ++-- .../js/app/routes/collections.$handle.jsx | 2 +- .../js/app/routes/products.$handle.jsx | 2 +- .../analytics-setup/js/app/routes/search.jsx | 3 +- .../analytics-setup/ts/app/entry-server.tsx | 2 +- .../analytics-setup/ts/app/root.tsx | 6 ++-- .../analytics-setup/ts/app/routes/cart.tsx | 6 ++-- .../ts/app/routes/collections.$handle.tsx | 4 +-- .../ts/app/routes/products.$handle.tsx | 7 ++--- .../analytics-setup/ts/app/routes/search.tsx | 3 +- examples/b2b/app/root.tsx | 6 ++-- examples/b2b/app/routes/b2blocations.tsx | 4 +-- examples/b2b/app/routes/products.$handle.tsx | 4 +-- examples/classic-remix/app/root.tsx | 6 ++-- .../custom-cart-method/app/routes/cart.tsx | 6 ++-- examples/express/app/entry.server.tsx | 2 ++ examples/express/app/root.tsx | 5 ++-- .../express/app/routes/products.$handle.tsx | 4 +-- examples/gtm/app/entry.server.tsx | 2 +- examples/gtm/app/root.tsx | 6 ++-- .../app/routes/collections.$handle.tsx | 4 +-- .../legacy-customer-account-flow/app/root.tsx | 4 +-- .../app/routes/account.addresses.tsx | 30 +++++++++---------- .../app/routes/account.orders.$id.tsx | 6 ++-- .../app/routes/account.orders._index.tsx | 8 ++--- .../app/routes/account.profile.tsx | 14 ++++----- .../app/routes/account.tsx | 8 ++--- ...account_.activate.$id.$activationToken.tsx | 10 +++---- .../app/routes/account_.login.tsx | 10 +++---- .../app/routes/account_.logout.tsx | 4 +-- .../app/routes/account_.recover.tsx | 12 ++++---- .../app/routes/account_.register.tsx | 12 ++++---- .../routes/account_.reset.$id.$resetToken.tsx | 8 ++--- .../app/routes/cart.tsx | 6 ++-- examples/metaobjects/README.md | 6 ++-- examples/metaobjects/app/root.tsx | 6 ++-- examples/metaobjects/app/routes/_index.tsx | 4 +-- .../metaobjects/app/routes/stores.$name.tsx | 4 +-- .../metaobjects/app/routes/stores._index.tsx | 4 +-- examples/multipass/app/root.tsx | 4 +-- .../app/routes/account.addresses.tsx | 30 +++++++++---------- .../app/routes/account.orders.$id.tsx | 6 ++-- .../app/routes/account.orders._index.tsx | 8 ++--- .../multipass/app/routes/account.profile.tsx | 14 ++++----- examples/multipass/app/routes/account.tsx | 8 ++--- ...account_.activate.$id.$activationToken.tsx | 10 +++---- .../app/routes/account_.login.multipass.tsx | 12 ++++---- .../multipass/app/routes/account_.login.tsx | 10 +++---- .../multipass/app/routes/account_.logout.tsx | 4 +-- .../multipass/app/routes/account_.recover.tsx | 12 ++++---- .../app/routes/account_.register.tsx | 12 ++++---- .../routes/account_.reset.$id.$resetToken.tsx | 8 ++--- examples/multipass/app/routes/cart.tsx | 6 ++-- examples/multipass/vite.config.ts | 1 + examples/partytown/README.md | 2 +- examples/partytown/app/entry.server.tsx | 2 +- examples/partytown/app/root.tsx | 6 ++-- examples/subscriptions/README.md | 2 +- .../app/routes/products.$handle.tsx | 6 ++-- .../third-party-queries-caching/README.md | 2 +- .../app/routes/_index.tsx | 4 +-- ...alyticsProvider.collectionView.example.jsx | 5 ++-- ...alyticsProvider.collectionView.example.tsx | 5 ++-- .../AnalyticsProvider.example.jsx | 5 ++-- .../AnalyticsProvider.example.tsx | 6 ++-- .../AnalyticsProvider.productView.example.jsx | 5 ++-- .../AnalyticsProvider.productView.example.tsx | 5 ++-- .../AnalyticsProvider.searchView.example.jsx | 5 +--- .../AnalyticsProvider.searchView.example.tsx | 6 ++-- .../getShopAnalytics.example.jsx | 5 ++-- .../getShopAnalytics.example.tsx | 6 ++-- .../hydrogen/src/cache/CacheCustom.example.js | 3 +- .../hydrogen/src/cache/CacheCustom.example.ts | 4 +-- .../hydrogen/src/cache/CacheLong.example.js | 3 +- .../hydrogen/src/cache/CacheLong.example.ts | 4 +-- .../hydrogen/src/cache/CacheNone.example.js | 3 +- .../hydrogen/src/cache/CacheNone.example.ts | 4 +-- .../hydrogen/src/cache/CacheShort.example.js | 3 +- .../hydrogen/src/cache/CacheShort.example.ts | 4 +-- .../generateCacheControlHeader.example.js | 4 +-- .../generateCacheControlHeader.example.ts | 4 +-- .../src/cart/CartForm.custom.example.jsx | 4 +-- .../src/cart/CartForm.custom.example.tsx | 4 +-- .../hydrogen/src/cart/CartForm.example.jsx | 4 +-- .../hydrogen/src/cart/CartForm.example.tsx | 4 +-- .../src/cart/CartForm.fetcher.example.jsx | 4 +-- .../src/cart/CartForm.fetcher.example.tsx | 4 +-- .../src/cart/CartForm.input-tag.example.jsx | 4 +-- .../src/cart/CartForm.input-tag.example.tsx | 4 +-- .../src/cart/cartSetIdDefault.example.js | 4 +-- .../optimistic/useOptimisticCart.example.jsx | 5 ++-- .../optimistic/useOptimisticCart.example.tsx | 6 ++-- .../createContentSecurityPolicy.example.jsx | 2 +- .../createContentSecurityPolicy.example.tsx | 2 +- .../customer.auth-handler.example.jsx | 3 +- .../customer.auth-handler.example.tsx | 4 +-- .../customer.opt-out-handler.example.jsx | 3 +- .../customer.opt-out-handler.example.tsx | 4 +-- .../src/pagination/Pagination.example.jsx | 3 +- .../src/pagination/Pagination.example.tsx | 4 +-- .../Pagination.multiple.example.jsx | 3 +- .../Pagination.multiple.example.tsx | 4 +-- .../getSelectedProductOptions.example.jsx | 5 ++-- .../getSelectedProductOptions.example.tsx | 6 ++-- .../product/useOptimisticVariant.example.jsx | 5 ++-- .../product/useOptimisticVariant.example.tsx | 6 ++-- packages/hydrogen/src/utils/graphql.ts | 2 +- rfc/cart.md | 2 +- rfc/pagination.md | 4 +-- templates/TEMPLATE_GUIDELINES.md | 24 +++++---------- templates/skeleton/app/entry.server.tsx | 2 +- templates/skeleton/app/root.tsx | 6 ++-- .../skeleton/app/routes/[robots.txt].tsx | 1 - templates/skeleton/app/routes/_index.tsx | 4 +-- .../blogs.$blogHandle.$articleHandle.tsx | 4 +-- .../app/routes/blogs.$blogHandle._index.tsx | 4 +-- .../skeleton/app/routes/blogs._index.tsx | 4 +-- .../app/routes/collections.$handle.tsx | 4 +-- .../app/routes/collections._index.tsx | 4 +-- .../skeleton/app/routes/collections.all.tsx | 4 +-- .../skeleton/app/routes/pages.$handle.tsx | 4 +-- .../skeleton/app/routes/products.$handle.tsx | 4 +-- .../predictiveSearch/predictiveSearch.md | 6 ++-- templates/skeleton/guides/search/search.md | 6 ++-- 128 files changed, 337 insertions(+), 372 deletions(-) diff --git a/docs/preview/app/root.tsx b/docs/preview/app/root.tsx index 5469d77c49..47446f0d23 100644 --- a/docs/preview/app/root.tsx +++ b/docs/preview/app/root.tsx @@ -1,4 +1,4 @@ -import {json, type LinksFunction} from '@remix-run/node'; +import {type LinksFunction} from '@remix-run/node'; import { Links, Meta, @@ -26,9 +26,7 @@ export async function loader() { } } - return json({ - data, - }); + return {data}; } export default function App() { diff --git a/docs/preview/app/routes/$doc.tsx b/docs/preview/app/routes/$doc.tsx index bf3cc745d6..3bc96aac42 100644 --- a/docs/preview/app/routes/$doc.tsx +++ b/docs/preview/app/routes/$doc.tsx @@ -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) { diff --git a/docs/shopify-dev/analytics-setup/js/app/entry-server.jsx b/docs/shopify-dev/analytics-setup/js/app/entry-server.jsx index 704df4239b..48777e7022 100644 --- a/docs/shopify-dev/analytics-setup/js/app/entry-server.jsx +++ b/docs/shopify-dev/analytics-setup/js/app/entry-server.jsx @@ -30,7 +30,7 @@ export default async function handleRequest( const body = await renderToReadableStream( - + , { nonce, diff --git a/docs/shopify-dev/analytics-setup/js/app/root.jsx b/docs/shopify-dev/analytics-setup/js/app/root.jsx index 588d41593d..15fc7d3a9c 100644 --- a/docs/shopify-dev/analytics-setup/js/app/root.jsx +++ b/docs/shopify-dev/analytics-setup/js/app/root.jsx @@ -5,7 +5,6 @@ import { Analytics, // [END import] } from '@shopify/hydrogen'; -import {defer} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -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, @@ -103,7 +102,7 @@ export async function loader(args) { language: args.context.storefront.i18n.language, }, // [END consent] - }); + }; } /** diff --git a/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx b/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx index c7381487d6..6cb2c6fbd8 100644 --- a/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx +++ b/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx @@ -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'; /** @@ -81,7 +81,7 @@ export async function action({request, context}) { headers.set('Location', redirectTo); } - return json( + return data( { cart: cartResult, errors, @@ -99,7 +99,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() { diff --git a/docs/shopify-dev/analytics-setup/js/app/routes/collections.$handle.jsx b/docs/shopify-dev/analytics-setup/js/app/routes/collections.$handle.jsx index 9360370805..e526fe84e5 100644 --- a/docs/shopify-dev/analytics-setup/js/app/routes/collections.$handle.jsx +++ b/docs/shopify-dev/analytics-setup/js/app/routes/collections.$handle.jsx @@ -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}; } /** diff --git a/docs/shopify-dev/analytics-setup/js/app/routes/products.$handle.jsx b/docs/shopify-dev/analytics-setup/js/app/routes/products.$handle.jsx index 606ee07e66..cf1a37f196 100644 --- a/docs/shopify-dev/analytics-setup/js/app/routes/products.$handle.jsx +++ b/docs/shopify-dev/analytics-setup/js/app/routes/products.$handle.jsx @@ -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}; } /** diff --git a/docs/shopify-dev/analytics-setup/js/app/routes/search.jsx b/docs/shopify-dev/analytics-setup/js/app/routes/search.jsx index b20ffc3820..b2dd0ed9f7 100644 --- a/docs/shopify-dev/analytics-setup/js/app/routes/search.jsx +++ b/docs/shopify-dev/analytics-setup/js/app/routes/search.jsx @@ -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'; @@ -27,7 +26,7 @@ export async function loader({request, context}) { return {term: '', result: null, error: error.message}; }); - return json(await searchPromise); + return await searchPromise; } /** diff --git a/docs/shopify-dev/analytics-setup/ts/app/entry-server.tsx b/docs/shopify-dev/analytics-setup/ts/app/entry-server.tsx index 3eb183a28a..1f36128659 100644 --- a/docs/shopify-dev/analytics-setup/ts/app/entry-server.tsx +++ b/docs/shopify-dev/analytics-setup/ts/app/entry-server.tsx @@ -29,7 +29,7 @@ export default async function handleRequest( const body = await renderToReadableStream( - + , { nonce, diff --git a/docs/shopify-dev/analytics-setup/ts/app/root.tsx b/docs/shopify-dev/analytics-setup/ts/app/root.tsx index b3a43d75ab..868cde5c65 100644 --- a/docs/shopify-dev/analytics-setup/ts/app/root.tsx +++ b/docs/shopify-dev/analytics-setup/ts/app/root.tsx @@ -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, @@ -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, @@ -103,7 +103,7 @@ export async function loader(args: LoaderFunctionArgs) { language: storefront.i18n.language, }, // [END consent] - }); + }; } /** diff --git a/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx b/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx index 0789789ad6..8d2dffc482 100644 --- a/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx +++ b/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx @@ -1,7 +1,7 @@ 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} from '@shopify/remix-oxygen'; import {CartMain} from '~/components/CartMain'; export const meta: MetaFunction = () => { @@ -80,7 +80,7 @@ export async function action({request, context}: ActionFunctionArgs) { headers.set('Location', redirectTo); } - return json( + return data( { cart: cartResult, errors, @@ -95,7 +95,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() { diff --git a/docs/shopify-dev/analytics-setup/ts/app/routes/collections.$handle.tsx b/docs/shopify-dev/analytics-setup/ts/app/routes/collections.$handle.tsx index 1e1b4c9171..086056ed0c 100644 --- a/docs/shopify-dev/analytics-setup/ts/app/routes/collections.$handle.tsx +++ b/docs/shopify-dev/analytics-setup/ts/app/routes/collections.$handle.tsx @@ -1,4 +1,4 @@ -import {defer, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, Link, type MetaFunction} from '@remix-run/react'; import { getPaginationVariables, @@ -21,7 +21,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/docs/shopify-dev/analytics-setup/ts/app/routes/products.$handle.tsx b/docs/shopify-dev/analytics-setup/ts/app/routes/products.$handle.tsx index fbd644dc85..918bdc8b0c 100644 --- a/docs/shopify-dev/analytics-setup/ts/app/routes/products.$handle.tsx +++ b/docs/shopify-dev/analytics-setup/ts/app/routes/products.$handle.tsx @@ -1,6 +1,5 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, type MetaFunction} from '@remix-run/react'; -import type {ProductFragment} from 'storefrontapi.generated'; import { getSelectedProductOptions, // [START import] @@ -11,8 +10,6 @@ import { getAdjacentAndFirstAvailableVariants, useSelectedOptionInUrlParam, } from '@shopify/hydrogen'; -import type {SelectedOption} from '@shopify/hydrogen/storefront-api-types'; -import {getVariantUrl} from '~/lib/variants'; import {ProductPrice} from '~/components/ProductPrice'; import {ProductImage} from '~/components/ProductImage'; import {ProductForm} from '~/components/ProductForm'; @@ -28,7 +25,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/docs/shopify-dev/analytics-setup/ts/app/routes/search.tsx b/docs/shopify-dev/analytics-setup/ts/app/routes/search.tsx index 01e6009f38..f3e6fcbc15 100644 --- a/docs/shopify-dev/analytics-setup/ts/app/routes/search.tsx +++ b/docs/shopify-dev/analytics-setup/ts/app/routes/search.tsx @@ -1,5 +1,4 @@ import { - json, type LoaderFunctionArgs, type ActionFunctionArgs, } from '@shopify/remix-oxygen'; @@ -29,7 +28,7 @@ export async function loader({request, context}: LoaderFunctionArgs) { return {term: '', result: null, error: error.message}; }); - return json(await searchPromise); + return await searchPromise; } /** diff --git a/examples/b2b/app/root.tsx b/examples/b2b/app/root.tsx index 41515dda50..6c86314bd9 100644 --- a/examples/b2b/app/root.tsx +++ b/examples/b2b/app/root.tsx @@ -1,5 +1,5 @@ import {useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -119,7 +119,7 @@ export async function loader({context}: LoaderFunctionArgs) { }, }); - return defer({ + return { cart: cartPromise, footer: footerPromise, header: await headerPromise, @@ -137,7 +137,7 @@ export async function loader({context}: LoaderFunctionArgs) { country: context.storefront.i18n.country, language: context.storefront.i18n.language, }, - }); + }; } export function Layout({children}: {children?: React.ReactNode}) { diff --git a/examples/b2b/app/routes/b2blocations.tsx b/examples/b2b/app/routes/b2blocations.tsx index 6cda28b53a..04a572a785 100644 --- a/examples/b2b/app/routes/b2blocations.tsx +++ b/examples/b2b/app/routes/b2blocations.tsx @@ -1,4 +1,4 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData} from '@remix-run/react'; import {B2BLocationSelector} from '../components/B2BLocationSelector'; import {CUSTOMER_LOCATIONS_QUERY} from '~/graphql/customer-account/CustomerLocationsQuery'; @@ -30,7 +30,7 @@ export async function loader({context}: LoaderFunctionArgs) { const modalOpen = Boolean(company) && !companyLocationId; - return defer({company, companyLocationId, modalOpen}); + return {company, companyLocationId, modalOpen}; } export default function CartRoute() { diff --git a/examples/b2b/app/routes/products.$handle.tsx b/examples/b2b/app/routes/products.$handle.tsx index 02f350ea5c..65bb5df85c 100644 --- a/examples/b2b/app/routes/products.$handle.tsx +++ b/examples/b2b/app/routes/products.$handle.tsx @@ -1,4 +1,4 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, type MetaFunction} from '@remix-run/react'; import { getSelectedProductOptions, @@ -53,7 +53,7 @@ export async function loader(args: LoaderFunctionArgs) { /********** EXAMPLE UPDATE END *************/ /***********************************************/ - return defer({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/examples/classic-remix/app/root.tsx b/examples/classic-remix/app/root.tsx index 832c78a7ff..5344691318 100644 --- a/examples/classic-remix/app/root.tsx +++ b/examples/classic-remix/app/root.tsx @@ -1,5 +1,5 @@ import {useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -72,7 +72,7 @@ export async function loader(args: LoaderFunctionArgs) { const {storefront, env} = args.context; - return defer({ + return { ...criticalData, ...deferredData, publicStoreDomain: env.PUBLIC_STORE_DOMAIN, @@ -84,7 +84,7 @@ export async function loader(args: LoaderFunctionArgs) { checkoutDomain: env.PUBLIC_CHECKOUT_DOMAIN, storefrontAccessToken: env.PUBLIC_STOREFRONT_API_TOKEN, }, - }); + }; } /** diff --git a/examples/custom-cart-method/app/routes/cart.tsx b/examples/custom-cart-method/app/routes/cart.tsx index ed003875bb..4d0057f865 100644 --- a/examples/custom-cart-method/app/routes/cart.tsx +++ b/examples/custom-cart-method/app/routes/cart.tsx @@ -1,7 +1,7 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {json, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; import type { SelectedOptionInput, CartLineUpdateInput, @@ -103,7 +103,7 @@ export async function action({request, context}: ActionFunctionArgs) { headers.set('Location', redirectTo); } - return json( + return data( { cart: cartResult, errors, @@ -117,7 +117,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() { diff --git a/examples/express/app/entry.server.tsx b/examples/express/app/entry.server.tsx index 3f3cfef03b..bef2bbb62f 100644 --- a/examples/express/app/entry.server.tsx +++ b/examples/express/app/entry.server.tsx @@ -59,6 +59,7 @@ function handleBotRequest( context={remixContext} url={request.url} abortDelay={ABORT_DELAY} + nonce={nonce} /> , { @@ -109,6 +110,7 @@ function handleBrowserRequest( context={remixContext} url={request.url} abortDelay={ABORT_DELAY} + nonce={nonce} /> , { diff --git a/examples/express/app/root.tsx b/examples/express/app/root.tsx index 122a44f0a0..76d53b0005 100644 --- a/examples/express/app/root.tsx +++ b/examples/express/app/root.tsx @@ -1,5 +1,4 @@ import { - defer, type LinksFunction, type LoaderFunctionArgs, } from '@remix-run/node'; @@ -66,11 +65,11 @@ export async function loader({context}: LoaderFunctionArgs) { await context.storefront.query<{shop: Shop}>(LAYOUT_QUERY), ]); - return defer({ + return { isLoggedIn: Boolean(customerAccessToken), cart, layout, - }); + }; } export function Layout({children}: {children?: React.ReactNode}) { diff --git a/examples/express/app/routes/products.$handle.tsx b/examples/express/app/routes/products.$handle.tsx index b50323fd9f..5264bb44a0 100644 --- a/examples/express/app/routes/products.$handle.tsx +++ b/examples/express/app/routes/products.$handle.tsx @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@remix-run/node'; +import {type LoaderFunctionArgs} from '@remix-run/node'; import {useLoaderData} from '@remix-run/react'; export async function loader({params, context}: LoaderFunctionArgs) { @@ -25,7 +25,7 @@ export async function loader({params, context}: LoaderFunctionArgs) { throw new Response(null, {status: 404}); } - return json({product}); + return {product}; } export default function Product() { diff --git a/examples/gtm/app/entry.server.tsx b/examples/gtm/app/entry.server.tsx index 389576f0f4..dcb1cf925e 100644 --- a/examples/gtm/app/entry.server.tsx +++ b/examples/gtm/app/entry.server.tsx @@ -37,7 +37,7 @@ export default async function handleRequest( const body = await renderToReadableStream( - + , { nonce, diff --git a/examples/gtm/app/root.tsx b/examples/gtm/app/root.tsx index 86898d0e59..141c003042 100644 --- a/examples/gtm/app/root.tsx +++ b/examples/gtm/app/root.tsx @@ -1,5 +1,5 @@ import {useNonce, getShopAnalytics, Analytics, Script} from '@shopify/hydrogen'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -74,7 +74,7 @@ export async function loader(args: LoaderFunctionArgs) { const {storefront, env} = args.context; - return defer({ + return { ...deferredData, ...criticalData, publicStoreDomain: env.PUBLIC_STORE_DOMAIN, @@ -90,7 +90,7 @@ export async function loader(args: LoaderFunctionArgs) { country: args.context.storefront.i18n.country, language: args.context.storefront.i18n.language, }, - }); + }; } /** diff --git a/examples/infinite-scroll/app/routes/collections.$handle.tsx b/examples/infinite-scroll/app/routes/collections.$handle.tsx index 66186a8ba8..cffe926e56 100644 --- a/examples/infinite-scroll/app/routes/collections.$handle.tsx +++ b/examples/infinite-scroll/app/routes/collections.$handle.tsx @@ -1,4 +1,4 @@ -import {defer, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { useLoaderData, useNavigate, @@ -28,7 +28,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/examples/legacy-customer-account-flow/app/root.tsx b/examples/legacy-customer-account-flow/app/root.tsx index 8b13edb28e..cc3b6ef801 100644 --- a/examples/legacy-customer-account-flow/app/root.tsx +++ b/examples/legacy-customer-account-flow/app/root.tsx @@ -1,5 +1,5 @@ import {useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -99,7 +99,7 @@ export async function loader({context}: LoaderFunctionArgs) { }, }); - return defer( + return data( { cart: cartPromise, footer: footerPromise, diff --git a/examples/legacy-customer-account-flow/app/routes/account.addresses.tsx b/examples/legacy-customer-account-flow/app/routes/account.addresses.tsx index 23ff85b0df..76ae8b40ae 100644 --- a/examples/legacy-customer-account-flow/app/routes/account.addresses.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account.addresses.tsx @@ -1,7 +1,7 @@ import type {MailingAddressInput} from '@shopify/hydrogen/storefront-api-types'; import type {AddressFragment, CustomerFragment} from 'storefrontapi.generated'; import { - json, + data, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -33,7 +33,7 @@ export async function loader({context}: LoaderFunctionArgs) { if (!customerAccessToken) { return redirect('/account/login'); } - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { @@ -51,7 +51,7 @@ export async function action({request, context}: ActionFunctionArgs) { const customerAccessToken = await session.get('customerAccessToken'); if (!customerAccessToken) { - return json({error: {[addressId]: 'Unauthorized'}}, {status: 401}); + return data({error: {[addressId]: 'Unauthorized'}}, {status: 401}); } const {accessToken} = customerAccessToken; @@ -120,12 +120,12 @@ export async function action({request, context}: ActionFunctionArgs) { } } - return json({error: null, createdAddress, defaultAddress}); + return {error: null, createdAddress, defaultAddress}; } catch (error: unknown) { if (error instanceof Error) { - return json({error: {[addressId]: error.message}}, {status: 400}); + return data({error: {[addressId]: error.message}}, {status: 400}); } - return json({error: {[addressId]: error}}, {status: 400}); + return data({error: {[addressId]: error}}, {status: 400}); } } @@ -167,12 +167,12 @@ export async function action({request, context}: ActionFunctionArgs) { } } - return json({error: null, updatedAddress, defaultAddress}); + return {error: null, updatedAddress, defaultAddress}; } catch (error: unknown) { if (error instanceof Error) { - return json({error: {[addressId]: error.message}}, {status: 400}); + return data({error: {[addressId]: error.message}}, {status: 400}); } - return json({error: {[addressId]: error}}, {status: 400}); + return data({error: {[addressId]: error}}, {status: 400}); } } @@ -190,17 +190,17 @@ export async function action({request, context}: ActionFunctionArgs) { const error = customerAddressDelete.customerUserErrors[0]; throw new Error(error.message); } - return json({error: null, deletedAddress: addressId}); + return {error: null, deletedAddress: addressId}; } catch (error: unknown) { if (error instanceof Error) { - return json({error: {[addressId]: error.message}}, {status: 400}); + return data({error: {[addressId]: error.message}}, {status: 400}); } - return json({error: {[addressId]: error}}, {status: 400}); + return data({error: {[addressId]: error}}, {status: 400}); } } default: { - return json( + return data( {error: {[addressId]: 'Method not allowed'}}, {status: 405}, ); @@ -208,9 +208,9 @@ export async function action({request, context}: ActionFunctionArgs) { } } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/legacy-customer-account-flow/app/routes/account.orders.$id.tsx b/examples/legacy-customer-account-flow/app/routes/account.orders.$id.tsx index 9d4feb4a4f..932ee97af9 100644 --- a/examples/legacy-customer-account-flow/app/routes/account.orders.$id.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account.orders.$id.tsx @@ -1,4 +1,4 @@ -import {json, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Link, useLoaderData, type MetaFunction} from '@remix-run/react'; import {Money, Image, flattenConnection} from '@shopify/hydrogen'; import type {OrderLineItemFullFragment} from 'storefrontapi.generated'; @@ -41,12 +41,12 @@ export async function loader({params, context}: LoaderFunctionArgs) { firstDiscount?.__typename === 'PricingPercentageValue' && firstDiscount?.percentage; - return json({ + return { order, lineItems, discountValue, discountPercentage, - }); + }; } export default function OrderRoute() { diff --git a/examples/legacy-customer-account-flow/app/routes/account.orders._index.tsx b/examples/legacy-customer-account-flow/app/routes/account.orders._index.tsx index 4604fb568d..d5bda5f2f3 100644 --- a/examples/legacy-customer-account-flow/app/routes/account.orders._index.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account.orders._index.tsx @@ -1,6 +1,6 @@ import {Link, useLoaderData, type MetaFunction} from '@remix-run/react'; import {Money, Pagination, getPaginationVariables} from '@shopify/hydrogen'; -import {json, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import type { CustomerOrdersFragment, OrderItemFragment, @@ -37,12 +37,12 @@ export async function loader({request, context}: LoaderFunctionArgs) { throw new Error('Customer not found'); } - return json({customer}); + return {customer}; } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/legacy-customer-account-flow/app/routes/account.profile.tsx b/examples/legacy-customer-account-flow/app/routes/account.profile.tsx index 3edf686d8a..b1a834b302 100644 --- a/examples/legacy-customer-account-flow/app/routes/account.profile.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account.profile.tsx @@ -1,7 +1,7 @@ import type {CustomerFragment} from 'storefrontapi.generated'; import type {CustomerUpdateInput} from '@shopify/hydrogen/storefront-api-types'; import { - json, + data, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -28,20 +28,20 @@ export async function loader({context}: LoaderFunctionArgs) { if (!customerAccessToken) { return redirect('/account/login'); } - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { const {session, storefront} = context; if (request.method !== 'PUT') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } const form = await request.formData(); const customerAccessToken = await session.get('customerAccessToken'); if (!customerAccessToken) { - return json({error: 'Unauthorized'}, {status: 401}); + return data({error: 'Unauthorized'}, {status: 401}); } try { @@ -80,7 +80,7 @@ export async function action({request, context}: ActionFunctionArgs) { // check for mutation errors if (updated.customerUpdate?.customerUserErrors?.length) { - return json( + return data( {error: updated.customerUpdate?.customerUserErrors[0]}, {status: 400}, ); @@ -94,9 +94,9 @@ export async function action({request, context}: ActionFunctionArgs) { ); } - return json({error: null, customer: updated.customerUpdate?.customer}); + return {error: null, customer: updated.customerUpdate?.customer}; } catch (error: any) { - return json({error: error.message, customer: null}, {status: 400}); + return data({error: error.message, customer: null}, {status: 400}); } } diff --git a/examples/legacy-customer-account-flow/app/routes/account.tsx b/examples/legacy-customer-account-flow/app/routes/account.tsx index 913cd65a93..6547c07f92 100644 --- a/examples/legacy-customer-account-flow/app/routes/account.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account.tsx @@ -1,5 +1,5 @@ import {Form, NavLink, Outlet, useLoaderData} from '@remix-run/react'; -import {json, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import type {CustomerFragment} from 'storefrontapi.generated'; export function shouldRevalidate() { @@ -23,12 +23,12 @@ export async function loader({request, context}: LoaderFunctionArgs) { return redirect('/account/login'); } else { // public subroute such as /account/login... - return json({ + return { isLoggedIn: false, isAccountHome, isPrivateRoute, customer: null, - }); + }; } } else { // loggedIn, default redirect to the orders page @@ -51,7 +51,7 @@ export async function loader({request, context}: LoaderFunctionArgs) { throw new Error('Customer not found'); } - return json( + return data( {isLoggedIn, isPrivateRoute, isAccountHome, customer}, { headers: { diff --git a/examples/legacy-customer-account-flow/app/routes/account_.activate.$id.$activationToken.tsx b/examples/legacy-customer-account-flow/app/routes/account_.activate.$id.$activationToken.tsx index 49d1c4106e..cd7f55ba94 100644 --- a/examples/legacy-customer-account-flow/app/routes/account_.activate.$id.$activationToken.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account_.activate.$id.$activationToken.tsx @@ -1,5 +1,5 @@ import { - json, + data, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -18,7 +18,7 @@ export async function loader({context}: LoaderFunctionArgs) { if (await context.session.get('customerAccessToken')) { return redirect('/account'); } - return json({}); + return {}; } export async function action({request, context, params}: ActionFunctionArgs) { @@ -26,7 +26,7 @@ export async function action({request, context, params}: ActionFunctionArgs) { const {id, activationToken} = params; if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } try { @@ -73,9 +73,9 @@ export async function action({request, context, params}: ActionFunctionArgs) { return redirect('/account'); } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/legacy-customer-account-flow/app/routes/account_.login.tsx b/examples/legacy-customer-account-flow/app/routes/account_.login.tsx index f3537f8ae2..fc3564188c 100644 --- a/examples/legacy-customer-account-flow/app/routes/account_.login.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account_.login.tsx @@ -1,5 +1,5 @@ import { - json, + data, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -18,14 +18,14 @@ export async function loader({context}: LoaderFunctionArgs) { if (await context.session.get('customerAccessToken')) { return redirect('/account'); } - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { const {session, storefront} = context; if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } try { @@ -57,9 +57,9 @@ export async function action({request, context}: ActionFunctionArgs) { return redirect('/account'); } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/legacy-customer-account-flow/app/routes/account_.logout.tsx b/examples/legacy-customer-account-flow/app/routes/account_.logout.tsx index 8981fc3993..31a41bc501 100644 --- a/examples/legacy-customer-account-flow/app/routes/account_.logout.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account_.logout.tsx @@ -1,4 +1,4 @@ -import {json, redirect, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, redirect, type ActionFunctionArgs} from '@shopify/remix-oxygen'; import {type MetaFunction} from '@remix-run/react'; export const meta: MetaFunction = () => { @@ -14,7 +14,7 @@ export async function action({request, context}: ActionFunctionArgs) { session.unset('customerAccessToken'); if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } return redirect('/'); diff --git a/examples/legacy-customer-account-flow/app/routes/account_.recover.tsx b/examples/legacy-customer-account-flow/app/routes/account_.recover.tsx index 25a20fea91..aded9d8530 100644 --- a/examples/legacy-customer-account-flow/app/routes/account_.recover.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account_.recover.tsx @@ -1,5 +1,5 @@ import { - json, + data, redirect, type LoaderFunctionArgs, type ActionFunctionArgs, @@ -17,7 +17,7 @@ export async function loader({context}: LoaderFunctionArgs) { return redirect('/account'); } - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { @@ -26,7 +26,7 @@ export async function action({request, context}: ActionFunctionArgs) { const email = form.has('email') ? String(form.get('email')) : null; if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } try { @@ -37,13 +37,13 @@ export async function action({request, context}: ActionFunctionArgs) { variables: {email}, }); - return json({resetRequested: true}); + return {resetRequested: true}; } catch (error: unknown) { const resetRequested = false; if (error instanceof Error) { - return json({error: error.message, resetRequested}, {status: 400}); + return data({error: error.message, resetRequested}, {status: 400}); } - return json({error, resetRequested}, {status: 400}); + return data({error, resetRequested}, {status: 400}); } } diff --git a/examples/legacy-customer-account-flow/app/routes/account_.register.tsx b/examples/legacy-customer-account-flow/app/routes/account_.register.tsx index 2bfa62a39e..64c8a104ee 100644 --- a/examples/legacy-customer-account-flow/app/routes/account_.register.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account_.register.tsx @@ -1,5 +1,5 @@ import { - json, + data, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -20,12 +20,12 @@ export async function loader({context}: LoaderFunctionArgs) { return redirect('/account'); } - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } const {storefront, session} = context; @@ -85,7 +85,7 @@ export async function action({request, context}: ActionFunctionArgs) { customerAccessTokenCreate?.customerAccessToken, ); - return json( + return data( {error: null, newCustomer}, { status: 302, @@ -96,9 +96,9 @@ export async function action({request, context}: ActionFunctionArgs) { ); } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/legacy-customer-account-flow/app/routes/account_.reset.$id.$resetToken.tsx b/examples/legacy-customer-account-flow/app/routes/account_.reset.$id.$resetToken.tsx index 9838590d1f..205e4ab9b8 100644 --- a/examples/legacy-customer-account-flow/app/routes/account_.reset.$id.$resetToken.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account_.reset.$id.$resetToken.tsx @@ -1,4 +1,4 @@ -import {type ActionFunctionArgs, json, redirect} from '@shopify/remix-oxygen'; +import {type ActionFunctionArgs, data, redirect} from '@shopify/remix-oxygen'; import {Form, useActionData, type MetaFunction} from '@remix-run/react'; type ActionResponse = { @@ -11,7 +11,7 @@ export const meta: MetaFunction = () => { export async function action({request, context, params}: ActionFunctionArgs) { if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } const {id, resetToken} = params; const {session, storefront} = context; @@ -50,9 +50,9 @@ export async function action({request, context, params}: ActionFunctionArgs) { return redirect('/account'); } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/legacy-customer-account-flow/app/routes/cart.tsx b/examples/legacy-customer-account-flow/app/routes/cart.tsx index afc7cc057f..c8f5e49e59 100644 --- a/examples/legacy-customer-account-flow/app/routes/cart.tsx +++ b/examples/legacy-customer-account-flow/app/routes/cart.tsx @@ -1,7 +1,7 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {json, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; import {CartMain} from '~/components/CartMain'; import type {RootLoader} from '~/root'; @@ -93,7 +93,7 @@ export async function action({request, context}: ActionFunctionArgs) { headers.set('Location', redirectTo); } - return json( + return data( { cart: cartResult, errors, @@ -107,7 +107,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() { diff --git a/examples/metaobjects/README.md b/examples/metaobjects/README.md index 584f9706f7..951b547326 100644 --- a/examples/metaobjects/README.md +++ b/examples/metaobjects/README.md @@ -76,11 +76,13 @@ To enable the Edit Route button return the env variable as `publicStoreSubdomain like so ```ts +import {data, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; + export async function loader({context}: LoaderFunctionArgs) { // other code ... const publicStoreDomain = context.env.PUBLIC_STORE_DOMAIN; - return defer( + return data( { // other code ... publicStoreSubdomain: context.env.PUBLIC_SHOPIFY_STORE_DOMAIN, @@ -109,7 +111,7 @@ export async function loader({context}: LoaderFunctionArgs) { cache: storefront.CacheNone(), }); - return json({route}); + return {route}; } ``` diff --git a/examples/metaobjects/app/root.tsx b/examples/metaobjects/app/root.tsx index 644cac9432..fd5bc8604e 100644 --- a/examples/metaobjects/app/root.tsx +++ b/examples/metaobjects/app/root.tsx @@ -1,5 +1,5 @@ import {useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -87,7 +87,7 @@ export async function loader({context}: LoaderFunctionArgs) { }, }); - return defer({ + return { cart: cartPromise, footer: footerPromise, header: await headerPromise, @@ -110,7 +110,7 @@ export async function loader({context}: LoaderFunctionArgs) { publictoreSubdomain: context.env.PUBLIC_SHOPIFY_STORE_DOMAIN, /********** EXAMPLE UPDATE END ************/ /***********************************************/ - }); + }; } export function Layout({children}: {children?: React.ReactNode}) { diff --git a/examples/metaobjects/app/routes/_index.tsx b/examples/metaobjects/app/routes/_index.tsx index 2c73a22cea..d08f5199f8 100644 --- a/examples/metaobjects/app/routes/_index.tsx +++ b/examples/metaobjects/app/routes/_index.tsx @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, type MetaFunction} from '@remix-run/react'; /***********************************************/ @@ -20,7 +20,7 @@ export async function loader({context}: LoaderFunctionArgs) { cache: storefront.CacheNone(), }); - return json({route}); + return {route}; } export default function Homepage() { diff --git a/examples/metaobjects/app/routes/stores.$name.tsx b/examples/metaobjects/app/routes/stores.$name.tsx index 0aac0bc40d..8985926202 100644 --- a/examples/metaobjects/app/routes/stores.$name.tsx +++ b/examples/metaobjects/app/routes/stores.$name.tsx @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, type MetaFunction} from '@remix-run/react'; // 1. Add metaobject content imports @@ -16,7 +16,7 @@ export async function loader({context, params}: LoaderFunctionArgs) { variables: {handle: `route-${name}`}, }); - return json({route}); + return {route}; } export default function Store() { diff --git a/examples/metaobjects/app/routes/stores._index.tsx b/examples/metaobjects/app/routes/stores._index.tsx index 5a4a032303..a2a63020d3 100644 --- a/examples/metaobjects/app/routes/stores._index.tsx +++ b/examples/metaobjects/app/routes/stores._index.tsx @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, type MetaFunction} from '@remix-run/react'; // 1. Add metaobject content imports @@ -17,7 +17,7 @@ export async function loader({context}: LoaderFunctionArgs) { cache: storefront.CacheNone(), }); - return json({route}); + return {route}; } export default function Stores() { diff --git a/examples/multipass/app/root.tsx b/examples/multipass/app/root.tsx index 8b13edb28e..cc3b6ef801 100644 --- a/examples/multipass/app/root.tsx +++ b/examples/multipass/app/root.tsx @@ -1,5 +1,5 @@ import {useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -99,7 +99,7 @@ export async function loader({context}: LoaderFunctionArgs) { }, }); - return defer( + return data( { cart: cartPromise, footer: footerPromise, diff --git a/examples/multipass/app/routes/account.addresses.tsx b/examples/multipass/app/routes/account.addresses.tsx index 23ff85b0df..76ae8b40ae 100644 --- a/examples/multipass/app/routes/account.addresses.tsx +++ b/examples/multipass/app/routes/account.addresses.tsx @@ -1,7 +1,7 @@ import type {MailingAddressInput} from '@shopify/hydrogen/storefront-api-types'; import type {AddressFragment, CustomerFragment} from 'storefrontapi.generated'; import { - json, + data, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -33,7 +33,7 @@ export async function loader({context}: LoaderFunctionArgs) { if (!customerAccessToken) { return redirect('/account/login'); } - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { @@ -51,7 +51,7 @@ export async function action({request, context}: ActionFunctionArgs) { const customerAccessToken = await session.get('customerAccessToken'); if (!customerAccessToken) { - return json({error: {[addressId]: 'Unauthorized'}}, {status: 401}); + return data({error: {[addressId]: 'Unauthorized'}}, {status: 401}); } const {accessToken} = customerAccessToken; @@ -120,12 +120,12 @@ export async function action({request, context}: ActionFunctionArgs) { } } - return json({error: null, createdAddress, defaultAddress}); + return {error: null, createdAddress, defaultAddress}; } catch (error: unknown) { if (error instanceof Error) { - return json({error: {[addressId]: error.message}}, {status: 400}); + return data({error: {[addressId]: error.message}}, {status: 400}); } - return json({error: {[addressId]: error}}, {status: 400}); + return data({error: {[addressId]: error}}, {status: 400}); } } @@ -167,12 +167,12 @@ export async function action({request, context}: ActionFunctionArgs) { } } - return json({error: null, updatedAddress, defaultAddress}); + return {error: null, updatedAddress, defaultAddress}; } catch (error: unknown) { if (error instanceof Error) { - return json({error: {[addressId]: error.message}}, {status: 400}); + return data({error: {[addressId]: error.message}}, {status: 400}); } - return json({error: {[addressId]: error}}, {status: 400}); + return data({error: {[addressId]: error}}, {status: 400}); } } @@ -190,17 +190,17 @@ export async function action({request, context}: ActionFunctionArgs) { const error = customerAddressDelete.customerUserErrors[0]; throw new Error(error.message); } - return json({error: null, deletedAddress: addressId}); + return {error: null, deletedAddress: addressId}; } catch (error: unknown) { if (error instanceof Error) { - return json({error: {[addressId]: error.message}}, {status: 400}); + return data({error: {[addressId]: error.message}}, {status: 400}); } - return json({error: {[addressId]: error}}, {status: 400}); + return data({error: {[addressId]: error}}, {status: 400}); } } default: { - return json( + return data( {error: {[addressId]: 'Method not allowed'}}, {status: 405}, ); @@ -208,9 +208,9 @@ export async function action({request, context}: ActionFunctionArgs) { } } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/multipass/app/routes/account.orders.$id.tsx b/examples/multipass/app/routes/account.orders.$id.tsx index 9d4feb4a4f..932ee97af9 100644 --- a/examples/multipass/app/routes/account.orders.$id.tsx +++ b/examples/multipass/app/routes/account.orders.$id.tsx @@ -1,4 +1,4 @@ -import {json, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Link, useLoaderData, type MetaFunction} from '@remix-run/react'; import {Money, Image, flattenConnection} from '@shopify/hydrogen'; import type {OrderLineItemFullFragment} from 'storefrontapi.generated'; @@ -41,12 +41,12 @@ export async function loader({params, context}: LoaderFunctionArgs) { firstDiscount?.__typename === 'PricingPercentageValue' && firstDiscount?.percentage; - return json({ + return { order, lineItems, discountValue, discountPercentage, - }); + }; } export default function OrderRoute() { diff --git a/examples/multipass/app/routes/account.orders._index.tsx b/examples/multipass/app/routes/account.orders._index.tsx index 4604fb568d..d5bda5f2f3 100644 --- a/examples/multipass/app/routes/account.orders._index.tsx +++ b/examples/multipass/app/routes/account.orders._index.tsx @@ -1,6 +1,6 @@ import {Link, useLoaderData, type MetaFunction} from '@remix-run/react'; import {Money, Pagination, getPaginationVariables} from '@shopify/hydrogen'; -import {json, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import type { CustomerOrdersFragment, OrderItemFragment, @@ -37,12 +37,12 @@ export async function loader({request, context}: LoaderFunctionArgs) { throw new Error('Customer not found'); } - return json({customer}); + return {customer}; } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/multipass/app/routes/account.profile.tsx b/examples/multipass/app/routes/account.profile.tsx index 3edf686d8a..b1a834b302 100644 --- a/examples/multipass/app/routes/account.profile.tsx +++ b/examples/multipass/app/routes/account.profile.tsx @@ -1,7 +1,7 @@ import type {CustomerFragment} from 'storefrontapi.generated'; import type {CustomerUpdateInput} from '@shopify/hydrogen/storefront-api-types'; import { - json, + data, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -28,20 +28,20 @@ export async function loader({context}: LoaderFunctionArgs) { if (!customerAccessToken) { return redirect('/account/login'); } - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { const {session, storefront} = context; if (request.method !== 'PUT') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } const form = await request.formData(); const customerAccessToken = await session.get('customerAccessToken'); if (!customerAccessToken) { - return json({error: 'Unauthorized'}, {status: 401}); + return data({error: 'Unauthorized'}, {status: 401}); } try { @@ -80,7 +80,7 @@ export async function action({request, context}: ActionFunctionArgs) { // check for mutation errors if (updated.customerUpdate?.customerUserErrors?.length) { - return json( + return data( {error: updated.customerUpdate?.customerUserErrors[0]}, {status: 400}, ); @@ -94,9 +94,9 @@ export async function action({request, context}: ActionFunctionArgs) { ); } - return json({error: null, customer: updated.customerUpdate?.customer}); + return {error: null, customer: updated.customerUpdate?.customer}; } catch (error: any) { - return json({error: error.message, customer: null}, {status: 400}); + return data({error: error.message, customer: null}, {status: 400}); } } diff --git a/examples/multipass/app/routes/account.tsx b/examples/multipass/app/routes/account.tsx index 913cd65a93..6547c07f92 100644 --- a/examples/multipass/app/routes/account.tsx +++ b/examples/multipass/app/routes/account.tsx @@ -1,5 +1,5 @@ import {Form, NavLink, Outlet, useLoaderData} from '@remix-run/react'; -import {json, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import type {CustomerFragment} from 'storefrontapi.generated'; export function shouldRevalidate() { @@ -23,12 +23,12 @@ export async function loader({request, context}: LoaderFunctionArgs) { return redirect('/account/login'); } else { // public subroute such as /account/login... - return json({ + return { isLoggedIn: false, isAccountHome, isPrivateRoute, customer: null, - }); + }; } } else { // loggedIn, default redirect to the orders page @@ -51,7 +51,7 @@ export async function loader({request, context}: LoaderFunctionArgs) { throw new Error('Customer not found'); } - return json( + return data( {isLoggedIn, isPrivateRoute, isAccountHome, customer}, { headers: { diff --git a/examples/multipass/app/routes/account_.activate.$id.$activationToken.tsx b/examples/multipass/app/routes/account_.activate.$id.$activationToken.tsx index 49d1c4106e..cd7f55ba94 100644 --- a/examples/multipass/app/routes/account_.activate.$id.$activationToken.tsx +++ b/examples/multipass/app/routes/account_.activate.$id.$activationToken.tsx @@ -1,5 +1,5 @@ import { - json, + data, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -18,7 +18,7 @@ export async function loader({context}: LoaderFunctionArgs) { if (await context.session.get('customerAccessToken')) { return redirect('/account'); } - return json({}); + return {}; } export async function action({request, context, params}: ActionFunctionArgs) { @@ -26,7 +26,7 @@ export async function action({request, context, params}: ActionFunctionArgs) { const {id, activationToken} = params; if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } try { @@ -73,9 +73,9 @@ export async function action({request, context, params}: ActionFunctionArgs) { return redirect('/account'); } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/multipass/app/routes/account_.login.multipass.tsx b/examples/multipass/app/routes/account_.login.multipass.tsx index bd28c6712b..07a26074a3 100644 --- a/examples/multipass/app/routes/account_.login.multipass.tsx +++ b/examples/multipass/app/routes/account_.login.multipass.tsx @@ -1,5 +1,5 @@ import { - json, + data as remixData, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -124,7 +124,7 @@ export async function action({request, context}: ActionFunctionArgs) { } // success, return token, url - return json( + return remixData( {data: {...data, error: null}}, { status: 200, @@ -162,7 +162,7 @@ export async function action({request, context}: ActionFunctionArgs) { } function handleMethodNotAllowed() { - return json( + return remixData( { data: null, error: 'Method not allowed.', @@ -175,7 +175,7 @@ function handleMethodNotAllowed() { } function handleOptionsPreflight(origin: string) { - return json(null, { + return remixData(null, { status: 204, headers: getCorsHeaders(origin), }); @@ -207,7 +207,7 @@ async function handleLoggedOutResponse(options: { // For example, checkoutDomain `checkout.hydrogen.shop` or `shop.example.com` or `{shop}.myshopify.com`. const logOutUrl = `https://${checkoutDomain}/account/logout?return_url=${encodedCheckoutUrl}&step=contact_information`; - return json({data: {url: logOutUrl}, error: null}); + return {data: {url: logOutUrl}, error: null}; } /* @@ -238,7 +238,7 @@ function notLoggedInResponse(options: NotLoggedInResponseType) { } // Always return the original URL. - return json({data: {url}, error}); + return {data: {url}, error}; } function getCorsHeaders(origin: string): {[key: string]: string} { diff --git a/examples/multipass/app/routes/account_.login.tsx b/examples/multipass/app/routes/account_.login.tsx index f3537f8ae2..fc3564188c 100644 --- a/examples/multipass/app/routes/account_.login.tsx +++ b/examples/multipass/app/routes/account_.login.tsx @@ -1,5 +1,5 @@ import { - json, + data, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -18,14 +18,14 @@ export async function loader({context}: LoaderFunctionArgs) { if (await context.session.get('customerAccessToken')) { return redirect('/account'); } - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { const {session, storefront} = context; if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } try { @@ -57,9 +57,9 @@ export async function action({request, context}: ActionFunctionArgs) { return redirect('/account'); } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/multipass/app/routes/account_.logout.tsx b/examples/multipass/app/routes/account_.logout.tsx index 8981fc3993..31a41bc501 100644 --- a/examples/multipass/app/routes/account_.logout.tsx +++ b/examples/multipass/app/routes/account_.logout.tsx @@ -1,4 +1,4 @@ -import {json, redirect, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, redirect, type ActionFunctionArgs} from '@shopify/remix-oxygen'; import {type MetaFunction} from '@remix-run/react'; export const meta: MetaFunction = () => { @@ -14,7 +14,7 @@ export async function action({request, context}: ActionFunctionArgs) { session.unset('customerAccessToken'); if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } return redirect('/'); diff --git a/examples/multipass/app/routes/account_.recover.tsx b/examples/multipass/app/routes/account_.recover.tsx index 25a20fea91..aded9d8530 100644 --- a/examples/multipass/app/routes/account_.recover.tsx +++ b/examples/multipass/app/routes/account_.recover.tsx @@ -1,5 +1,5 @@ import { - json, + data, redirect, type LoaderFunctionArgs, type ActionFunctionArgs, @@ -17,7 +17,7 @@ export async function loader({context}: LoaderFunctionArgs) { return redirect('/account'); } - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { @@ -26,7 +26,7 @@ export async function action({request, context}: ActionFunctionArgs) { const email = form.has('email') ? String(form.get('email')) : null; if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } try { @@ -37,13 +37,13 @@ export async function action({request, context}: ActionFunctionArgs) { variables: {email}, }); - return json({resetRequested: true}); + return {resetRequested: true}; } catch (error: unknown) { const resetRequested = false; if (error instanceof Error) { - return json({error: error.message, resetRequested}, {status: 400}); + return data({error: error.message, resetRequested}, {status: 400}); } - return json({error, resetRequested}, {status: 400}); + return data({error, resetRequested}, {status: 400}); } } diff --git a/examples/multipass/app/routes/account_.register.tsx b/examples/multipass/app/routes/account_.register.tsx index 2bfa62a39e..64c8a104ee 100644 --- a/examples/multipass/app/routes/account_.register.tsx +++ b/examples/multipass/app/routes/account_.register.tsx @@ -1,5 +1,5 @@ import { - json, + data, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -20,12 +20,12 @@ export async function loader({context}: LoaderFunctionArgs) { return redirect('/account'); } - return json({}); + return {}; } export async function action({request, context}: ActionFunctionArgs) { if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } const {storefront, session} = context; @@ -85,7 +85,7 @@ export async function action({request, context}: ActionFunctionArgs) { customerAccessTokenCreate?.customerAccessToken, ); - return json( + return data( {error: null, newCustomer}, { status: 302, @@ -96,9 +96,9 @@ export async function action({request, context}: ActionFunctionArgs) { ); } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/multipass/app/routes/account_.reset.$id.$resetToken.tsx b/examples/multipass/app/routes/account_.reset.$id.$resetToken.tsx index 9838590d1f..205e4ab9b8 100644 --- a/examples/multipass/app/routes/account_.reset.$id.$resetToken.tsx +++ b/examples/multipass/app/routes/account_.reset.$id.$resetToken.tsx @@ -1,4 +1,4 @@ -import {type ActionFunctionArgs, json, redirect} from '@shopify/remix-oxygen'; +import {type ActionFunctionArgs, data, redirect} from '@shopify/remix-oxygen'; import {Form, useActionData, type MetaFunction} from '@remix-run/react'; type ActionResponse = { @@ -11,7 +11,7 @@ export const meta: MetaFunction = () => { export async function action({request, context, params}: ActionFunctionArgs) { if (request.method !== 'POST') { - return json({error: 'Method not allowed'}, {status: 405}); + return data({error: 'Method not allowed'}, {status: 405}); } const {id, resetToken} = params; const {session, storefront} = context; @@ -50,9 +50,9 @@ export async function action({request, context, params}: ActionFunctionArgs) { return redirect('/account'); } catch (error: unknown) { if (error instanceof Error) { - return json({error: error.message}, {status: 400}); + return data({error: error.message}, {status: 400}); } - return json({error}, {status: 400}); + return data({error}, {status: 400}); } } diff --git a/examples/multipass/app/routes/cart.tsx b/examples/multipass/app/routes/cart.tsx index ecbc71dcfc..6f10b7cc87 100644 --- a/examples/multipass/app/routes/cart.tsx +++ b/examples/multipass/app/routes/cart.tsx @@ -1,7 +1,7 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {json, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; import {CartMain} from '~/components/Cart'; export const meta: MetaFunction = () => { @@ -92,7 +92,7 @@ export async function action({request, context}: ActionFunctionArgs) { headers.set('Location', redirectTo); } - return json( + return data( { cart: cartResult, errors, @@ -106,7 +106,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() { diff --git a/examples/multipass/vite.config.ts b/examples/multipass/vite.config.ts index d0ce6d1328..bb5f7412fd 100644 --- a/examples/multipass/vite.config.ts +++ b/examples/multipass/vite.config.ts @@ -15,6 +15,7 @@ export default defineConfig({ v3_relativeSplatPath: true, v3_throwAbortReason: true, v3_lazyRouteDiscovery: true, + v3_singleFetch: true, }, }), tsconfigPaths(), diff --git a/examples/partytown/README.md b/examples/partytown/README.md index f2def26664..964981fcdd 100644 --- a/examples/partytown/README.md +++ b/examples/partytown/README.md @@ -98,7 +98,7 @@ Update the `loader` function ```ts export async function loader({context}: LoaderFunctionArgs) { const layout = await context.storefront.query<{shop: Shop}>(LAYOUT_QUERY); - return json( + return data( { layout, // 1. Pass the GTM container ID to the client diff --git a/examples/partytown/app/entry.server.tsx b/examples/partytown/app/entry.server.tsx index 7b1b4e72fb..ba1b0ae7dd 100644 --- a/examples/partytown/app/entry.server.tsx +++ b/examples/partytown/app/entry.server.tsx @@ -25,7 +25,7 @@ export default async function handleRequest( const body = await renderToReadableStream( - + , { nonce, diff --git a/examples/partytown/app/root.tsx b/examples/partytown/app/root.tsx index 19a5bdd0b8..455cda7b89 100644 --- a/examples/partytown/app/root.tsx +++ b/examples/partytown/app/root.tsx @@ -1,5 +1,5 @@ import {Script, useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -81,7 +81,7 @@ export async function loader(args: LoaderFunctionArgs) { const {storefront, env} = args.context; - return defer({ + return { ...deferredData, ...criticalData, publicStoreDomain: env.PUBLIC_STORE_DOMAIN, @@ -102,7 +102,7 @@ export async function loader(args: LoaderFunctionArgs) { gtmContainerId: args.context.env.GTM_CONTAINER_ID, /********** EXAMPLE UPDATE END ************/ /***********************************************/ - }); + }; } /** diff --git a/examples/subscriptions/README.md b/examples/subscriptions/README.md index e385234f7d..df5ec3798a 100644 --- a/examples/subscriptions/README.md +++ b/examples/subscriptions/README.md @@ -229,7 +229,7 @@ export async function loader({params, request, context}: LoaderFunctionArgs) { const selectedVariant = product.variants.nodes[0]; // 5. Pass the selectedSellingPlan to the client - return json({product, selectedVariant, selectedSellingPlan}); + return {product, selectedVariant, selectedSellingPlan}; } ``` diff --git a/examples/subscriptions/app/routes/products.$handle.tsx b/examples/subscriptions/app/routes/products.$handle.tsx index 16ddaccdc3..31b6cf8539 100644 --- a/examples/subscriptions/app/routes/products.$handle.tsx +++ b/examples/subscriptions/app/routes/products.$handle.tsx @@ -1,5 +1,5 @@ import {Suspense} from 'react'; -import {defer, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Await, Link, @@ -129,7 +129,7 @@ export async function loader({params, request, context}: LoaderFunctionArgs) { variables: {handle}, }); - return defer({ + return { product, variants, /***********************************************/ @@ -138,7 +138,7 @@ export async function loader({params, request, context}: LoaderFunctionArgs) { selectedSellingPlan, /********** EXAMPLE UPDATE END ************/ /***********************************************/ - }); + }; } function redirectToFirstVariant({ diff --git a/examples/third-party-queries-caching/README.md b/examples/third-party-queries-caching/README.md index 74dde5f870..0a5298b39c 100644 --- a/examples/third-party-queries-caching/README.md +++ b/examples/third-party-queries-caching/README.md @@ -127,7 +127,7 @@ export async function loader({context}: LoaderFunctionArgs) { const {characters} = await context.rickAndMorty.query(CHARACTERS_QUERY, { cache: CacheShort(), }); - return json({characters}); + return {characters}; } ``` diff --git a/examples/third-party-queries-caching/app/routes/_index.tsx b/examples/third-party-queries-caching/app/routes/_index.tsx index 7739219a8b..4b9d6bcccd 100644 --- a/examples/third-party-queries-caching/app/routes/_index.tsx +++ b/examples/third-party-queries-caching/app/routes/_index.tsx @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData} from '@remix-run/react'; import {CacheShort} from '@shopify/hydrogen'; @@ -7,7 +7,7 @@ export async function loader({context}: LoaderFunctionArgs) { const {characters} = await context.rickAndMorty.query(CHARACTERS_QUERY, { cache: CacheShort(), }); - return json({characters}); + return {characters}; } type Character = { diff --git a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.collectionView.example.jsx b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.collectionView.example.jsx index 256d67da51..13b838ce8c 100644 --- a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.collectionView.example.jsx +++ b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.collectionView.example.jsx @@ -1,15 +1,14 @@ import {useLoaderData} from '@remix-run/react'; -import {json} from '@shopify/remix-oxygen'; import {Analytics} from '@shopify/hydrogen'; export async function loader() { - return json({ + return { collection: { id: '123', title: 'ABC', handle: 'abc', }, - }); + }; } export default function Collection() { diff --git a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.collectionView.example.tsx b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.collectionView.example.tsx index 5ede664723..ec45c2c72c 100644 --- a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.collectionView.example.tsx +++ b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.collectionView.example.tsx @@ -1,15 +1,14 @@ import {useLoaderData} from '@remix-run/react'; -import {json} from '@shopify/remix-oxygen'; import {Analytics} from '@shopify/hydrogen'; export async function loader() { - return json({ + return { collection: { id: '123', title: 'ABC', handle: 'abc', }, - }); + }; } export default function Collection() { diff --git a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.example.jsx b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.example.jsx index fcafd50ae0..a5c9445726 100644 --- a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.example.jsx +++ b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.example.jsx @@ -1,12 +1,11 @@ import {Analytics, getShopAnalytics} from '@shopify/hydrogen'; -import {defer} from '@shopify/remix-oxygen'; import {Outlet, useLoaderData} from '@remix-run/react'; export async function loader({context}) { const {cart, env} = context; const cartPromise = cart.get(); - return defer({ + return { cart: cartPromise, shop: getShopAnalytics(context), consent: { @@ -17,7 +16,7 @@ export async function loader({context}) { country: context.storefront.i18n.country, language: context.storefront.i18n.language, }, - }); + }; } export default function App() { diff --git a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.example.tsx b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.example.tsx index 2d7ec489cb..1786a5003a 100644 --- a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.example.tsx +++ b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.example.tsx @@ -1,12 +1,12 @@ import {Analytics, getShopAnalytics} from '@shopify/hydrogen'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Outlet, useLoaderData} from '@remix-run/react'; export async function loader({context}: LoaderFunctionArgs) { const {cart, env} = context; const cartPromise = cart.get(); - return defer({ + return { cart: cartPromise, shop: getShopAnalytics({ storefront: context.storefront, @@ -20,7 +20,7 @@ export async function loader({context}: LoaderFunctionArgs) { country: context.storefront.i18n.country, language: context.storefront.i18n.language, }, - }); + }; } export default function App() { diff --git a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.productView.example.jsx b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.productView.example.jsx index 6d59bf5689..5f3b698e8e 100644 --- a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.productView.example.jsx +++ b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.productView.example.jsx @@ -1,9 +1,8 @@ import {useLoaderData} from '@remix-run/react'; -import {json} from '@shopify/remix-oxygen'; import {Analytics} from '@shopify/hydrogen'; export async function loader() { - return json({ + return { product: { id: '123', title: 'ABC', @@ -16,7 +15,7 @@ export async function loader() { }, }, }, - }); + }; } export default function Product() { diff --git a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.productView.example.tsx b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.productView.example.tsx index fa0184d81b..ca9b5c5938 100644 --- a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.productView.example.tsx +++ b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.productView.example.tsx @@ -1,9 +1,8 @@ import {useLoaderData} from '@remix-run/react'; -import {json} from '@shopify/remix-oxygen'; import {Analytics} from '@shopify/hydrogen'; export async function loader() { - return json({ + return { product: { id: '123', title: 'ABC', @@ -16,7 +15,7 @@ export async function loader() { }, }, }, - }); + }; } export default function Product() { diff --git a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.searchView.example.jsx b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.searchView.example.jsx index b2b7c9a57f..4b772deab5 100644 --- a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.searchView.example.jsx +++ b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.searchView.example.jsx @@ -1,5 +1,4 @@ import {Analytics} from '@shopify/hydrogen'; -import {json} from '@shopify/remix-oxygen'; import {useLoaderData} from '@remix-run/react'; export async function loader({request}) { @@ -7,9 +6,7 @@ export async function loader({request}) { const searchParams = new URLSearchParams(url.search); const searchTerm = String(searchParams.get('q') || ''); - return json({ - searchTerm, - }); + return {searchTerm}; } export default function SearchPage() { diff --git a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.searchView.example.tsx b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.searchView.example.tsx index 3209c867b1..dfa1defffe 100644 --- a/packages/hydrogen/src/analytics-manager/AnalyticsProvider.searchView.example.tsx +++ b/packages/hydrogen/src/analytics-manager/AnalyticsProvider.searchView.example.tsx @@ -1,5 +1,5 @@ import {Analytics} from '@shopify/hydrogen'; -import {type LoaderFunctionArgs, json} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData} from '@remix-run/react'; export async function loader({request}: LoaderFunctionArgs) { @@ -7,9 +7,7 @@ export async function loader({request}: LoaderFunctionArgs) { const searchParams = new URLSearchParams(url.search); const searchTerm = String(searchParams.get('q') || ''); - return json({ - searchTerm, - }); + return {searchTerm}; } export default function SearchPage() { diff --git a/packages/hydrogen/src/analytics-manager/getShopAnalytics.example.jsx b/packages/hydrogen/src/analytics-manager/getShopAnalytics.example.jsx index ac4a64ab8f..2a1bcf6be7 100644 --- a/packages/hydrogen/src/analytics-manager/getShopAnalytics.example.jsx +++ b/packages/hydrogen/src/analytics-manager/getShopAnalytics.example.jsx @@ -1,12 +1,11 @@ import {Analytics, getShopAnalytics} from '@shopify/hydrogen'; -import {defer} from '@shopify/remix-oxygen'; import {Outlet, useLoaderData} from '@remix-run/react'; export async function loader({context}) { const {cart, env} = context; const cartPromise = cart.get(); - return defer({ + return { cart: cartPromise, shop: getShopAnalytics({ storefront: context.storefront, @@ -20,7 +19,7 @@ export async function loader({context}) { country: context.storefront.i18n.country, language: context.storefront.i18n.language, }, - }); + }; } export default function App() { diff --git a/packages/hydrogen/src/analytics-manager/getShopAnalytics.example.tsx b/packages/hydrogen/src/analytics-manager/getShopAnalytics.example.tsx index 2d7ec489cb..1786a5003a 100644 --- a/packages/hydrogen/src/analytics-manager/getShopAnalytics.example.tsx +++ b/packages/hydrogen/src/analytics-manager/getShopAnalytics.example.tsx @@ -1,12 +1,12 @@ import {Analytics, getShopAnalytics} from '@shopify/hydrogen'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Outlet, useLoaderData} from '@remix-run/react'; export async function loader({context}: LoaderFunctionArgs) { const {cart, env} = context; const cartPromise = cart.get(); - return defer({ + return { cart: cartPromise, shop: getShopAnalytics({ storefront: context.storefront, @@ -20,7 +20,7 @@ export async function loader({context}: LoaderFunctionArgs) { country: context.storefront.i18n.country, language: context.storefront.i18n.language, }, - }); + }; } export default function App() { diff --git a/packages/hydrogen/src/cache/CacheCustom.example.js b/packages/hydrogen/src/cache/CacheCustom.example.js index b7eb3e680a..83407e7fd4 100644 --- a/packages/hydrogen/src/cache/CacheCustom.example.js +++ b/packages/hydrogen/src/cache/CacheCustom.example.js @@ -1,4 +1,3 @@ -import {json} from '@shopify/remix-oxygen'; import {CacheCustom} from '@shopify/hydrogen'; export async function loader({context}) { @@ -18,5 +17,5 @@ export async function loader({context}) { }, ); - return json(data); + return data; } diff --git a/packages/hydrogen/src/cache/CacheCustom.example.ts b/packages/hydrogen/src/cache/CacheCustom.example.ts index 91d4347f99..863d427f4f 100644 --- a/packages/hydrogen/src/cache/CacheCustom.example.ts +++ b/packages/hydrogen/src/cache/CacheCustom.example.ts @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {CacheCustom} from '@shopify/hydrogen'; export async function loader({context}: LoaderFunctionArgs) { @@ -18,5 +18,5 @@ export async function loader({context}: LoaderFunctionArgs) { }, ); - return json(data); + return data; } diff --git a/packages/hydrogen/src/cache/CacheLong.example.js b/packages/hydrogen/src/cache/CacheLong.example.js index 4f8daa59b4..539b399e23 100644 --- a/packages/hydrogen/src/cache/CacheLong.example.js +++ b/packages/hydrogen/src/cache/CacheLong.example.js @@ -1,4 +1,3 @@ -import {json} from '@shopify/remix-oxygen'; import {CacheLong} from '@shopify/hydrogen'; export async function loader({context}) { @@ -15,5 +14,5 @@ export async function loader({context}) { }, ); - return json(data); + return data; } diff --git a/packages/hydrogen/src/cache/CacheLong.example.ts b/packages/hydrogen/src/cache/CacheLong.example.ts index e983a03901..c169b1ff04 100644 --- a/packages/hydrogen/src/cache/CacheLong.example.ts +++ b/packages/hydrogen/src/cache/CacheLong.example.ts @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {CacheLong} from '@shopify/hydrogen'; export async function loader({context}: LoaderFunctionArgs) { @@ -15,5 +15,5 @@ export async function loader({context}: LoaderFunctionArgs) { }, ); - return json(data); + return data; } diff --git a/packages/hydrogen/src/cache/CacheNone.example.js b/packages/hydrogen/src/cache/CacheNone.example.js index bc64db636c..5243717f2a 100644 --- a/packages/hydrogen/src/cache/CacheNone.example.js +++ b/packages/hydrogen/src/cache/CacheNone.example.js @@ -1,4 +1,3 @@ -import {json} from '@shopify/remix-oxygen'; import {CacheNone} from '@shopify/hydrogen'; export async function loader({context}) { @@ -15,5 +14,5 @@ export async function loader({context}) { }, ); - return json(data); + return data; } diff --git a/packages/hydrogen/src/cache/CacheNone.example.ts b/packages/hydrogen/src/cache/CacheNone.example.ts index 5d4e7e8a9c..63a48a4668 100644 --- a/packages/hydrogen/src/cache/CacheNone.example.ts +++ b/packages/hydrogen/src/cache/CacheNone.example.ts @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {CacheNone} from '@shopify/hydrogen'; export async function loader({context}: LoaderFunctionArgs) { @@ -15,5 +15,5 @@ export async function loader({context}: LoaderFunctionArgs) { }, ); - return json(data); + return data; } diff --git a/packages/hydrogen/src/cache/CacheShort.example.js b/packages/hydrogen/src/cache/CacheShort.example.js index 8ecc934945..6f33b1a77e 100644 --- a/packages/hydrogen/src/cache/CacheShort.example.js +++ b/packages/hydrogen/src/cache/CacheShort.example.js @@ -1,4 +1,3 @@ -import {json} from '@shopify/remix-oxygen'; import {CacheShort} from '@shopify/hydrogen'; export async function loader({context}) { @@ -15,5 +14,5 @@ export async function loader({context}) { }, ); - return json(data); + return data; } diff --git a/packages/hydrogen/src/cache/CacheShort.example.ts b/packages/hydrogen/src/cache/CacheShort.example.ts index fa612b74a9..42698b01d0 100644 --- a/packages/hydrogen/src/cache/CacheShort.example.ts +++ b/packages/hydrogen/src/cache/CacheShort.example.ts @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {CacheShort} from '@shopify/hydrogen'; export async function loader({context}: LoaderFunctionArgs) { @@ -15,5 +15,5 @@ export async function loader({context}: LoaderFunctionArgs) { }, ); - return json(data); + return data; } diff --git a/packages/hydrogen/src/cache/generateCacheControlHeader.example.js b/packages/hydrogen/src/cache/generateCacheControlHeader.example.js index 5d36f7d598..7e685da89a 100644 --- a/packages/hydrogen/src/cache/generateCacheControlHeader.example.js +++ b/packages/hydrogen/src/cache/generateCacheControlHeader.example.js @@ -1,8 +1,8 @@ -import {json} from '@shopify/remix-oxygen'; +import {data} from '@shopify/remix-oxygen'; import {generateCacheControlHeader, CacheShort} from '@shopify/hydrogen'; export async function loader() { - return json( + return data( {some: 'data'}, { headers: { diff --git a/packages/hydrogen/src/cache/generateCacheControlHeader.example.ts b/packages/hydrogen/src/cache/generateCacheControlHeader.example.ts index 5d36f7d598..7e685da89a 100644 --- a/packages/hydrogen/src/cache/generateCacheControlHeader.example.ts +++ b/packages/hydrogen/src/cache/generateCacheControlHeader.example.ts @@ -1,8 +1,8 @@ -import {json} from '@shopify/remix-oxygen'; +import {data} from '@shopify/remix-oxygen'; import {generateCacheControlHeader, CacheShort} from '@shopify/hydrogen'; export async function loader() { - return json( + return data( {some: 'data'}, { headers: { diff --git a/packages/hydrogen/src/cart/CartForm.custom.example.jsx b/packages/hydrogen/src/cart/CartForm.custom.example.jsx index b62df0f03e..ddb73849e3 100644 --- a/packages/hydrogen/src/cart/CartForm.custom.example.jsx +++ b/packages/hydrogen/src/cart/CartForm.custom.example.jsx @@ -1,4 +1,4 @@ -import {json} from '@remix-run/server-runtime'; +import {data} from '@remix-run/server-runtime'; import {CartForm} from '@shopify/hydrogen'; import invariant from 'tiny-invariant'; @@ -39,5 +39,5 @@ export async function action({request, context}) { const headers = cart.setCartId(result.cart.id); - return json(result, {status, headers}); + return data(result, {status, headers}); } diff --git a/packages/hydrogen/src/cart/CartForm.custom.example.tsx b/packages/hydrogen/src/cart/CartForm.custom.example.tsx index 97926e9522..8acb77b793 100644 --- a/packages/hydrogen/src/cart/CartForm.custom.example.tsx +++ b/packages/hydrogen/src/cart/CartForm.custom.example.tsx @@ -1,4 +1,4 @@ -import {type ActionFunctionArgs, json} from '@remix-run/server-runtime'; +import {type ActionFunctionArgs, data} from '@remix-run/server-runtime'; import { type CartQueryDataReturn, type HydrogenCart, @@ -47,5 +47,5 @@ export async function action({request, context}: ActionFunctionArgs) { const headers = cart.setCartId(result.cart.id); - return json(result, {status, headers}); + return data(result, {status, headers}); } diff --git a/packages/hydrogen/src/cart/CartForm.example.jsx b/packages/hydrogen/src/cart/CartForm.example.jsx index 656fc8e767..4e8a9ae2bb 100644 --- a/packages/hydrogen/src/cart/CartForm.example.jsx +++ b/packages/hydrogen/src/cart/CartForm.example.jsx @@ -1,4 +1,4 @@ -import {json} from '@remix-run/server-runtime'; +import {data} from '@remix-run/server-runtime'; import {CartForm} from '@shopify/hydrogen'; import invariant from 'tiny-invariant'; @@ -38,5 +38,5 @@ export async function action({request, context}) { const headers = cart.setCartId(result.cart.id); - return json(result, {status, headers}); + return data(result, {status, headers}); } diff --git a/packages/hydrogen/src/cart/CartForm.example.tsx b/packages/hydrogen/src/cart/CartForm.example.tsx index b1af1539ad..7cbf8ffd75 100644 --- a/packages/hydrogen/src/cart/CartForm.example.tsx +++ b/packages/hydrogen/src/cart/CartForm.example.tsx @@ -1,4 +1,4 @@ -import {type ActionFunctionArgs, json} from '@remix-run/server-runtime'; +import {type ActionFunctionArgs, data} from '@remix-run/server-runtime'; import { type CartQueryDataReturn, type HydrogenCart, @@ -45,5 +45,5 @@ export async function action({request, context}: ActionFunctionArgs) { const headers = cart.setCartId(result.cart.id); - return json(result, {status, headers}); + return data(result, {status, headers}); } diff --git a/packages/hydrogen/src/cart/CartForm.fetcher.example.jsx b/packages/hydrogen/src/cart/CartForm.fetcher.example.jsx index bbaea31230..5f02675cb9 100644 --- a/packages/hydrogen/src/cart/CartForm.fetcher.example.jsx +++ b/packages/hydrogen/src/cart/CartForm.fetcher.example.jsx @@ -1,5 +1,5 @@ import {useFetcher} from '@remix-run/react'; -import {json} from '@remix-run/server-runtime'; +import {data} from '@remix-run/server-runtime'; import {CartForm} from '@shopify/hydrogen'; import invariant from 'tiny-invariant'; @@ -56,5 +56,5 @@ export async function action({request, context}) { const headers = cart.setCartId(result.cart.id); - return json(result, {status, headers}); + return data(result, {status, headers}); } diff --git a/packages/hydrogen/src/cart/CartForm.fetcher.example.tsx b/packages/hydrogen/src/cart/CartForm.fetcher.example.tsx index d80ff25005..ebfa358ab7 100644 --- a/packages/hydrogen/src/cart/CartForm.fetcher.example.tsx +++ b/packages/hydrogen/src/cart/CartForm.fetcher.example.tsx @@ -1,5 +1,5 @@ import {useFetcher} from '@remix-run/react'; -import {type ActionFunctionArgs, json} from '@remix-run/server-runtime'; +import {type ActionFunctionArgs, data} from '@remix-run/server-runtime'; import { type CartQueryDataReturn, type HydrogenCart, @@ -67,5 +67,5 @@ export async function action({request, context}: ActionFunctionArgs) { const headers = cart.setCartId(result.cart.id); - return json(result, {status, headers}); + return data(result, {status, headers}); } diff --git a/packages/hydrogen/src/cart/CartForm.input-tag.example.jsx b/packages/hydrogen/src/cart/CartForm.input-tag.example.jsx index a5ae40ce0e..b109a651bc 100644 --- a/packages/hydrogen/src/cart/CartForm.input-tag.example.jsx +++ b/packages/hydrogen/src/cart/CartForm.input-tag.example.jsx @@ -1,4 +1,4 @@ -import {json} from '@remix-run/server-runtime'; +import {data} from '@remix-run/server-runtime'; import {CartForm} from '@shopify/hydrogen'; import invariant from 'tiny-invariant'; @@ -28,5 +28,5 @@ export async function action({request, context}) { const headers = cart.setCartId(result.cart.id); - return json(result, {status, headers}); + return data(result, {status, headers}); } diff --git a/packages/hydrogen/src/cart/CartForm.input-tag.example.tsx b/packages/hydrogen/src/cart/CartForm.input-tag.example.tsx index 54d4586eb3..e04c4b6d66 100644 --- a/packages/hydrogen/src/cart/CartForm.input-tag.example.tsx +++ b/packages/hydrogen/src/cart/CartForm.input-tag.example.tsx @@ -1,4 +1,4 @@ -import {type ActionFunctionArgs, json} from '@remix-run/server-runtime'; +import {type ActionFunctionArgs, data} from '@remix-run/server-runtime'; import { type CartQueryDataReturn, type HydrogenCart, @@ -35,5 +35,5 @@ export async function action({request, context}: ActionFunctionArgs) { const headers = cart.setCartId(result.cart.id); - return json(result, {status, headers}); + return data(result, {status, headers}); } diff --git a/packages/hydrogen/src/cart/cartSetIdDefault.example.js b/packages/hydrogen/src/cart/cartSetIdDefault.example.js index cb65ce6682..1b9e94b653 100644 --- a/packages/hydrogen/src/cart/cartSetIdDefault.example.js +++ b/packages/hydrogen/src/cart/cartSetIdDefault.example.js @@ -1,4 +1,4 @@ -import {json} from '@remix-run/server-runtime'; +import {data} from '@remix-run/server-runtime'; import {cartGetIdDefault, cartSetIdDefault} from '@shopify/hydrogen'; // server.js @@ -22,5 +22,5 @@ export async function action({context}) { const headers = cart.setCartId(result.cart.id); - return json(result, {headers}); + return data(result, {headers}); } diff --git a/packages/hydrogen/src/cart/optimistic/useOptimisticCart.example.jsx b/packages/hydrogen/src/cart/optimistic/useOptimisticCart.example.jsx index e88dff13ea..652fffceda 100644 --- a/packages/hydrogen/src/cart/optimistic/useOptimisticCart.example.jsx +++ b/packages/hydrogen/src/cart/optimistic/useOptimisticCart.example.jsx @@ -1,12 +1,11 @@ -import {defer} from '@shopify/remix-oxygen'; import {Link} from '@remix-run/react'; import {CartForm, useOptimisticCart} from '@shopify/hydrogen'; // Root loader returns the cart data export async function loader({context}) { - return defer({ + return { cart: context.cart.get(), - }); + }; } // The cart component renders each line item in the cart. diff --git a/packages/hydrogen/src/cart/optimistic/useOptimisticCart.example.tsx b/packages/hydrogen/src/cart/optimistic/useOptimisticCart.example.tsx index 6c4de7bfdc..b5d99208d7 100644 --- a/packages/hydrogen/src/cart/optimistic/useOptimisticCart.example.tsx +++ b/packages/hydrogen/src/cart/optimistic/useOptimisticCart.example.tsx @@ -1,13 +1,13 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Link} from '@remix-run/react'; import {CartForm, useOptimisticCart} from '@shopify/hydrogen'; import type {Cart} from '@shopify/hydrogen/storefront-api-types'; // Root loader returns the cart data export async function loader({context}: LoaderFunctionArgs) { - return defer({ + return { cart: context.cart.get(), - }); + }; } // The cart component renders each line item in the cart. diff --git a/packages/hydrogen/src/csp/createContentSecurityPolicy.example.jsx b/packages/hydrogen/src/csp/createContentSecurityPolicy.example.jsx index e795a812b8..39283038b3 100644 --- a/packages/hydrogen/src/csp/createContentSecurityPolicy.example.jsx +++ b/packages/hydrogen/src/csp/createContentSecurityPolicy.example.jsx @@ -19,7 +19,7 @@ export default async function handleRequest( }); const body = await renderToReadableStream( - + , { nonce, diff --git a/packages/hydrogen/src/csp/createContentSecurityPolicy.example.tsx b/packages/hydrogen/src/csp/createContentSecurityPolicy.example.tsx index 127321376d..79195d917c 100644 --- a/packages/hydrogen/src/csp/createContentSecurityPolicy.example.tsx +++ b/packages/hydrogen/src/csp/createContentSecurityPolicy.example.tsx @@ -20,7 +20,7 @@ export default async function handleRequest( }); const body = await renderToReadableStream( - + , { nonce, diff --git a/packages/hydrogen/src/customer/customer.auth-handler.example.jsx b/packages/hydrogen/src/customer/customer.auth-handler.example.jsx index 4dbd7464ea..6128f4d7fe 100644 --- a/packages/hydrogen/src/customer/customer.auth-handler.example.jsx +++ b/packages/hydrogen/src/customer/customer.auth-handler.example.jsx @@ -101,7 +101,6 @@ import { isRouteErrorResponse, useLocation, } from '@remix-run/react'; -import {json} from '@shopify/remix-oxygen'; export async function loader({context}) { const {data} = await context.customerAccount.query(`#graphql @@ -113,7 +112,7 @@ export async function loader({context}) { } `); - return json({customer: data.customer}); + return {customer: data.customer}; } export function ErrorBoundary() { diff --git a/packages/hydrogen/src/customer/customer.auth-handler.example.tsx b/packages/hydrogen/src/customer/customer.auth-handler.example.tsx index a7e01db439..6a0deba273 100644 --- a/packages/hydrogen/src/customer/customer.auth-handler.example.tsx +++ b/packages/hydrogen/src/customer/customer.auth-handler.example.tsx @@ -132,7 +132,7 @@ import { isRouteErrorResponse, useLocation, } from '@remix-run/react'; -import {type LoaderFunctionArgs, json} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; export async function loader({context}: LoaderFunctionArgs) { const {data} = await context.customerAccount.query<{ @@ -146,7 +146,7 @@ export async function loader({context}: LoaderFunctionArgs) { } `); - return json({customer: data.customer}); + return {customer: data.customer}; } export function ErrorBoundary() { diff --git a/packages/hydrogen/src/customer/customer.opt-out-handler.example.jsx b/packages/hydrogen/src/customer/customer.opt-out-handler.example.jsx index e9e4d29ba7..6ac1a1e329 100644 --- a/packages/hydrogen/src/customer/customer.opt-out-handler.example.jsx +++ b/packages/hydrogen/src/customer/customer.opt-out-handler.example.jsx @@ -101,7 +101,6 @@ import { isRouteErrorResponse, useLocation, } from '@remix-run/react'; -import {json} from '@shopify/remix-oxygen'; export async function loader({context}) { if (!(await context.customerAccount.isLoggedIn())) { @@ -121,7 +120,7 @@ export async function loader({context}) { `, ); - return json({customer: data.customer}); + return {customer: data.customer}; } export function ErrorBoundary() { diff --git a/packages/hydrogen/src/customer/customer.opt-out-handler.example.tsx b/packages/hydrogen/src/customer/customer.opt-out-handler.example.tsx index 2960c3ff61..e33e9ea5b4 100644 --- a/packages/hydrogen/src/customer/customer.opt-out-handler.example.tsx +++ b/packages/hydrogen/src/customer/customer.opt-out-handler.example.tsx @@ -115,7 +115,7 @@ import { isRouteErrorResponse, useLocation, } from '@remix-run/react'; -import {type LoaderFunctionArgs, json} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; export async function loader({context}: LoaderFunctionArgs) { if (!(await context.customerAccount.isLoggedIn())) { @@ -135,7 +135,7 @@ export async function loader({context}: LoaderFunctionArgs) { `, ); - return json({customer: data.customer}); + return {customer: data.customer}; } export function ErrorBoundary() { diff --git a/packages/hydrogen/src/pagination/Pagination.example.jsx b/packages/hydrogen/src/pagination/Pagination.example.jsx index 4219d429b9..e290db2ce2 100644 --- a/packages/hydrogen/src/pagination/Pagination.example.jsx +++ b/packages/hydrogen/src/pagination/Pagination.example.jsx @@ -1,4 +1,3 @@ -import {json} from '@shopify/remix-oxygen'; import {Pagination, getPaginationVariables} from '@shopify/hydrogen'; import {useLoaderData, Link} from '@remix-run/react'; @@ -9,7 +8,7 @@ export async function loader({request, context: {storefront}}) { variables, }); - return json({products: data.products}); + return {products: data.products}; } export default function List() { diff --git a/packages/hydrogen/src/pagination/Pagination.example.tsx b/packages/hydrogen/src/pagination/Pagination.example.tsx index a0c6743e7b..92159661cc 100644 --- a/packages/hydrogen/src/pagination/Pagination.example.tsx +++ b/packages/hydrogen/src/pagination/Pagination.example.tsx @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Pagination, getPaginationVariables} from '@shopify/hydrogen'; import {useLoaderData, Link} from '@remix-run/react'; import {ProductConnection} from '@shopify/hydrogen/storefront-api-types'; @@ -16,7 +16,7 @@ export async function loader({ }, ); - return json({products: data.products}); + return {products: data.products}; } export default function List() { diff --git a/packages/hydrogen/src/pagination/Pagination.multiple.example.jsx b/packages/hydrogen/src/pagination/Pagination.multiple.example.jsx index 11e0357633..e8d09a1d55 100644 --- a/packages/hydrogen/src/pagination/Pagination.multiple.example.jsx +++ b/packages/hydrogen/src/pagination/Pagination.multiple.example.jsx @@ -1,4 +1,3 @@ -import {json} from '@shopify/remix-oxygen'; import {useLoaderData, Link} from '@remix-run/react'; import {getPaginationVariables, Pagination} from '@shopify/hydrogen'; @@ -21,7 +20,7 @@ export async function loader({request, context: {storefront}}) { }), ]); - return json({womensProducts, mensProducts}); + return {womensProducts, mensProducts}; } export default function Collection() { diff --git a/packages/hydrogen/src/pagination/Pagination.multiple.example.tsx b/packages/hydrogen/src/pagination/Pagination.multiple.example.tsx index e2261f8d2f..702119311d 100644 --- a/packages/hydrogen/src/pagination/Pagination.multiple.example.tsx +++ b/packages/hydrogen/src/pagination/Pagination.multiple.example.tsx @@ -1,4 +1,4 @@ -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, Link} from '@remix-run/react'; import {getPaginationVariables, Pagination} from '@shopify/hydrogen'; import {type Collection} from '@shopify/hydrogen-react/storefront-api-types'; @@ -25,7 +25,7 @@ export async function loader({ }), ]); - return json({womensProducts, mensProducts}); + return {womensProducts, mensProducts}; } export default function Collection() { diff --git a/packages/hydrogen/src/product/getSelectedProductOptions.example.jsx b/packages/hydrogen/src/product/getSelectedProductOptions.example.jsx index 7b6ca69109..2534dc4972 100644 --- a/packages/hydrogen/src/product/getSelectedProductOptions.example.jsx +++ b/packages/hydrogen/src/product/getSelectedProductOptions.example.jsx @@ -1,5 +1,4 @@ import {getSelectedProductOptions} from '@shopify/hydrogen'; -import {json} from '@shopify/remix-oxygen'; export async function loader({request, params, context}) { const selectedOptions = getSelectedProductOptions(request); @@ -11,7 +10,7 @@ export async function loader({request, params, context}) { }, }); - return json({product}); + return {product}; } const PRODUCT_QUERY = `#graphql @@ -21,7 +20,7 @@ const PRODUCT_QUERY = `#graphql description options { name - values + values } selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions, ignoreUnknownOptions: true, caseInsensitiveMatch: true) { ...ProductVariantFragment diff --git a/packages/hydrogen/src/product/getSelectedProductOptions.example.tsx b/packages/hydrogen/src/product/getSelectedProductOptions.example.tsx index 45c1a453d2..41dff2b476 100644 --- a/packages/hydrogen/src/product/getSelectedProductOptions.example.tsx +++ b/packages/hydrogen/src/product/getSelectedProductOptions.example.tsx @@ -1,5 +1,5 @@ import {getSelectedProductOptions} from '@shopify/hydrogen'; -import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; export async function loader({request, params, context}: LoaderFunctionArgs) { const selectedOptions = getSelectedProductOptions(request); @@ -11,7 +11,7 @@ export async function loader({request, params, context}: LoaderFunctionArgs) { }, }); - return json({product}); + return {product}; } const PRODUCT_QUERY = `#graphql @@ -21,7 +21,7 @@ const PRODUCT_QUERY = `#graphql description options { name - values + values } selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions, ignoreUnknownOptions: true, caseInsensitiveMatch: true) { ...ProductVariantFragment diff --git a/packages/hydrogen/src/product/useOptimisticVariant.example.jsx b/packages/hydrogen/src/product/useOptimisticVariant.example.jsx index 69cbbb0eab..5f7eef7e3a 100644 --- a/packages/hydrogen/src/product/useOptimisticVariant.example.jsx +++ b/packages/hydrogen/src/product/useOptimisticVariant.example.jsx @@ -1,13 +1,12 @@ import {useLoaderData} from '@remix-run/react'; -import {defer} from '@remix-run/server-runtime'; import {useOptimisticVariant} from '@shopify/hydrogen'; export async function loader({context}) { - return defer({ + return { product: await context.storefront.query('/** product query **/'), // Note that variants does not need to be awaited to be used by `useOptimisticVariant` variants: context.storefront.query('/** variants query **/'), - }); + }; } function Product() { diff --git a/packages/hydrogen/src/product/useOptimisticVariant.example.tsx b/packages/hydrogen/src/product/useOptimisticVariant.example.tsx index dd369418af..cdac06ccc5 100644 --- a/packages/hydrogen/src/product/useOptimisticVariant.example.tsx +++ b/packages/hydrogen/src/product/useOptimisticVariant.example.tsx @@ -1,13 +1,13 @@ import {useLoaderData} from '@remix-run/react'; -import {defer, LoaderFunctionArgs} from '@remix-run/server-runtime'; +import {LoaderFunctionArgs} from '@remix-run/server-runtime'; import {useOptimisticVariant} from '@shopify/hydrogen'; export async function loader({context}: LoaderFunctionArgs) { - return defer({ + return { product: await context.storefront.query('/** product query */'), // Note that variants does not need to be awaited to be used by `useOptimisticVariant` variants: context.storefront.query('/** variants query */'), - }); + }; } function Product() { diff --git a/packages/hydrogen/src/utils/graphql.ts b/packages/hydrogen/src/utils/graphql.ts index f0d9277df0..9b31c516d6 100644 --- a/packages/hydrogen/src/utils/graphql.ts +++ b/packages/hydrogen/src/utils/graphql.ts @@ -144,7 +144,7 @@ export class GraphQLError extends Error { /** * Note: toJSON` is internally used by `JSON.stringify(...)`. * The most common scenario when this error instance is going to be stringified is - * when it's passed to Remix' `json` and `defer` functions: e.g. `defer({promise: storefront.query(...)})`. + * when it's passed to Remix' `json` and `defer` functions: e.g. `{promise: storefront.query(...)}`. * In this situation, we don't want to expose private error information to the browser so we only * do it in development. */ diff --git a/rfc/cart.md b/rfc/cart.md index 7b6f719827..5b92f367a7 100644 --- a/rfc/cart.md +++ b/rfc/cart.md @@ -70,7 +70,7 @@ export async function action({request, context}) { session.set('cartId', cartId); const {cart, errors} = result; - return json({cart, errors}, {status, headers}); + return data({cart, errors}, {status, headers}); } const USER_ERROR_FRAGMENT = `#graphql diff --git a/rfc/pagination.md b/rfc/pagination.md index c1d73df490..3e372b3200 100644 --- a/rfc/pagination.md +++ b/rfc/pagination.md @@ -39,7 +39,7 @@ export async function loader({context, request}: LoaderArgs) { throw new Response(null, {status: 404}); } - return json({products}); + return {products}; } ``` @@ -97,7 +97,7 @@ export async function loader({context, request}: LoaderArgs) { throw new Response(null, {status: 404}); } - return json({products}); + return {products}; } ``` diff --git a/templates/TEMPLATE_GUIDELINES.md b/templates/TEMPLATE_GUIDELINES.md index 42492fd8cb..c5bfdf7b2c 100644 --- a/templates/TEMPLATE_GUIDELINES.md +++ b/templates/TEMPLATE_GUIDELINES.md @@ -21,7 +21,6 @@ Always demonstrate realistic error-handling. Skeleton templates should be a shin - **Have an `ErrorBoundary` in every route template.** `ErrorBoundary` is used when an Error is thrown in a “loader”, and is generally meant for unexpected errors, like 500, 503, etc. Any Storefront query or mutation error will be handled by the `ErrorBoundary`. Type the error as “unknown” since _anything_ in JS can be thrown 🙂 - **Use the “errorElement” prop on every `` component.** When using “defer”, some promises may be rejected at a later time. The only way to handle this is to use the “errorElement” on the associated component, otherwise the error is swallowed. - **Use try/catch** – except in “loader”, “action”, and the Component. Those three “Route Module APIs” are handled automatically by `ErrorBoundary` and CatchBoundary, but the rest – such as “meta”, “links”, “handle”, etc. – will crash the server if an error is thrown. -- **Have a CatchBoundary if necessary.** A CatchBoundary is used when a new Response is thrown, and is generally meant for expected errors caused by the user, such as 401, 404, etc. Note that `CatchBoundary`s will be deprecated in Remix V2, at which time we'll remove this recommendation. ### Don’t @@ -45,7 +44,7 @@ export async function loader() { } //... - return defer() + return data } export function meta() { @@ -60,12 +59,6 @@ export function ErrorBoundary({error}) { return (
{error.message}
) } -// Note that `CatchBoundary`s will be deprecated in Remix V2 -export function CatchBoundary() { - const {statusText} = useCatch() - return (
{statusText}
) -} - export default function TheUIComponents() { return ( An error occurred}> @@ -124,7 +117,7 @@ Remix-specific route API functions should be ordered and consistent in style, to 1. Http header tweaks (`shouldRevalidate`, `headers`, `meta`, `links`) 1. Data manipulation (`loader`, `action`) 1. UI (`Component`) - 1. Error handling (`ErrorBoundary`, `CatchBoundary`) + 1. Error handling (`ErrorBoundary`) 1. Storefront API GraphQL query strings - Use function declarations when possible - Use the most specific type available for Remix Route APIs. @@ -158,8 +151,6 @@ export default function Component() {} export function ErrorBoundary() {} -export function CatchBoundary() {} - /* storefront Queries/Mutations, see more specific recommendations below */ ``` @@ -187,17 +178,18 @@ Use the correct return type in `loader()`, `action()`, etc. ### Do -- Use `json()` by default +- Return raw json object by default +- Use `await` if you want the data to be streamed in later - Use `redirect()` from the `@shopify/remix-oxygen` package to redirect -- Use `defer()` when there is a need to have content streamed in later -- Use `new Response()` for errors (like 404s) and for unique document responses like `.xml` and `.txt` +- Use `data()` for errors (like 404s) +- Use `new Response()` for unique document responses like `.xml` and `.txt` - Use capitalized and kebab-cased headers in responses, like `Cache-Control` ### Example ```tsx export async function loader() { - return json({foo: 'bar'}); + return {foo: 'bar'}; } ``` @@ -210,7 +202,7 @@ export async function loader() { ```tsx export async function loader() { - return json( + return data( {foo: 'bar'}, { headers: { diff --git a/templates/skeleton/app/entry.server.tsx b/templates/skeleton/app/entry.server.tsx index d7c0f4337c..cce5daefd5 100644 --- a/templates/skeleton/app/entry.server.tsx +++ b/templates/skeleton/app/entry.server.tsx @@ -20,7 +20,7 @@ export default async function handleRequest( const body = await renderToReadableStream( - + , { nonce, diff --git a/templates/skeleton/app/root.tsx b/templates/skeleton/app/root.tsx index eeb97d99a5..17b8c59c1c 100644 --- a/templates/skeleton/app/root.tsx +++ b/templates/skeleton/app/root.tsx @@ -1,5 +1,5 @@ import {useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -70,7 +70,7 @@ export async function loader(args: LoaderFunctionArgs) { const {storefront, env} = args.context; - return defer({ + return { ...deferredData, ...criticalData, publicStoreDomain: env.PUBLIC_STORE_DOMAIN, @@ -86,7 +86,7 @@ export async function loader(args: LoaderFunctionArgs) { country: args.context.storefront.i18n.country, language: args.context.storefront.i18n.language, }, - }); + }; } /** diff --git a/templates/skeleton/app/routes/[robots.txt].tsx b/templates/skeleton/app/routes/[robots.txt].tsx index 3feeb475c9..d6109ab89f 100644 --- a/templates/skeleton/app/routes/[robots.txt].tsx +++ b/templates/skeleton/app/routes/[robots.txt].tsx @@ -1,5 +1,4 @@ import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; -import {useRouteError, isRouteErrorResponse} from '@remix-run/react'; import {parseGid} from '@shopify/hydrogen'; export async function loader({request, context}: LoaderFunctionArgs) { diff --git a/templates/skeleton/app/routes/_index.tsx b/templates/skeleton/app/routes/_index.tsx index 4b74fb03fd..9fa3364233 100644 --- a/templates/skeleton/app/routes/_index.tsx +++ b/templates/skeleton/app/routes/_index.tsx @@ -1,4 +1,4 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Await, useLoaderData, Link, type MetaFunction} from '@remix-run/react'; import {Suspense} from 'react'; import {Image, Money} from '@shopify/hydrogen'; @@ -18,7 +18,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/templates/skeleton/app/routes/blogs.$blogHandle.$articleHandle.tsx b/templates/skeleton/app/routes/blogs.$blogHandle.$articleHandle.tsx index 37ebb42aae..55ad500adb 100644 --- a/templates/skeleton/app/routes/blogs.$blogHandle.$articleHandle.tsx +++ b/templates/skeleton/app/routes/blogs.$blogHandle.$articleHandle.tsx @@ -1,4 +1,4 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, type MetaFunction} from '@remix-run/react'; import {Image} from '@shopify/hydrogen'; @@ -13,7 +13,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/templates/skeleton/app/routes/blogs.$blogHandle._index.tsx b/templates/skeleton/app/routes/blogs.$blogHandle._index.tsx index 9d9de5a827..a4ae80ee84 100644 --- a/templates/skeleton/app/routes/blogs.$blogHandle._index.tsx +++ b/templates/skeleton/app/routes/blogs.$blogHandle._index.tsx @@ -1,4 +1,4 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Link, useLoaderData, type MetaFunction} from '@remix-run/react'; import {Image, getPaginationVariables} from '@shopify/hydrogen'; import type {ArticleItemFragment} from 'storefrontapi.generated'; @@ -15,7 +15,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/templates/skeleton/app/routes/blogs._index.tsx b/templates/skeleton/app/routes/blogs._index.tsx index e5c9ce8b49..29f110fb56 100644 --- a/templates/skeleton/app/routes/blogs._index.tsx +++ b/templates/skeleton/app/routes/blogs._index.tsx @@ -1,4 +1,4 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {Link, useLoaderData, type MetaFunction} from '@remix-run/react'; import {getPaginationVariables} from '@shopify/hydrogen'; import {PaginatedResourceSection} from '~/components/PaginatedResourceSection'; @@ -14,7 +14,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/templates/skeleton/app/routes/collections.$handle.tsx b/templates/skeleton/app/routes/collections.$handle.tsx index 9dcddfb3b1..a67d46438c 100644 --- a/templates/skeleton/app/routes/collections.$handle.tsx +++ b/templates/skeleton/app/routes/collections.$handle.tsx @@ -1,4 +1,4 @@ -import {defer, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, Link, type MetaFunction} from '@remix-run/react'; import { getPaginationVariables, @@ -21,7 +21,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/templates/skeleton/app/routes/collections._index.tsx b/templates/skeleton/app/routes/collections._index.tsx index f381c4b4f3..bf4f74ea85 100644 --- a/templates/skeleton/app/routes/collections._index.tsx +++ b/templates/skeleton/app/routes/collections._index.tsx @@ -1,5 +1,5 @@ import {useLoaderData, Link} from '@remix-run/react'; -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {getPaginationVariables, Image} from '@shopify/hydrogen'; import type {CollectionFragment} from 'storefrontapi.generated'; import {PaginatedResourceSection} from '~/components/PaginatedResourceSection'; @@ -11,7 +11,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/templates/skeleton/app/routes/collections.all.tsx b/templates/skeleton/app/routes/collections.all.tsx index 9dd4780773..84c768bb3f 100644 --- a/templates/skeleton/app/routes/collections.all.tsx +++ b/templates/skeleton/app/routes/collections.all.tsx @@ -1,4 +1,4 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, Link, type MetaFunction} from '@remix-run/react'; import {getPaginationVariables, Image, Money} from '@shopify/hydrogen'; import type {ProductItemFragment} from 'storefrontapi.generated'; @@ -16,7 +16,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/templates/skeleton/app/routes/pages.$handle.tsx b/templates/skeleton/app/routes/pages.$handle.tsx index 1b01769a79..3a75708514 100644 --- a/templates/skeleton/app/routes/pages.$handle.tsx +++ b/templates/skeleton/app/routes/pages.$handle.tsx @@ -1,4 +1,4 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, type MetaFunction} from '@remix-run/react'; export const meta: MetaFunction = ({data}) => { @@ -12,7 +12,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/templates/skeleton/app/routes/products.$handle.tsx b/templates/skeleton/app/routes/products.$handle.tsx index 11e495cbac..0028b423de 100644 --- a/templates/skeleton/app/routes/products.$handle.tsx +++ b/templates/skeleton/app/routes/products.$handle.tsx @@ -1,4 +1,4 @@ -import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, type MetaFunction} from '@remix-run/react'; import { getSelectedProductOptions, @@ -29,7 +29,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({...deferredData, ...criticalData}); + return {...deferredData, ...criticalData}; } /** diff --git a/templates/skeleton/guides/predictiveSearch/predictiveSearch.md b/templates/skeleton/guides/predictiveSearch/predictiveSearch.md index 3f3d45835a..21ad9e6243 100644 --- a/templates/skeleton/guides/predictiveSearch/predictiveSearch.md +++ b/templates/skeleton/guides/predictiveSearch/predictiveSearch.md @@ -194,7 +194,7 @@ async function predictiveSearch({ const total = Object.values(items).reduce((acc, {length}) => acc + length, 0); - return json({term, result: {items, total}, error: null}); + return {term, result: {items, total}, error: null}; } ``` @@ -217,7 +217,7 @@ export async function loader({request, context}: LoaderFunctionArgs) { const isPredictive = url.searchParams.has('predictive'); if (!isPredictive) { - return json({}) + return {} } const searchPromise = predictiveSearch({request, context}) @@ -227,7 +227,7 @@ export async function loader({request, context}: LoaderFunctionArgs) { return {term: '', result: null, error: error.message}; }); - return json(await searchPromise); + return await searchPromise; } ``` diff --git a/templates/skeleton/guides/search/search.md b/templates/skeleton/guides/search/search.md index 5925d5e017..c4d695090c 100644 --- a/templates/skeleton/guides/search/search.md +++ b/templates/skeleton/guides/search/search.md @@ -191,7 +191,7 @@ async function search({ return acc + nodes.length; }, 0); - return json({term, result: {total, items}}); + return {term, result: {total, items}}; } ``` @@ -212,7 +212,7 @@ export async function loader({request, context}: LoaderFunctionArgs) { const isRegular = !url.searchParams.has('predictive'); if (!isRegular) { - return json({}) + return {} } const searchPromise = regularSearch({request, context}); @@ -222,7 +222,7 @@ export async function loader({request, context}: LoaderFunctionArgs) { return {term: '', result: null, error: error.message}; }); - return json(await searchPromise); + return await searchPromise; } ``` From d3664c0051253328bae5ea99cd25443ab26dd1d8 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 15 Jan 2025 11:04:46 -0800 Subject: [PATCH 04/22] missed a couple flags in examples --- examples/classic-remix/remix.config.js | 1 + examples/express/vite.config.ts | 1 + packages/cli/assets/vite/vite.config.js | 1 + 3 files changed, 3 insertions(+) diff --git a/examples/classic-remix/remix.config.js b/examples/classic-remix/remix.config.js index 73250a97f9..81dcaf80cf 100644 --- a/examples/classic-remix/remix.config.js +++ b/examples/classic-remix/remix.config.js @@ -21,5 +21,6 @@ module.exports = { v3_relativeSplatpath: true, v3_throwAbortReason: true, v3_lazyRouteDiscovery: true, + v3_singleFetch: true, }, }; diff --git a/examples/express/vite.config.ts b/examples/express/vite.config.ts index b24f88644c..203db64983 100644 --- a/examples/express/vite.config.ts +++ b/examples/express/vite.config.ts @@ -13,6 +13,7 @@ export default defineConfig({ v3_relativeSplatPath: true, v3_throwAbortReason: true, v3_lazyRouteDiscovery: true, + v3_singleFetch: true, }, }), tsconfigPaths(), diff --git a/packages/cli/assets/vite/vite.config.js b/packages/cli/assets/vite/vite.config.js index 5ee9b626fb..09c0c84fff 100644 --- a/packages/cli/assets/vite/vite.config.js +++ b/packages/cli/assets/vite/vite.config.js @@ -15,6 +15,7 @@ export default defineConfig({ v3_relativeSplatPath: true, v3_throwAbortReason: true, v3_lazyRouteDiscovery: true, + v3_singleFetch: true, }, }), tsconfigPaths(), From daf69be3be5031129a2bda52d49c04c39f2c9567 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 15 Jan 2025 11:18:57 -0800 Subject: [PATCH 05/22] Revert the setupVite update --- packages/cli/assets/vite/vite.config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cli/assets/vite/vite.config.js b/packages/cli/assets/vite/vite.config.js index 09c0c84fff..f2da56a0a9 100644 --- a/packages/cli/assets/vite/vite.config.js +++ b/packages/cli/assets/vite/vite.config.js @@ -14,8 +14,6 @@ export default defineConfig({ v3_fetcherPersist: true, v3_relativeSplatPath: true, v3_throwAbortReason: true, - v3_lazyRouteDiscovery: true, - v3_singleFetch: true, }, }), tsconfigPaths(), From 666d502e10464a202eaf41a7f8e8a2d436a44499 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 15 Jan 2025 13:02:01 -0800 Subject: [PATCH 06/22] Fix typescript error --- examples/b2b/tsconfig.json | 3 ++- examples/classic-remix/tsconfig.json | 2 +- examples/custom-cart-method/tsconfig.json | 3 ++- examples/gtm/tsconfig.json | 3 ++- examples/infinite-scroll/tsconfig.json | 8 +++++++- examples/legacy-customer-account-flow/tsconfig.json | 2 +- examples/metaobjects/tsconfig.json | 2 +- examples/multipass/vite.config.ts | 6 ++++++ examples/partytown/tsconfig.json | 2 +- examples/subscriptions/tsconfig.json | 3 ++- examples/third-party-queries-caching/tsconfig.json | 3 ++- packages/hydrogen/dev.env.d.ts | 6 ++++++ 12 files changed, 33 insertions(+), 10 deletions(-) diff --git a/examples/b2b/tsconfig.json b/examples/b2b/tsconfig.json index 5b672cc6e1..c54afd12a4 100644 --- a/examples/b2b/tsconfig.json +++ b/examples/b2b/tsconfig.json @@ -4,7 +4,8 @@ "./**/*.d.ts", "./**/*.ts", "./**/*.tsx", - "../../templates/skeleton/*.d.ts" + "../../templates/skeleton/*.d.ts", + "../../templates/skeleton/vite.config.ts" ], "compilerOptions": { "baseUrl": ".", diff --git a/examples/classic-remix/tsconfig.json b/examples/classic-remix/tsconfig.json index 110d781eea..3bf5db6c41 100644 --- a/examples/classic-remix/tsconfig.json +++ b/examples/classic-remix/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../templates/skeleton/tsconfig.json", - "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"], + "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx", "../../templates/skeleton/vite.config.ts"], "compilerOptions": { "baseUrl": ".", "paths": { diff --git a/examples/custom-cart-method/tsconfig.json b/examples/custom-cart-method/tsconfig.json index 5b672cc6e1..c54afd12a4 100644 --- a/examples/custom-cart-method/tsconfig.json +++ b/examples/custom-cart-method/tsconfig.json @@ -4,7 +4,8 @@ "./**/*.d.ts", "./**/*.ts", "./**/*.tsx", - "../../templates/skeleton/*.d.ts" + "../../templates/skeleton/*.d.ts", + "../../templates/skeleton/vite.config.ts" ], "compilerOptions": { "baseUrl": ".", diff --git a/examples/gtm/tsconfig.json b/examples/gtm/tsconfig.json index 5b672cc6e1..c54afd12a4 100644 --- a/examples/gtm/tsconfig.json +++ b/examples/gtm/tsconfig.json @@ -4,7 +4,8 @@ "./**/*.d.ts", "./**/*.ts", "./**/*.tsx", - "../../templates/skeleton/*.d.ts" + "../../templates/skeleton/*.d.ts", + "../../templates/skeleton/vite.config.ts" ], "compilerOptions": { "baseUrl": ".", diff --git a/examples/infinite-scroll/tsconfig.json b/examples/infinite-scroll/tsconfig.json index ad451d25b6..c54afd12a4 100644 --- a/examples/infinite-scroll/tsconfig.json +++ b/examples/infinite-scroll/tsconfig.json @@ -1,6 +1,12 @@ { "extends": "../../templates/skeleton/tsconfig.json", - "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx", "../../templates/skeleton/*.d.ts"], + "include": [ + "./**/*.d.ts", + "./**/*.ts", + "./**/*.tsx", + "../../templates/skeleton/*.d.ts", + "../../templates/skeleton/vite.config.ts" + ], "compilerOptions": { "baseUrl": ".", "paths": { diff --git a/examples/legacy-customer-account-flow/tsconfig.json b/examples/legacy-customer-account-flow/tsconfig.json index 110d781eea..3bf5db6c41 100644 --- a/examples/legacy-customer-account-flow/tsconfig.json +++ b/examples/legacy-customer-account-flow/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../templates/skeleton/tsconfig.json", - "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"], + "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx", "../../templates/skeleton/vite.config.ts"], "compilerOptions": { "baseUrl": ".", "paths": { diff --git a/examples/metaobjects/tsconfig.json b/examples/metaobjects/tsconfig.json index 110d781eea..3bf5db6c41 100644 --- a/examples/metaobjects/tsconfig.json +++ b/examples/metaobjects/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../templates/skeleton/tsconfig.json", - "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"], + "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx", "../../templates/skeleton/vite.config.ts"], "compilerOptions": { "baseUrl": ".", "paths": { diff --git a/examples/multipass/vite.config.ts b/examples/multipass/vite.config.ts index bb5f7412fd..d26be954c7 100644 --- a/examples/multipass/vite.config.ts +++ b/examples/multipass/vite.config.ts @@ -4,6 +4,12 @@ import {oxygen} from '@shopify/mini-oxygen/vite'; import {vitePlugin as remix} from '@remix-run/dev'; import tsconfigPaths from 'vite-tsconfig-paths'; +declare module "@remix-run/server-runtime" { + interface Future { + v3_singleFetch: true; + } +} + export default defineConfig({ plugins: [ hydrogen(), diff --git a/examples/partytown/tsconfig.json b/examples/partytown/tsconfig.json index 53b8b07351..d64ff30108 100644 --- a/examples/partytown/tsconfig.json +++ b/examples/partytown/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../templates/skeleton/tsconfig.json", - "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"], + "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx", "../../templates/skeleton/vite.config.ts"], "compilerOptions": { "baseUrl": ".", "paths": { diff --git a/examples/subscriptions/tsconfig.json b/examples/subscriptions/tsconfig.json index 5b672cc6e1..c54afd12a4 100644 --- a/examples/subscriptions/tsconfig.json +++ b/examples/subscriptions/tsconfig.json @@ -4,7 +4,8 @@ "./**/*.d.ts", "./**/*.ts", "./**/*.tsx", - "../../templates/skeleton/*.d.ts" + "../../templates/skeleton/*.d.ts", + "../../templates/skeleton/vite.config.ts" ], "compilerOptions": { "baseUrl": ".", diff --git a/examples/third-party-queries-caching/tsconfig.json b/examples/third-party-queries-caching/tsconfig.json index 5b672cc6e1..c54afd12a4 100644 --- a/examples/third-party-queries-caching/tsconfig.json +++ b/examples/third-party-queries-caching/tsconfig.json @@ -4,7 +4,8 @@ "./**/*.d.ts", "./**/*.ts", "./**/*.tsx", - "../../templates/skeleton/*.d.ts" + "../../templates/skeleton/*.d.ts", + "../../templates/skeleton/vite.config.ts" ], "compilerOptions": { "baseUrl": ".", diff --git a/packages/hydrogen/dev.env.d.ts b/packages/hydrogen/dev.env.d.ts index 8acc32f1d7..0ebedba179 100644 --- a/packages/hydrogen/dev.env.d.ts +++ b/packages/hydrogen/dev.env.d.ts @@ -38,3 +38,9 @@ declare module '@shopify/remix-oxygen' { // declare local additions to the Remix session data here } } + +declare module "@remix-run/server-runtime" { + interface Future { + v3_singleFetch: true; + } +} From 40130cd7ebfcbca9504ad0421557d56a87dfffbf Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 15 Jan 2025 13:07:08 -0800 Subject: [PATCH 07/22] formet --- packages/hydrogen/dev.env.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hydrogen/dev.env.d.ts b/packages/hydrogen/dev.env.d.ts index 0ebedba179..6bb59cf9e2 100644 --- a/packages/hydrogen/dev.env.d.ts +++ b/packages/hydrogen/dev.env.d.ts @@ -39,7 +39,7 @@ declare module '@shopify/remix-oxygen' { } } -declare module "@remix-run/server-runtime" { +declare module '@remix-run/server-runtime' { interface Future { v3_singleFetch: true; } From dda1d5fa71a3cb245596c4bbf7388739f0d8143a Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 15 Jan 2025 13:18:44 -0800 Subject: [PATCH 08/22] add future flag to preview doc --- docs/preview/vite.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/preview/vite.config.js b/docs/preview/vite.config.js index 4510819d7f..be2d91154f 100644 --- a/docs/preview/vite.config.js +++ b/docs/preview/vite.config.js @@ -23,6 +23,8 @@ export default defineConfig({ v3_fetcherPersist: false, v3_relativeSplatPath: false, v3_throwAbortReason: false, + v3_lazyRouteDiscovery: true, + v3_singleFetch: true, }, }), tsconfigPaths(), From 7e79643f84da3aa3ae36772f66a52a35c100e876 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 15 Jan 2025 14:00:06 -0800 Subject: [PATCH 09/22] Add test for redirect uri --- .../hydrogen/src/customer/customer.test.ts | 60 +++++++++++++++++++ packages/hydrogen/src/customer/customer.ts | 3 +- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/packages/hydrogen/src/customer/customer.test.ts b/packages/hydrogen/src/customer/customer.test.ts index f3f7693df6..93d8a05159 100644 --- a/packages/hydrogen/src/customer/customer.test.ts +++ b/packages/hydrogen/src/customer/customer.test.ts @@ -1392,6 +1392,66 @@ describe('customer', () => { expect(customAuthStatusHandler).toHaveBeenCalledOnce(); } }); + + it('handles Remix `https://localhost/account/orders.data` url extensions when passing current path as param if logged out', async () => { + const customer = createCustomerAccountClient({ + session, + customerAccountId: 'customerAccountId', + customerAccountUrl: 'https://customer-api', + request: new Request('https://localhost/account/orders.data'), + waitUntil: vi.fn(), + }); + (session.get as any).mockReturnValueOnce(undefined); + + try { + await customer.handleAuthStatus(); + } catch (error) { + expect((error as Response).status).toBe(302); + expect((error as Response).headers.get('location')).toBe( + '/account/login?return_to=%2Faccount%2Forders', + ); + } + }); + + it('handles Remix `https://localhost/account/_root.data` url extensions when passing current path as param if logged out', async () => { + const customer = createCustomerAccountClient({ + session, + customerAccountId: 'customerAccountId', + customerAccountUrl: 'https://customer-api', + request: new Request('https://localhost/account/_root.data'), + waitUntil: vi.fn(), + }); + (session.get as any).mockReturnValueOnce(undefined); + + try { + await customer.handleAuthStatus(); + } catch (error) { + expect((error as Response).status).toBe(302); + expect((error as Response).headers.get('location')).toBe( + '/account/login?return_to=%2Faccount', + ); + } + }); + + it('handles Remix `https://localhost/_root.data` url extensions when passing current path as param if logged out', async () => { + const customer = createCustomerAccountClient({ + session, + customerAccountId: 'customerAccountId', + customerAccountUrl: 'https://customer-api', + request: new Request('https://localhost/_root.data'), + waitUntil: vi.fn(), + }); + (session.get as any).mockReturnValueOnce(undefined); + + try { + await customer.handleAuthStatus(); + } catch (error) { + expect((error as Response).status).toBe(302); + expect((error as Response).headers.get('location')).toBe( + '/account/login?return_to=%2F', + ); + } + }); }); describe('query', () => { diff --git a/packages/hydrogen/src/customer/customer.ts b/packages/hydrogen/src/customer/customer.ts index 622e865395..20299f7ba5 100644 --- a/packages/hydrogen/src/customer/customer.ts +++ b/packages/hydrogen/src/customer/customer.ts @@ -71,7 +71,8 @@ function defaultAuthStatusHandler( */ const cleanedPathname = pathname .replace(/\.data$/, '') - .replace(/^\/_root$/, '/'); + .replace(/\/_root$/, '/') + .replace(/(.+)\/$/, '$1'); const redirectTo = defaultLoginUrl + From f7b45a2ef21abcd497a9c4e129208202da809d8b Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 15 Jan 2025 14:01:44 -0800 Subject: [PATCH 10/22] Update templates/skeleton/app/routes/search.tsx Co-authored-by: Rheese <110670476+rbshop@users.noreply.github.com> --- templates/skeleton/app/routes/search.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/skeleton/app/routes/search.tsx b/templates/skeleton/app/routes/search.tsx index 1f2aa590eb..c423960f08 100644 --- a/templates/skeleton/app/routes/search.tsx +++ b/templates/skeleton/app/routes/search.tsx @@ -28,8 +28,6 @@ export async function loader({request, context}: LoaderFunctionArgs) { return {term: '', result: null, error: error.message}; }); - - return await searchPromise; } From 9166141490f3c4bc536c5c199333d2811af54d59 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 15 Jan 2025 14:17:15 -0800 Subject: [PATCH 11/22] revert other flag change --- docs/preview/vite.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/preview/vite.config.js b/docs/preview/vite.config.js index be2d91154f..3cabcc28c3 100644 --- a/docs/preview/vite.config.js +++ b/docs/preview/vite.config.js @@ -23,7 +23,6 @@ export default defineConfig({ v3_fetcherPersist: false, v3_relativeSplatPath: false, v3_throwAbortReason: false, - v3_lazyRouteDiscovery: true, v3_singleFetch: true, }, }), From 34beed24880968ed5d83e9807ed91be37dfbd590 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Fri, 17 Jan 2025 15:05:44 -0800 Subject: [PATCH 12/22] Add changeset --- .changeset/plenty-cycles-act.md | 93 ++++++++++++++++++++++++++++++++ .changeset/purple-buses-laugh.md | 7 +++ 2 files changed, 100 insertions(+) create mode 100644 .changeset/plenty-cycles-act.md create mode 100644 .changeset/purple-buses-laugh.md diff --git a/.changeset/plenty-cycles-act.md b/.changeset/plenty-cycles-act.md new file mode 100644 index 0000000000..72928dbffe --- /dev/null +++ b/.changeset/plenty-cycles-act.md @@ -0,0 +1,93 @@ +--- +'skeleton': patch +--- + +[**Breaking change**] + +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 + +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 ``. + + ```diff + const body = await renderToReadableStream( + + + , + ``` +3. 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} from "@shopify/remix-oxygen"; + + export async function loader({}: LoaderFunctionArgs) { + let tasks = await fetchTasks(); + - return json(tasks, { + + return data(tasks, { + headers: { + "Cache-Control": "public, max-age=604800" + } + }); + } + ``` diff --git a/.changeset/purple-buses-laugh.md b/.changeset/purple-buses-laugh.md new file mode 100644 index 0000000000..f1b9a54a92 --- /dev/null +++ b/.changeset/purple-buses-laugh.md @@ -0,0 +1,7 @@ +--- +'@shopify/remix-oxygen': patch +'@shopify/hydrogen': patch +'@shopify/cli-hydrogen': patch +--- + +Turn on Remix `v3_singleFetch` future flag From 025b6a296b46c4693421b6192ecaf6b3755eea86 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Mon, 20 Jan 2025 12:52:40 -0800 Subject: [PATCH 13/22] update changeset --- .changeset/plenty-cycles-act.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.changeset/plenty-cycles-act.md b/.changeset/plenty-cycles-act.md index 72928dbffe..21521ce6b7 100644 --- a/.changeset/plenty-cycles-act.md +++ b/.changeset/plenty-cycles-act.md @@ -9,6 +9,8 @@ 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 @@ -48,7 +50,7 @@ Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single /> , ``` -3. Deprecate `json` and `defer` import usage from `@shopify/remix-oxygen` +3. Deprecate `json` and `defer` import usage from `@shopify/remix-oxygen`. Remove `json()`/`defer()` in favor of raw objects. From 52d32c11455ee219ef572ca6c0b1240ff8e87363 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 5 Feb 2025 11:47:08 -0800 Subject: [PATCH 14/22] Fix test --- package-lock.json | 14 +++++++------- packages/hydrogen/src/customer/customer.test.ts | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index f8ba3725b2..cad9e7dbe8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31927,7 +31927,7 @@ }, "packages/cli": { "name": "@shopify/cli-hydrogen", - "version": "9.0.4", + "version": "9.0.5", "license": "MIT", "dependencies": { "@ast-grep/napi": "0.11.0", @@ -31976,7 +31976,7 @@ "@graphql-codegen/cli": "^5.0.2", "@remix-run/dev": "^2.1.0", "@shopify/hydrogen-codegen": "^0.3.2", - "@shopify/mini-oxygen": "^3.1.0", + "@shopify/mini-oxygen": "^3.1.1", "graphql-config": "^5.0.3", "vite": "^5.1.0" }, @@ -32261,7 +32261,7 @@ }, "packages/create-hydrogen": { "name": "@shopify/create-hydrogen", - "version": "5.0.14", + "version": "5.0.15", "license": "MIT", "dependencies": { "@ast-grep/napi": "0.11.0" @@ -34465,7 +34465,7 @@ }, "packages/mini-oxygen": { "name": "@shopify/mini-oxygen", - "version": "3.1.0", + "version": "3.1.1", "license": "MIT", "dependencies": { "@miniflare/cache": "^2.14.2", @@ -34560,7 +34560,7 @@ }, "packages/remix-oxygen": { "name": "@shopify/remix-oxygen", - "version": "2.0.9", + "version": "2.0.10", "license": "MIT", "devDependencies": { "@remix-run/server-runtime": "^2.15.2", @@ -34577,7 +34577,7 @@ "@remix-run/react": "^2.15.2", "@remix-run/server-runtime": "^2.15.2", "@shopify/hydrogen": "2025.1.0", - "@shopify/remix-oxygen": "^2.0.9", + "@shopify/remix-oxygen": "^2.0.10", "graphql": "^16.6.0", "graphql-tag": "^2.12.6", "isbot": "^3.8.0", @@ -34590,7 +34590,7 @@ "@remix-run/eslint-config": "^2.15.2", "@shopify/cli": "~3.74.1", "@shopify/hydrogen-codegen": "^0.3.2", - "@shopify/mini-oxygen": "^3.1.0", + "@shopify/mini-oxygen": "^3.1.1", "@shopify/oxygen-workers-types": "^4.1.2", "@shopify/prettier-config": "^1.1.2", "@total-typescript/ts-reset": "^0.4.2", diff --git a/packages/hydrogen/src/customer/customer.test.ts b/packages/hydrogen/src/customer/customer.test.ts index 82881aca93..9fa954e16b 100644 --- a/packages/hydrogen/src/customer/customer.test.ts +++ b/packages/hydrogen/src/customer/customer.test.ts @@ -946,7 +946,7 @@ describe('customer', () => { const customer = createCustomerAccountClient({ session, customerAccountId: 'customerAccountId', - customerAccountUrl: 'https://customer-api', + shopId: '1', request: new Request('https://localhost/account/orders.data'), waitUntil: vi.fn(), }); @@ -966,7 +966,7 @@ describe('customer', () => { const customer = createCustomerAccountClient({ session, customerAccountId: 'customerAccountId', - customerAccountUrl: 'https://customer-api', + shopId: '1', request: new Request('https://localhost/account/_root.data'), waitUntil: vi.fn(), }); @@ -986,7 +986,7 @@ describe('customer', () => { const customer = createCustomerAccountClient({ session, customerAccountId: 'customerAccountId', - customerAccountUrl: 'https://customer-api', + shopId: '1', request: new Request('https://localhost/_root.data'), waitUntil: vi.fn(), }); From fa2841ddaf31be04277049ae7c810bffda947387 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 5 Feb 2025 16:12:08 -0800 Subject: [PATCH 15/22] Make sure headers are not lost --- .../analytics-setup/js/app/routes/cart.jsx | 6 + .../analytics-setup/ts/app/routes/cart.tsx | 4 +- .../custom-cart-method/app/routes/cart.tsx | 4 +- .../app/routes/cart.tsx | 5 +- examples/multipass/app/routes/cart.tsx | 4 +- package-lock.json | 583 ++++-------------- templates/skeleton/app/routes/cart.tsx | 4 +- 7 files changed, 142 insertions(+), 468 deletions(-) diff --git a/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx b/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx index 6cb2c6fbd8..64d9c2c919 100644 --- a/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx +++ b/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx @@ -10,6 +10,11 @@ export const meta = () => { return [{title: `Hydrogen | Cart`}]; }; +/** + * @type {HeadersFunction} + */ +export const headers = ({ actionHeaders }) => actionHeaders; + /** * @param {ActionFunctionArgs} */ @@ -118,6 +123,7 @@ export default function Cart() { } /** @template T @typedef {import('@remix-run/react').MetaFunction} MetaFunction */ +/** @template T @typedef {import('@remix-run/react').HeadersFunction} HeadersFunction */ /** @typedef {import('@shopify/hydrogen').CartQueryDataReturn} CartQueryDataReturn */ /** @typedef {import('@shopify/remix-oxygen').LoaderFunctionArgs} LoaderFunctionArgs */ /** @typedef {import('@shopify/remix-oxygen').SerializeFrom} ActionReturnData */ diff --git a/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx b/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx index 8d2dffc482..1ecfef7083 100644 --- a/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx +++ b/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx @@ -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 {data, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs, 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; diff --git a/examples/custom-cart-method/app/routes/cart.tsx b/examples/custom-cart-method/app/routes/cart.tsx index 4d0057f865..70fea6c192 100644 --- a/examples/custom-cart-method/app/routes/cart.tsx +++ b/examples/custom-cart-method/app/routes/cart.tsx @@ -1,7 +1,7 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs, HeadersFunction} from '@shopify/remix-oxygen'; import type { SelectedOptionInput, CartLineUpdateInput, @@ -13,6 +13,8 @@ export const meta: MetaFunction = () => { return [{title: `Hydrogen | Cart`}]; }; +export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; + export async function action({request, context}: ActionFunctionArgs) { const {cart} = context; diff --git a/examples/legacy-customer-account-flow/app/routes/cart.tsx b/examples/legacy-customer-account-flow/app/routes/cart.tsx index c8f5e49e59..ae918f60b4 100644 --- a/examples/legacy-customer-account-flow/app/routes/cart.tsx +++ b/examples/legacy-customer-account-flow/app/routes/cart.tsx @@ -1,14 +1,15 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs, HeadersFunction} from '@shopify/remix-oxygen'; import {CartMain} from '~/components/CartMain'; -import type {RootLoader} from '~/root'; export const meta: MetaFunction = () => { return [{title: `Hydrogen | Cart`}]; }; +export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; + export async function action({request, context}: ActionFunctionArgs) { /***********************************************/ /********** EXAMPLE UPDATE STARTS ************/ diff --git a/examples/multipass/app/routes/cart.tsx b/examples/multipass/app/routes/cart.tsx index 6f10b7cc87..ade682f7ea 100644 --- a/examples/multipass/app/routes/cart.tsx +++ b/examples/multipass/app/routes/cart.tsx @@ -1,13 +1,15 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs, HeadersFunction} from '@shopify/remix-oxygen'; import {CartMain} from '~/components/Cart'; export const meta: MetaFunction = () => { return [{title: `Hydrogen | Cart`}]; }; +export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; + export async function action({request, context}: ActionFunctionArgs) { /***********************************************/ /********** EXAMPLE UPDATE STARTS ************/ diff --git a/package-lock.json b/package-lock.json index 8a60daa9a9..ba3f9d099e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -83,209 +83,6 @@ "node": ">=18.0.0" } }, - "docs/preview/node_modules/@remix-run/eslint-config": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/eslint-config/-/eslint-config-2.15.2.tgz", - "integrity": "sha512-zch4J6ImrEjSvCbT9qufhubm6ym8E7LgGWpRihR2KB6oZndKEnSNe4ZklrwFXxIeGG+ACFSVm9JPR0lmAcAbTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.8", - "@babel/eslint-parser": "^7.21.8", - "@babel/preset-react": "^7.18.6", - "@rushstack/eslint-patch": "^1.2.0", - "@typescript-eslint/eslint-plugin": "^5.59.0", - "@typescript-eslint/parser": "^5.59.0", - "eslint-import-resolver-node": "0.3.7", - "eslint-import-resolver-typescript": "^3.5.4", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-jest": "^26.9.0", - "eslint-plugin-jest-dom": "^4.0.3", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-react": "^7.32.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-testing-library": "^5.10.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.0", - "react": "^18.0.0", - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "docs/preview/node_modules/@remix-run/express": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/express/-/express-2.15.2.tgz", - "integrity": "sha512-54FKQ6/Zj2DCxc4/9tWKUJLPkFakCUf1m7j7a5zp4JGDr436lkZEpS9btfoBZAVq14SIMp5Uc4yt5rUJ1PMORw==", - "license": "MIT", - "dependencies": { - "@remix-run/node": "2.15.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "express": "^4.20.0", - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "docs/preview/node_modules/@remix-run/express/node_modules/@remix-run/node": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.15.2.tgz", - "integrity": "sha512-NS/h5uxje7DYCNgcKqKAiUhf0r2HVnoYUBWLyIIMmCUP1ddWurBP6xTPcWzGhEvV/EvguniYi1wJZ5+X8sonWw==", - "license": "MIT", - "dependencies": { - "@remix-run/server-runtime": "2.15.2", - "@remix-run/web-fetch": "^4.4.2", - "@web3-storage/multipart-parser": "^1.0.0", - "cookie-signature": "^1.1.0", - "source-map-support": "^0.5.21", - "stream-slice": "^0.1.2", - "undici": "^6.11.1" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "docs/preview/node_modules/@remix-run/express/node_modules/@remix-run/router": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", - "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "docs/preview/node_modules/@remix-run/express/node_modules/@remix-run/server-runtime": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.15.2.tgz", - "integrity": "sha512-OqiPcvEnnU88B8b1LIWHHkQ3Tz2GDAmQ1RihFNQsbrFKpDsQLkw0lJlnfgKA/uHd0CEEacpfV7C9qqJT3V6Z2g==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.21.0", - "@types/cookie": "^0.6.0", - "@web3-storage/multipart-parser": "^1.0.0", - "cookie": "^0.6.0", - "set-cookie-parser": "^2.4.8", - "source-map": "^0.7.3", - "turbo-stream": "2.4.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "docs/preview/node_modules/@remix-run/serve": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/serve/-/serve-2.15.2.tgz", - "integrity": "sha512-m/nZtAUzzGcixNgNc3RNjA1ocFlWAuZFALpZ5fJdPXmITwqRwfjo/1gI+jx7AL7haoo+4j/sAljuAQw2CiswXA==", - "license": "MIT", - "dependencies": { - "@remix-run/express": "2.15.2", - "@remix-run/node": "2.15.2", - "chokidar": "^3.5.3", - "compression": "^1.7.4", - "express": "^4.20.0", - "get-port": "5.1.1", - "morgan": "^1.10.0", - "source-map-support": "^0.5.21" - }, - "bin": { - "remix-serve": "dist/cli.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "docs/preview/node_modules/@remix-run/serve/node_modules/@remix-run/node": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.15.2.tgz", - "integrity": "sha512-NS/h5uxje7DYCNgcKqKAiUhf0r2HVnoYUBWLyIIMmCUP1ddWurBP6xTPcWzGhEvV/EvguniYi1wJZ5+X8sonWw==", - "license": "MIT", - "dependencies": { - "@remix-run/server-runtime": "2.15.2", - "@remix-run/web-fetch": "^4.4.2", - "@web3-storage/multipart-parser": "^1.0.0", - "cookie-signature": "^1.1.0", - "source-map-support": "^0.5.21", - "stream-slice": "^0.1.2", - "undici": "^6.11.1" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "docs/preview/node_modules/@remix-run/serve/node_modules/@remix-run/router": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", - "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "docs/preview/node_modules/@remix-run/serve/node_modules/@remix-run/server-runtime": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.15.2.tgz", - "integrity": "sha512-OqiPcvEnnU88B8b1LIWHHkQ3Tz2GDAmQ1RihFNQsbrFKpDsQLkw0lJlnfgKA/uHd0CEEacpfV7C9qqJT3V6Z2g==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.21.0", - "@types/cookie": "^0.6.0", - "@web3-storage/multipart-parser": "^1.0.0", - "cookie": "^0.6.0", - "set-cookie-parser": "^2.4.8", - "source-map": "^0.7.3", - "turbo-stream": "2.4.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "docs/preview/node_modules/@tailwindcss/oxide": { "version": "4.0.0-beta.8", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.0-beta.8.tgz", @@ -495,15 +292,6 @@ "vite": "^5.2.0 || ^6" } }, - "docs/preview/node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, "docs/preview/node_modules/lightningcss": { "version": "1.28.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.28.2.tgz", @@ -775,135 +563,6 @@ "node": ">=18.0.0" } }, - "examples/express/node_modules/@remix-run/eslint-config": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/eslint-config/-/eslint-config-2.15.2.tgz", - "integrity": "sha512-zch4J6ImrEjSvCbT9qufhubm6ym8E7LgGWpRihR2KB6oZndKEnSNe4ZklrwFXxIeGG+ACFSVm9JPR0lmAcAbTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.8", - "@babel/eslint-parser": "^7.21.8", - "@babel/preset-react": "^7.18.6", - "@rushstack/eslint-patch": "^1.2.0", - "@typescript-eslint/eslint-plugin": "^5.59.0", - "@typescript-eslint/parser": "^5.59.0", - "eslint-import-resolver-node": "0.3.7", - "eslint-import-resolver-typescript": "^3.5.4", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-jest": "^26.9.0", - "eslint-plugin-jest-dom": "^4.0.3", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-react": "^7.32.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-testing-library": "^5.10.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.0", - "react": "^18.0.0", - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "examples/express/node_modules/@remix-run/express": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/express/-/express-2.15.2.tgz", - "integrity": "sha512-54FKQ6/Zj2DCxc4/9tWKUJLPkFakCUf1m7j7a5zp4JGDr436lkZEpS9btfoBZAVq14SIMp5Uc4yt5rUJ1PMORw==", - "license": "MIT", - "dependencies": { - "@remix-run/node": "2.15.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "express": "^4.20.0", - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "examples/express/node_modules/@remix-run/express/node_modules/@remix-run/node": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.15.2.tgz", - "integrity": "sha512-NS/h5uxje7DYCNgcKqKAiUhf0r2HVnoYUBWLyIIMmCUP1ddWurBP6xTPcWzGhEvV/EvguniYi1wJZ5+X8sonWw==", - "license": "MIT", - "dependencies": { - "@remix-run/server-runtime": "2.15.2", - "@remix-run/web-fetch": "^4.4.2", - "@web3-storage/multipart-parser": "^1.0.0", - "cookie-signature": "^1.1.0", - "source-map-support": "^0.5.21", - "stream-slice": "^0.1.2", - "undici": "^6.11.1" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "examples/express/node_modules/@remix-run/express/node_modules/@remix-run/router": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", - "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "examples/express/node_modules/@remix-run/express/node_modules/@remix-run/server-runtime": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.15.2.tgz", - "integrity": "sha512-OqiPcvEnnU88B8b1LIWHHkQ3Tz2GDAmQ1RihFNQsbrFKpDsQLkw0lJlnfgKA/uHd0CEEacpfV7C9qqJT3V6Z2g==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.21.0", - "@types/cookie": "^0.6.0", - "@web3-storage/multipart-parser": "^1.0.0", - "cookie": "^0.6.0", - "set-cookie-parser": "^2.4.8", - "source-map": "^0.7.3", - "turbo-stream": "2.4.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "examples/express/node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, "examples/gtm": { "name": "example-gtm" }, @@ -8011,14 +7670,49 @@ "node": ">=6" } }, + "node_modules/@remix-run/eslint-config": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/eslint-config/-/eslint-config-2.15.3.tgz", + "integrity": "sha512-p9V3rvFAgFRPyjxZnW9bTR7WZfTl13wcbfWtySWcNdeqJZauCN9xdkB1r+PnsjaK77RXWtSCsEgRn/+mwx9Kzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.8", + "@babel/eslint-parser": "^7.21.8", + "@babel/preset-react": "^7.18.6", + "@rushstack/eslint-patch": "^1.2.0", + "@typescript-eslint/eslint-plugin": "^5.59.0", + "@typescript-eslint/parser": "^5.59.0", + "eslint-import-resolver-node": "0.3.7", + "eslint-import-resolver-typescript": "^3.5.4", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-jest": "^26.9.0", + "eslint-plugin-jest-dom": "^4.0.3", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-testing-library": "^5.10.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0", + "react": "^18.0.0", + "typescript": "^5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@remix-run/express": { "version": "2.15.3", "resolved": "https://registry.npmjs.org/@remix-run/express/-/express-2.15.3.tgz", "integrity": "sha512-6a4S5KrRNiLDiaOneuoMpMMFo9ztB4gZ1AOJSX6BmqpXmcq/P+WhRXOqXrUYeL4fMstJhTVM8CzlInlfpem8Vw==", - "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "@remix-run/node": "2.15.3" }, @@ -8109,10 +7803,7 @@ "version": "2.15.3", "resolved": "https://registry.npmjs.org/@remix-run/serve/-/serve-2.15.3.tgz", "integrity": "sha512-7P0FWeNu1NEaSJpL2Xn8fZbr4zxkrOR6Qg03p7iYbipcQ1L0zr9Nxe5KKG4m9n8EyRWWxDV2L+wXHI7Ul9HiMw==", - "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "@remix-run/express": "2.15.3", "@remix-run/node": "2.15.3", @@ -9505,6 +9196,89 @@ "resolved": "packages/mini-oxygen", "link": true }, + "node_modules/@shopify/oxygen-cli": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/@shopify/oxygen-cli/-/oxygen-cli-4.6.6.tgz", + "integrity": "sha512-cdxyw7wu0lhpfC1RB9jRUOW5TP1SDsDjeJHg6XwzC/2hsFXuwJiJtb9IoMMkoPoX0uEUSJdMjltheauOnvCzuA==", + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32" + ], + "dependencies": { + "@bugsnag/core": "^8.1.1", + "@bugsnag/js": "^8.1.2", + "@bugsnag/node": "^8.0.0", + "async": "^3.2.6" + }, + "bin": { + "oxygen-cli": "dist/oxygen-cli.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "@oclif/core": "^3.15.1", + "@shopify/cli-kit": "^3.58.0" + } + }, + "node_modules/@shopify/oxygen-cli/node_modules/@bugsnag/browser": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@bugsnag/browser/-/browser-8.2.0.tgz", + "integrity": "sha512-C4BfE3eVsjOAqoXbdrPXfKbgp/hz2H7mKBU0p11Jf9uz+5gUCfZK+39JLrQKvRXwqoDcTlBSfz9Xz5kXLyHg2Q==", + "license": "MIT", + "dependencies": { + "@bugsnag/core": "^8.2.0" + } + }, + "node_modules/@shopify/oxygen-cli/node_modules/@bugsnag/core": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@bugsnag/core/-/core-8.2.0.tgz", + "integrity": "sha512-dFSs80ZwJ508nlC6UTLTUMdHgTaHY5UKvMiuHqstCQrQrOjqFcIv+x4o+l2WrSyOpoYhHAxDlKfzKN8AjwslQw==", + "license": "MIT", + "dependencies": { + "@bugsnag/cuid": "^3.0.0", + "@bugsnag/safe-json-stringify": "^6.0.0", + "error-stack-parser": "^2.0.3", + "iserror": "^0.0.2", + "stack-generator": "^2.0.3" + } + }, + "node_modules/@shopify/oxygen-cli/node_modules/@bugsnag/js": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@bugsnag/js/-/js-8.2.0.tgz", + "integrity": "sha512-DTtQwV1Ly5VXSOnVtzW8gSwB+ld3qIc/h0yMS836DEYUfA3V9JPwJE3+2EbD8Ea2ogkDWZ+a0jl0SNSNGiOmfA==", + "license": "MIT", + "dependencies": { + "@bugsnag/browser": "^8.2.0", + "@bugsnag/node": "^8.2.0" + } + }, + "node_modules/@shopify/oxygen-cli/node_modules/@bugsnag/node": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@bugsnag/node/-/node-8.2.0.tgz", + "integrity": "sha512-6XC/KgX61m6YFgsBQP/GaH1UzlJkJmpi3AwlZQLsXloRh3O9lM/0EIk6+2sZm+vlz+GwxCFavcuIDgVmH/qi7Q==", + "license": "MIT", + "dependencies": { + "@bugsnag/core": "^8.2.0", + "byline": "^5.0.0", + "error-stack-parser": "^2.0.3", + "iserror": "^0.0.2", + "pump": "^3.0.0", + "stack-generator": "^2.0.3" + } + }, + "node_modules/@shopify/oxygen-cli/node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/@shopify/oxygen-workers-types": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/@shopify/oxygen-workers-types/-/oxygen-workers-types-4.1.3.tgz", @@ -32251,74 +32025,6 @@ } } }, - "packages/cli/node_modules/@bugsnag/browser": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@bugsnag/browser/-/browser-8.1.2.tgz", - "integrity": "sha512-COP3OanbGCOS/K4g0rGwSYk2/RcIooG2PqxqdrF0MDY4u3Nz44iOaJg8w7WlOr8CckmDEiwSW2DVR+M7qZcoQw==", - "dependencies": { - "@bugsnag/core": "^8.1.1" - } - }, - "packages/cli/node_modules/@bugsnag/core": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@bugsnag/core/-/core-8.1.1.tgz", - "integrity": "sha512-ytOWqwm4H2h8rADqYPR+tQpDHsBav3NEZ5E2VSCCxPbT2R89Q0/t0PZTbQVlOS+TRutajO29VxTV9qsAREfpSw==", - "dependencies": { - "@bugsnag/cuid": "^3.0.0", - "@bugsnag/safe-json-stringify": "^6.0.0", - "error-stack-parser": "^2.0.3", - "iserror": "^0.0.2", - "stack-generator": "^2.0.3" - } - }, - "packages/cli/node_modules/@bugsnag/js": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@bugsnag/js/-/js-8.1.2.tgz", - "integrity": "sha512-vMl/TlMTpm0Xv4IKqYpmsjeVE4nPWAZL8uOxOi0JI6dgTaWE8+VQF6KuSpeKiA7Pr34uUxpWhSBrkZ3uSwCBdg==", - "dependencies": { - "@bugsnag/browser": "^8.1.2", - "@bugsnag/node": "^8.1.1" - } - }, - "packages/cli/node_modules/@bugsnag/node": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@bugsnag/node/-/node-8.1.1.tgz", - "integrity": "sha512-Ckdgj4tQto3CH4wWo12BAG0lQuS9s5SUpZvwmYF3meW5Qxq7D8XHZzrfGQo9nb1VTvmwOB+uyMt8En6Cy86Jcg==", - "dependencies": { - "@bugsnag/core": "^8.1.1", - "byline": "^5.0.0", - "error-stack-parser": "^2.0.3", - "iserror": "^0.0.2", - "pump": "^3.0.0", - "stack-generator": "^2.0.3" - } - }, - "packages/cli/node_modules/@shopify/oxygen-cli": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/@shopify/oxygen-cli/-/oxygen-cli-4.5.3.tgz", - "integrity": "sha512-mCpseNEqDbR26fPmIKL83BDsYSxXu/eeWrRGTG8cdQhq1z+xbDn93w/So/gi0r0MRH2vYsPOzJOIUyiWWVawDQ==", - "os": [ - "darwin", - "linux", - "win32" - ], - "dependencies": { - "@bugsnag/core": "^8.0.0", - "@bugsnag/js": "^8.0.0", - "@bugsnag/node": "^8.0.0", - "async": "^3.2.6" - }, - "bin": { - "oxygen-cli": "dist/oxygen-cli.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "@oclif/core": "^3.15.1", - "@shopify/cli-kit": "^3.58.0" - } - }, "packages/cli/node_modules/@shopify/plugin-cloudflare": { "version": "3.74.1", "resolved": "https://registry.npmjs.org/@shopify/plugin-cloudflare/-/plugin-cloudflare-3.74.1.tgz", @@ -32442,15 +32148,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/cli/node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "packages/cli/node_modules/slice-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", @@ -34857,44 +34554,6 @@ "engines": { "node": ">=18.0.0" } - }, - "templates/skeleton/node_modules/@remix-run/eslint-config": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/eslint-config/-/eslint-config-2.15.2.tgz", - "integrity": "sha512-zch4J6ImrEjSvCbT9qufhubm6ym8E7LgGWpRihR2KB6oZndKEnSNe4ZklrwFXxIeGG+ACFSVm9JPR0lmAcAbTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.8", - "@babel/eslint-parser": "^7.21.8", - "@babel/preset-react": "^7.18.6", - "@rushstack/eslint-patch": "^1.2.0", - "@typescript-eslint/eslint-plugin": "^5.59.0", - "@typescript-eslint/parser": "^5.59.0", - "eslint-import-resolver-node": "0.3.7", - "eslint-import-resolver-typescript": "^3.5.4", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-jest": "^26.9.0", - "eslint-plugin-jest-dom": "^4.0.3", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-react": "^7.32.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-testing-library": "^5.10.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.0", - "react": "^18.0.0", - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } } } } diff --git a/templates/skeleton/app/routes/cart.tsx b/templates/skeleton/app/routes/cart.tsx index 86b3f08888..8cee015098 100644 --- a/templates/skeleton/app/routes/cart.tsx +++ b/templates/skeleton/app/routes/cart.tsx @@ -1,13 +1,15 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs, type ActionFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs, 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; From d25ac4612eb5d8c90b218e85804b555fd015e977 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 5 Feb 2025 16:37:47 -0800 Subject: [PATCH 16/22] Update instructions --- .changeset/plenty-cycles-act.md | 40 ++++++++++++++++++- .../analytics-setup/ts/app/routes/cart.tsx | 2 +- .../custom-cart-method/app/routes/cart.tsx | 2 +- .../app/routes/cart.tsx | 2 +- examples/multipass/app/routes/cart.tsx | 2 +- templates/skeleton/app/routes/cart.tsx | 2 +- 6 files changed, 43 insertions(+), 7 deletions(-) diff --git a/.changeset/plenty-cycles-act.md b/.changeset/plenty-cycles-act.md index 21521ce6b7..e236e4bdf6 100644 --- a/.changeset/plenty-cycles-act.md +++ b/.changeset/plenty-cycles-act.md @@ -50,7 +50,37 @@ Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single /> , ``` -3. Deprecate `json` and `defer` import usage from `@shopify/remix-oxygen`. + +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}, + ); + } + ``` + +4. Deprecate `json` and `defer` import usage from `@shopify/remix-oxygen`. Remove `json()`/`defer()` in favor of raw objects. @@ -81,7 +111,13 @@ Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single ```diff - import {json} from "@shopify/remix-oxygen"; - + import {data} 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(); diff --git a/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx b/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx index 1ecfef7083..f64b889905 100644 --- a/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx +++ b/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx @@ -1,7 +1,7 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm, Analytics} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs, type ActionFunctionArgs, HeadersFunction} 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 = () => { diff --git a/examples/custom-cart-method/app/routes/cart.tsx b/examples/custom-cart-method/app/routes/cart.tsx index 70fea6c192..478f70b39b 100644 --- a/examples/custom-cart-method/app/routes/cart.tsx +++ b/examples/custom-cart-method/app/routes/cart.tsx @@ -1,7 +1,7 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs, type ActionFunctionArgs, HeadersFunction} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs, type HeadersFunction} from '@shopify/remix-oxygen'; import type { SelectedOptionInput, CartLineUpdateInput, diff --git a/examples/legacy-customer-account-flow/app/routes/cart.tsx b/examples/legacy-customer-account-flow/app/routes/cart.tsx index ae918f60b4..3713a6eb78 100644 --- a/examples/legacy-customer-account-flow/app/routes/cart.tsx +++ b/examples/legacy-customer-account-flow/app/routes/cart.tsx @@ -1,7 +1,7 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs, type ActionFunctionArgs, HeadersFunction} 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 = () => { diff --git a/examples/multipass/app/routes/cart.tsx b/examples/multipass/app/routes/cart.tsx index ade682f7ea..25fca265c7 100644 --- a/examples/multipass/app/routes/cart.tsx +++ b/examples/multipass/app/routes/cart.tsx @@ -1,7 +1,7 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs, type ActionFunctionArgs, HeadersFunction} from '@shopify/remix-oxygen'; +import {data, type LoaderFunctionArgs, type ActionFunctionArgs, type HeadersFunction} from '@shopify/remix-oxygen'; import {CartMain} from '~/components/Cart'; export const meta: MetaFunction = () => { diff --git a/templates/skeleton/app/routes/cart.tsx b/templates/skeleton/app/routes/cart.tsx index 8cee015098..6cf6b859c0 100644 --- a/templates/skeleton/app/routes/cart.tsx +++ b/templates/skeleton/app/routes/cart.tsx @@ -1,7 +1,7 @@ import {type MetaFunction, useLoaderData} from '@remix-run/react'; import type {CartQueryDataReturn} from '@shopify/hydrogen'; import {CartForm} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs, type ActionFunctionArgs, HeadersFunction} 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 = () => { From a429919f9519f121e32b08f84d671b349c3c2d40 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Wed, 5 Feb 2025 16:40:59 -0800 Subject: [PATCH 17/22] missed a bit of code diff --- .changeset/plenty-cycles-act.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.changeset/plenty-cycles-act.md b/.changeset/plenty-cycles-act.md index e236e4bdf6..900f7ff454 100644 --- a/.changeset/plenty-cycles-act.md +++ b/.changeset/plenty-cycles-act.md @@ -78,6 +78,12 @@ Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single {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`. From 70ced4aa5ad7d34438f3c1d1546b25a1ce37d8f6 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Thu, 6 Feb 2025 12:23:49 -0800 Subject: [PATCH 18/22] Fix headers on legacy example --- examples/legacy-customer-account-flow/app/root.tsx | 7 ++++++- .../legacy-customer-account-flow/app/routes/account.tsx | 6 ++++-- .../app/routes/account_.register.tsx | 3 +++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/examples/legacy-customer-account-flow/app/root.tsx b/examples/legacy-customer-account-flow/app/root.tsx index cc3b6ef801..4bd1724ef2 100644 --- a/examples/legacy-customer-account-flow/app/root.tsx +++ b/examples/legacy-customer-account-flow/app/root.tsx @@ -1,5 +1,5 @@ import {useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, HeadersFunction, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -64,6 +64,11 @@ export function links() { {rel: 'icon', type: 'image/svg+xml', href: favicon}, ]; } +/***********************************************/ +/********** EXAMPLE UPDATE STARTS ************/ +export const headers: HeadersFunction = ({ loaderHeaders }) => loaderHeaders; +/********** EXAMPLE UPDATE END ************/ +/***********************************************/ export async function loader({context}: LoaderFunctionArgs) { const {storefront, customerAccount, cart, env} = context; diff --git a/examples/legacy-customer-account-flow/app/routes/account.tsx b/examples/legacy-customer-account-flow/app/routes/account.tsx index 6547c07f92..321e5dd44c 100644 --- a/examples/legacy-customer-account-flow/app/routes/account.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account.tsx @@ -1,11 +1,13 @@ import {Form, NavLink, Outlet, useLoaderData} from '@remix-run/react'; -import {data, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, HeadersFunction, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import type {CustomerFragment} from 'storefrontapi.generated'; export function shouldRevalidate() { return true; } +export const headers: HeadersFunction = ({ loaderHeaders }) => loaderHeaders; + export async function loader({request, context}: LoaderFunctionArgs) { const {session, storefront} = context; const {pathname} = new URL(request.url); @@ -67,7 +69,7 @@ export async function loader({request, context}: LoaderFunctionArgs) { } } -export default function Acccount() { +export default function Account() { const {customer, isPrivateRoute, isAccountHome} = useLoaderData(); diff --git a/examples/legacy-customer-account-flow/app/routes/account_.register.tsx b/examples/legacy-customer-account-flow/app/routes/account_.register.tsx index 64c8a104ee..539b608ac2 100644 --- a/examples/legacy-customer-account-flow/app/routes/account_.register.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account_.register.tsx @@ -1,5 +1,6 @@ import { data, + HeadersFunction, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -14,6 +15,8 @@ type ActionResponse = { | null; }; +export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; + export async function loader({context}: LoaderFunctionArgs) { const customerAccessToken = await context.session.get('customerAccessToken'); if (customerAccessToken) { From 8d9474fae3e189dda7dcbf6343e8225c2fc41710 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Thu, 6 Feb 2025 13:00:58 -0800 Subject: [PATCH 19/22] add headers export to multipass --- .changeset/plenty-cycles-act.md | 4 ++-- .../shopify-dev/analytics-setup/js/app/routes/cart.jsx | 2 +- .../shopify-dev/analytics-setup/ts/app/routes/cart.tsx | 2 +- examples/custom-cart-method/app/routes/cart.tsx | 2 +- examples/legacy-customer-account-flow/app/root.tsx | 5 +++-- .../app/routes/account.tsx | 4 ++-- .../app/routes/account_.register.tsx | 2 +- .../legacy-customer-account-flow/app/routes/cart.tsx | 2 +- examples/multipass/app/root.tsx | 10 +++++++++- examples/multipass/app/routes/account.tsx | 4 +++- .../multipass/app/routes/account_.login.multipass.tsx | 3 +++ examples/multipass/app/routes/account_.register.tsx | 3 +++ examples/multipass/app/routes/cart.tsx | 2 +- templates/skeleton/app/routes/cart.tsx | 2 +- 14 files changed, 32 insertions(+), 15 deletions(-) diff --git a/.changeset/plenty-cycles-act.md b/.changeset/plenty-cycles-act.md index 900f7ff454..d8b4ea3434 100644 --- a/.changeset/plenty-cycles-act.md +++ b/.changeset/plenty-cycles-act.md @@ -61,7 +61,7 @@ Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single type ActionFunctionArgs, type HeadersFunction } from '@shopify/remix-oxygen'; - + export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; + + export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders; export async function action({request, context}: ActionFunctionArgs) { ... @@ -123,7 +123,7 @@ Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single + // 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 const headers: HeadersFunction = ({loaderHeaders}) => loaderHeaders; export async function loader({}: LoaderFunctionArgs) { let tasks = await fetchTasks(); diff --git a/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx b/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx index 64d9c2c919..e47887f7bf 100644 --- a/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx +++ b/docs/shopify-dev/analytics-setup/js/app/routes/cart.jsx @@ -13,7 +13,7 @@ export const meta = () => { /** * @type {HeadersFunction} */ -export const headers = ({ actionHeaders }) => actionHeaders; +export const headers = ({actionHeaders}) => actionHeaders; /** * @param {ActionFunctionArgs} diff --git a/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx b/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx index f64b889905..2d4a74de04 100644 --- a/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx +++ b/docs/shopify-dev/analytics-setup/ts/app/routes/cart.tsx @@ -8,7 +8,7 @@ export const meta: MetaFunction = () => { return [{title: `Hydrogen | Cart`}]; }; -export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; +export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders; export async function action({request, context}: ActionFunctionArgs) { const {cart} = context; diff --git a/examples/custom-cart-method/app/routes/cart.tsx b/examples/custom-cart-method/app/routes/cart.tsx index 478f70b39b..62a709be28 100644 --- a/examples/custom-cart-method/app/routes/cart.tsx +++ b/examples/custom-cart-method/app/routes/cart.tsx @@ -13,7 +13,7 @@ export const meta: MetaFunction = () => { return [{title: `Hydrogen | Cart`}]; }; -export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; +export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders; export async function action({request, context}: ActionFunctionArgs) { const {cart} = context; diff --git a/examples/legacy-customer-account-flow/app/root.tsx b/examples/legacy-customer-account-flow/app/root.tsx index 4bd1724ef2..e3ef746c2a 100644 --- a/examples/legacy-customer-account-flow/app/root.tsx +++ b/examples/legacy-customer-account-flow/app/root.tsx @@ -1,5 +1,5 @@ import {useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen'; -import {data, HeadersFunction, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type HeadersFunction, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -64,9 +64,10 @@ export function links() { {rel: 'icon', type: 'image/svg+xml', href: favicon}, ]; } + /***********************************************/ /********** EXAMPLE UPDATE STARTS ************/ -export const headers: HeadersFunction = ({ loaderHeaders }) => loaderHeaders; +export const headers: HeadersFunction = ({loaderHeaders}) => loaderHeaders; /********** EXAMPLE UPDATE END ************/ /***********************************************/ diff --git a/examples/legacy-customer-account-flow/app/routes/account.tsx b/examples/legacy-customer-account-flow/app/routes/account.tsx index 321e5dd44c..59e48e1ed1 100644 --- a/examples/legacy-customer-account-flow/app/routes/account.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account.tsx @@ -1,12 +1,12 @@ import {Form, NavLink, Outlet, useLoaderData} from '@remix-run/react'; -import {data, HeadersFunction, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type HeadersFunction, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import type {CustomerFragment} from 'storefrontapi.generated'; export function shouldRevalidate() { return true; } -export const headers: HeadersFunction = ({ loaderHeaders }) => loaderHeaders; +export const headers: HeadersFunction = ({loaderHeaders}) => loaderHeaders; export async function loader({request, context}: LoaderFunctionArgs) { const {session, storefront} = context; diff --git a/examples/legacy-customer-account-flow/app/routes/account_.register.tsx b/examples/legacy-customer-account-flow/app/routes/account_.register.tsx index 539b608ac2..46897ffaf4 100644 --- a/examples/legacy-customer-account-flow/app/routes/account_.register.tsx +++ b/examples/legacy-customer-account-flow/app/routes/account_.register.tsx @@ -15,7 +15,7 @@ type ActionResponse = { | null; }; -export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; +export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders; export async function loader({context}: LoaderFunctionArgs) { const customerAccessToken = await context.session.get('customerAccessToken'); diff --git a/examples/legacy-customer-account-flow/app/routes/cart.tsx b/examples/legacy-customer-account-flow/app/routes/cart.tsx index 3713a6eb78..0e5eb46717 100644 --- a/examples/legacy-customer-account-flow/app/routes/cart.tsx +++ b/examples/legacy-customer-account-flow/app/routes/cart.tsx @@ -8,7 +8,7 @@ export const meta: MetaFunction = () => { return [{title: `Hydrogen | Cart`}]; }; -export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; +export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders; export async function action({request, context}: ActionFunctionArgs) { /***********************************************/ diff --git a/examples/multipass/app/root.tsx b/examples/multipass/app/root.tsx index cc3b6ef801..85d9700afa 100644 --- a/examples/multipass/app/root.tsx +++ b/examples/multipass/app/root.tsx @@ -1,5 +1,5 @@ import {useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen'; -import {data, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type HeadersFunction, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import { Links, Meta, @@ -65,6 +65,14 @@ export function links() { ]; } + +/***********************************************/ +/********** EXAMPLE UPDATE STARTS ************/ +export const headers: HeadersFunction = ({loaderHeaders}) => loaderHeaders; +/********** EXAMPLE UPDATE END ************/ +/***********************************************/ + + export async function loader({context}: LoaderFunctionArgs) { const {storefront, customerAccount, cart, env} = context; const publicStoreDomain = env.PUBLIC_STORE_DOMAIN; diff --git a/examples/multipass/app/routes/account.tsx b/examples/multipass/app/routes/account.tsx index 6547c07f92..629f246d1e 100644 --- a/examples/multipass/app/routes/account.tsx +++ b/examples/multipass/app/routes/account.tsx @@ -1,11 +1,13 @@ import {Form, NavLink, Outlet, useLoaderData} from '@remix-run/react'; -import {data, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; +import {data, type HeadersFunction, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import type {CustomerFragment} from 'storefrontapi.generated'; export function shouldRevalidate() { return true; } +export const headers: HeadersFunction = ({loaderHeaders}) => loaderHeaders; + export async function loader({request, context}: LoaderFunctionArgs) { const {session, storefront} = context; const {pathname} = new URL(request.url); diff --git a/examples/multipass/app/routes/account_.login.multipass.tsx b/examples/multipass/app/routes/account_.login.multipass.tsx index 07a26074a3..25751e6b9b 100644 --- a/examples/multipass/app/routes/account_.login.multipass.tsx +++ b/examples/multipass/app/routes/account_.login.multipass.tsx @@ -3,6 +3,7 @@ import { redirect, type ActionFunctionArgs, type LoaderFunctionArgs, + type HeadersFunction, } from '@shopify/remix-oxygen'; import {Multipassify} from '~/lib/multipass/multipassify.server'; import type { @@ -11,6 +12,8 @@ import type { NotLoggedInResponseType, } from '~/lib/multipass/types'; +export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders; + /* Redirect document GET requests to the login page (housekeeping) */ diff --git a/examples/multipass/app/routes/account_.register.tsx b/examples/multipass/app/routes/account_.register.tsx index 64c8a104ee..46897ffaf4 100644 --- a/examples/multipass/app/routes/account_.register.tsx +++ b/examples/multipass/app/routes/account_.register.tsx @@ -1,5 +1,6 @@ import { data, + HeadersFunction, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, @@ -14,6 +15,8 @@ type ActionResponse = { | null; }; +export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders; + export async function loader({context}: LoaderFunctionArgs) { const customerAccessToken = await context.session.get('customerAccessToken'); if (customerAccessToken) { diff --git a/examples/multipass/app/routes/cart.tsx b/examples/multipass/app/routes/cart.tsx index 25fca265c7..d67f23ee88 100644 --- a/examples/multipass/app/routes/cart.tsx +++ b/examples/multipass/app/routes/cart.tsx @@ -8,7 +8,7 @@ export const meta: MetaFunction = () => { return [{title: `Hydrogen | Cart`}]; }; -export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; +export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders; export async function action({request, context}: ActionFunctionArgs) { /***********************************************/ diff --git a/templates/skeleton/app/routes/cart.tsx b/templates/skeleton/app/routes/cart.tsx index 6cf6b859c0..62dc760276 100644 --- a/templates/skeleton/app/routes/cart.tsx +++ b/templates/skeleton/app/routes/cart.tsx @@ -8,7 +8,7 @@ export const meta: MetaFunction = () => { return [{title: `Hydrogen | Cart`}]; }; -export const headers: HeadersFunction = ({ actionHeaders }) => actionHeaders; +export const headers: HeadersFunction = ({actionHeaders}) => actionHeaders; export async function action({request, context}: ActionFunctionArgs) { const {cart} = context; From 0a2e75dfafec6132361267db31f139a24a957b3e Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Thu, 6 Feb 2025 14:38:50 -0800 Subject: [PATCH 20/22] Fix error when navigating to checkout --- examples/multipass/README.md | 2 +- examples/multipass/app/routes/account_.login.multipass.tsx | 4 ++-- examples/multipass/env.d.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/multipass/README.md b/examples/multipass/README.md index 66378a3fcf..a07d39088d 100644 --- a/examples/multipass/README.md +++ b/examples/multipass/README.md @@ -57,7 +57,7 @@ npm i --save-dev @types/crypto-js - In your Hydrogen app, create the new files from the file list above, copying in the code as you go. - If you already have a `.env` file, copy over these key-value pairs: - `PRIVATE_SHOPIFY_STORE_MULTIPASS_SECRET` - - `PRIVATE_SHOPIFY_CHECKOUT_DOMAIN` + - `SHOPIFY_CHECKOUT_DOMAIN` ### 3. Edit the Cart component file diff --git a/examples/multipass/app/routes/account_.login.multipass.tsx b/examples/multipass/app/routes/account_.login.multipass.tsx index 25751e6b9b..b89d621b35 100644 --- a/examples/multipass/app/routes/account_.login.multipass.tsx +++ b/examples/multipass/app/routes/account_.login.multipass.tsx @@ -65,7 +65,7 @@ export async function action({request, context}: ActionFunctionArgs) { if (!customerAccessToken) { return handleLoggedOutResponse({ return_to: body?.return_to ?? null, - checkoutDomain: env.PRIVATE_SHOPIFY_CHECKOUT_DOMAIN, + checkoutDomain: env.SHOPIFY_CHECKOUT_DOMAIN, }); } @@ -127,7 +127,7 @@ export async function action({request, context}: ActionFunctionArgs) { } // success, return token, url - return remixData( + return Response.json( {data: {...data, error: null}}, { status: 200, diff --git a/examples/multipass/env.d.ts b/examples/multipass/env.d.ts index e485dd7f82..eb352d0efb 100644 --- a/examples/multipass/env.d.ts +++ b/examples/multipass/env.d.ts @@ -23,7 +23,7 @@ declare global { /***********************************************/ /********** EXAMPLE UPDATE STARTS ************/ PRIVATE_SHOPIFY_STORE_MULTIPASS_SECRET: string; - PRIVATE_SHOPIFY_CHECKOUT_DOMAIN: string; + SHOPIFY_CHECKOUT_DOMAIN: string; /********** EXAMPLE UPDATE END ************/ /***********************************************/ } From e9c7e5c46d0b7ac3dad987af4cf706557000b477 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Thu, 6 Feb 2025 15:04:02 -0800 Subject: [PATCH 21/22] update upgrade instructions --- .changeset/plenty-cycles-act.md | 48 ++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/.changeset/plenty-cycles-act.md b/.changeset/plenty-cycles-act.md index d8b4ea3434..38298a120f 100644 --- a/.changeset/plenty-cycles-act.md +++ b/.changeset/plenty-cycles-act.md @@ -2,8 +2,6 @@ 'skeleton': patch --- -[**Breaking change**] - Turn on Remix `v3_singleFetch` future flag Remix single fetch migration quick guide: https://remix.run/docs/en/main/start/future-flags#v3_singlefetch @@ -119,10 +117,12 @@ Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single - 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 + + /** + + * 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) { @@ -135,3 +135,39 @@ Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single }); } ``` + +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), + }, + ); + ``` From c48e524bd6973baec182d0edb0242a56242dc1e7 Mon Sep 17 00:00:00 2001 From: Helen Lin Date: Fri, 7 Feb 2025 11:48:11 -0800 Subject: [PATCH 22/22] add node import resolver --- package-lock.json | 35 +++++++++++++++++++++++------------ package.json | 1 + 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5aa5214e69..f4444fee41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "@typescript-eslint/parser": "^8.21.0", "cross-env": "^7.0.3", "eslint": "^9.18.0", + "eslint-import-resolver-node": "^0.3.9", "eslint-import-resolver-typescript": "^3.7.0", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.31.0", @@ -13451,6 +13452,28 @@ } } }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/eslint-import-resolver-typescript": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.7.0.tgz", @@ -13601,18 +13624,6 @@ "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, "node_modules/eslint-plugin-import/node_modules/json5": { "version": "1.0.2", "dev": true, diff --git a/package.json b/package.json index 9dfe5e9cc5..21760b97f5 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "@typescript-eslint/parser": "^8.21.0", "cross-env": "^7.0.3", "eslint": "^9.18.0", + "eslint-import-resolver-node": "^0.3.9", "eslint-import-resolver-typescript": "^3.7.0", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.31.0",