@shopify/create-hydrogen 5.0.23 → 5.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/assets/hydrogen/bundle/analyzer.html +155 -148
  2. package/dist/assets/hydrogen/starter/.cursor/rules/hydrogen-react-router.mdc +5 -3
  3. package/dist/assets/hydrogen/starter/CHANGELOG.md +97 -9
  4. package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +1 -1
  5. package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +1 -1
  6. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +1 -1
  7. package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +62 -29
  8. package/dist/assets/hydrogen/starter/app/components/Header.tsx +1 -1
  9. package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +1 -1
  10. package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +2 -2
  11. package/dist/assets/hydrogen/starter/app/components/SearchForm.tsx +1 -1
  12. package/dist/assets/hydrogen/starter/app/components/SearchFormPredictive.tsx +8 -3
  13. package/dist/assets/hydrogen/starter/app/components/SearchResults.tsx +3 -11
  14. package/dist/assets/hydrogen/starter/app/components/SearchResultsPredictive.tsx +2 -6
  15. package/dist/assets/hydrogen/starter/app/entry.client.tsx +10 -2
  16. package/dist/assets/hydrogen/starter/app/entry.server.tsx +5 -3
  17. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerAddressMutations.ts +7 -4
  18. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -1
  19. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrderQuery.ts +4 -1
  20. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +10 -5
  21. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +3 -2
  22. package/dist/assets/hydrogen/starter/app/lib/context.ts +34 -17
  23. package/dist/assets/hydrogen/starter/app/lib/fragments.ts +1 -0
  24. package/dist/assets/hydrogen/starter/app/lib/orderFilters.ts +90 -0
  25. package/dist/assets/hydrogen/starter/app/lib/redirect.ts +1 -1
  26. package/dist/assets/hydrogen/starter/app/lib/session.ts +1 -1
  27. package/dist/assets/hydrogen/starter/app/lib/variants.ts +1 -1
  28. package/dist/assets/hydrogen/starter/app/root.tsx +23 -18
  29. package/dist/assets/hydrogen/starter/app/routes/$.tsx +2 -2
  30. package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +2 -2
  31. package/dist/assets/hydrogen/starter/app/routes/[sitemap.xml].tsx +2 -3
  32. package/dist/assets/hydrogen/starter/app/routes/_index.tsx +12 -8
  33. package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +4 -3
  34. package/dist/assets/hydrogen/starter/app/routes/account._index.tsx +1 -1
  35. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +15 -11
  36. package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +47 -22
  37. package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +152 -23
  38. package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +11 -8
  39. package/dist/assets/hydrogen/starter/app/routes/account.tsx +16 -4
  40. package/dist/assets/hydrogen/starter/app/routes/account_.authorize.tsx +2 -2
  41. package/dist/assets/hydrogen/starter/app/routes/account_.login.tsx +5 -3
  42. package/dist/assets/hydrogen/starter/app/routes/account_.logout.tsx +3 -2
  43. package/dist/assets/hydrogen/starter/app/routes/api.$version.[graphql.json].tsx +2 -2
  44. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +6 -10
  45. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +10 -7
  46. package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +13 -7
  47. package/dist/assets/hydrogen/starter/app/routes/cart.$lines.tsx +3 -2
  48. package/dist/assets/hydrogen/starter/app/routes/cart.tsx +13 -9
  49. package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +8 -11
  50. package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +6 -6
  51. package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +10 -7
  52. package/dist/assets/hydrogen/starter/app/routes/discount.$code.tsx +3 -2
  53. package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +8 -6
  54. package/dist/assets/hydrogen/starter/app/routes/policies.$handle.tsx +7 -4
  55. package/dist/assets/hydrogen/starter/app/routes/policies._index.tsx +19 -13
  56. package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +9 -6
  57. package/dist/assets/hydrogen/starter/app/routes/search.tsx +14 -14
  58. package/dist/assets/hydrogen/starter/app/routes/sitemap.$type.$page[.xml].tsx +2 -3
  59. package/dist/assets/hydrogen/starter/app/routes.ts +1 -1
  60. package/dist/assets/hydrogen/starter/app/styles/app.css +53 -1
  61. package/dist/assets/hydrogen/starter/customer-accountapi.generated.d.ts +47 -13
  62. package/dist/assets/hydrogen/starter/env.d.ts +1 -39
  63. package/dist/assets/hydrogen/starter/eslint.config.js +35 -52
  64. package/dist/assets/hydrogen/starter/package.json +14 -15
  65. package/dist/assets/hydrogen/starter/react-router.config.ts +9 -3
  66. package/dist/assets/hydrogen/starter/server.ts +7 -7
  67. package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +1 -1
  68. package/dist/assets/hydrogen/starter/tsconfig.json +17 -13
  69. package/dist/assets/hydrogen/starter/vite.config.ts +4 -1
  70. package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +13 -20
  71. package/dist/assets/hydrogen/virtual-routes/routes/[.]well-known.appspecific.com[.]chrome[.]devtools[.]json.jsx +37 -0
  72. package/dist/chunk-2LZQLWDR.js +1189 -0
  73. package/dist/{chunk-EO6F7WJJ.js → chunk-6YUUFKYO.js} +1 -1
  74. package/dist/chunk-AUULK6IN.js +5 -0
  75. package/dist/chunk-CJKPLQJ7.js +51 -0
  76. package/dist/{chunk-MNT4XW23.js → chunk-LBUW5UHX.js} +1 -1
  77. package/dist/chunk-RUCJI22O.js +3 -0
  78. package/dist/{chunk-PMDMUCNY.js → chunk-VXJIQGAB.js} +1 -1
  79. package/dist/chunk-Y5VZE2FH.js +32 -0
  80. package/dist/chunk-ZLNTSIDN.js +2 -0
  81. package/dist/create-app.js +293 -288
  82. package/dist/{del-72VO4HYK.js → del-VDYQZFAQ.js} +1 -1
  83. package/dist/devtools-3BYEW2L2.js +8 -0
  84. package/dist/error-handler-XRI3ZDLO.js +2 -0
  85. package/dist/is-wsl-52AELLDM.js +2 -0
  86. package/dist/{morph-3JSBLNUD.js → morph-S2LU6PQ4.js} +7 -7
  87. package/dist/{multipart-parser-QIHQVIZA.js → multipart-parser-MX4R5XJM.js} +1 -1
  88. package/dist/open-PMJ32HTM.js +2 -0
  89. package/dist/out-U7AI7XUQ.js +2 -0
  90. package/package.json +4 -2
  91. package/dist/chokidar-FXMI63T6.js +0 -12
  92. package/dist/chunk-3LZ6M5C2.js +0 -3
  93. package/dist/chunk-D7CI46F7.js +0 -10
  94. package/dist/chunk-FB327AH7.js +0 -5
  95. package/dist/chunk-MZPD7BFF.js +0 -23
  96. package/dist/chunk-NIHY2BIB.js +0 -1180
  97. package/dist/chunk-UASQ33JG.js +0 -45
  98. package/dist/chunk-VMIOG46Y.js +0 -2
  99. package/dist/devtools-DGRGSZU7.js +0 -8
  100. package/dist/error-handler-O653XSNU.js +0 -2
  101. package/dist/is-wsl-LL6KGQIK.js +0 -2
  102. package/dist/open-OD6DRFEG.js +0 -2
  103. package/dist/out-DXB3K325.js +0 -2
@@ -1,7 +1,7 @@
1
1
  ---
2
- description: This Hydrogen project is based on react-router, NOT @remix-run/react!
3
- globs: *
4
- alwaysApply: false
2
+ description:
3
+ globs:
4
+ alwaysApply: true
5
5
  ---
6
6
 
7
7
  # React Router Import Rule for Hydrogen
@@ -29,6 +29,8 @@ When you see imports from Remix packages, replace them with their equivalent Rea
29
29
  | `@remix-run/server-runtime` | `react-router` |
30
30
  | `@remix-run/testing` | `react-router` |
31
31
 
32
+ NEVER USE 'react-router-dom' imports!
33
+
32
34
  ## Common Import Examples
33
35
 
34
36
  ```js
@@ -1,5 +1,102 @@
1
1
  # skeleton
2
2
 
3
+ ## 2025.7.0
4
+
5
+ ### Major Changes
6
+
7
+ - Update Storefront API and Customer Account API to version 2025-07 ([#3082](https://github.com/Shopify/hydrogen/pull/3082)) by [@juanpprieto](https://github.com/juanpprieto)
8
+
9
+ This update includes:
10
+ - Updated API version constants to 2025-07
11
+ - Regenerated GraphQL types for both Storefront and Customer Account APIs
12
+ - Updated all hardcoded API version references in documentation and tests
13
+ - Regenerated skeleton template types
14
+ - Updated skeleton's @shopify/cli dependency to ~3.83.3
15
+
16
+ Breaking changes may occur due to API schema changes between versions.
17
+
18
+ ### Patch Changes
19
+
20
+ - Fix defer/streaming in development & preview ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
21
+
22
+ - Upgrade Miniflare from v2 to v4 in mini-oxygen package. ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
23
+ - Internal MiniOxygen API has been refactored to work with Miniflare v4's new architecture.
24
+ - Simplified MiniOxygen class - no longer extends MiniflareCore.
25
+ - Updated global fetch handling to use Miniflare v4's `outboundService` API.
26
+ - Fixed test infrastructure to use project-relative temporary directories.
27
+ - Added support for Oxygen compatibility parameters (`compatibilityDate`, `compatibilityFlags`).
28
+ - Removed dependency on multiple `@miniflare/*` packages in favor of the consolidated `miniflare` package.
29
+
30
+ - Update and pin react-router to 7.9.2 for 2025.7.0 ([#3138](https://github.com/Shopify/hydrogen/pull/3138)) by [@juanpprieto](https://github.com/juanpprieto)
31
+
32
+ - Add TypeScript ESLint rules for promise handling to prevent Cloudflare Workers errors ([#3146](https://github.com/Shopify/hydrogen/pull/3146)) by [@kdaviduik](https://github.com/kdaviduik)
33
+
34
+ Added `@typescript-eslint/no-floating-promises` and `@typescript-eslint/no-misused-promises` rules to help prevent "The script will never generate a response" errors when deploying to Oxygen/Cloudflare Workers. These rules ensure promises are properly handled with await, return, or void operators, as recommended by [Cloudflare's error documentation](https://developers.cloudflare.com/workers/observability/errors/#the-script-will-never-generate-a-response-errors).
35
+
36
+ - Fixed React Context error that occurred during client-side hydration when using Content Security Policy (CSP) with nonces. The error "Cannot read properties of null (reading 'useContext')" was caused by the `NonceProvider` being present during server-side rendering but missing during client hydration. ([#3082](https://github.com/Shopify/hydrogen/pull/3082)) by [@juanpprieto](https://github.com/juanpprieto)
37
+
38
+ #### Changes for Existing Projects
39
+
40
+ If you have customized your `app/entry.client.tsx` file, you may need to wrap your app with the `NonceProvider` during hydration to avoid this error:
41
+
42
+ ```diff
43
+ // app/entry.client.tsx
44
+ import {HydratedRouter} from 'react-router/dom';
45
+ import {startTransition, StrictMode} from 'react';
46
+ import {hydrateRoot} from 'react-dom/client';
47
+ + import {NonceProvider} from '@shopify/hydrogen';
48
+
49
+ if (!window.location.origin.includes('webcache.googleusercontent.com')) {
50
+ startTransition(() => {
51
+ + // Extract nonce from existing script tags
52
+ + const existingNonce = document
53
+ + .querySelector<HTMLScriptElement>('script[nonce]')
54
+ + ?.nonce;
55
+ +
56
+ hydrateRoot(
57
+ document,
58
+ <StrictMode>
59
+ - <HydratedRouter />
60
+ + <NonceProvider value={existingNonce}>
61
+ + <HydratedRouter />
62
+ + </NonceProvider>
63
+ </StrictMode>,
64
+ );
65
+ });
66
+ }
67
+ ```
68
+
69
+ This ensures the React Context tree matches between server and client rendering, preventing hydration mismatches.
70
+
71
+ #### Package Changes
72
+ - **@shopify/hydrogen**: Exported `NonceProvider` from the main package to allow client-side usage and simplified Vite configuration to improve React Context stability during development
73
+ - **skeleton**: Updated the template's `entry.client.tsx` to include the `NonceProvider` wrapper during hydration
74
+
75
+ - Add `fulfillmentStatus` to CAAPI order query and route ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
76
+
77
+ - Add GraphQL @defer directive support to storefront client ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
78
+
79
+ - Unpin react-router and react-router-dom versions in the skeleton template ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
80
+
81
+ - Add `@inContext` language support to Customer Account API mutations ([#3039](https://github.com/Shopify/hydrogen/pull/3039)) by [@kdaviduik](https://github.com/kdaviduik)
82
+
83
+ - Add order filtering support to the skeleton /account/orders route for Customer Account API flow ([#3125](https://github.com/Shopify/hydrogen/pull/3125)) by [@juanpprieto](https://github.com/juanpprieto)
84
+
85
+ - Updated dependencies [[`6d067665562223ce2865f1c14be54b0b50258bd4`](https://github.com/Shopify/hydrogen/commit/6d067665562223ce2865f1c14be54b0b50258bd4), [`d57782a1ae3fa0017836d6010fb6ac5ab5d25965`](https://github.com/Shopify/hydrogen/commit/d57782a1ae3fa0017836d6010fb6ac5ab5d25965), [`48cbd450699a29a5667bee7174f3856430508ecc`](https://github.com/Shopify/hydrogen/commit/48cbd450699a29a5667bee7174f3856430508ecc), [`6d067665562223ce2865f1c14be54b0b50258bd4`](https://github.com/Shopify/hydrogen/commit/6d067665562223ce2865f1c14be54b0b50258bd4), [`0b4f01c9aa0e09332140a6a4e3114949873fb0f9`](https://github.com/Shopify/hydrogen/commit/0b4f01c9aa0e09332140a6a4e3114949873fb0f9), [`0d165ff280692411712176427bcd7e0df43b56fe`](https://github.com/Shopify/hydrogen/commit/0d165ff280692411712176427bcd7e0df43b56fe), [`ae7bedc89c1968b4a035f421b5ee6908f6376b1b`](https://github.com/Shopify/hydrogen/commit/ae7bedc89c1968b4a035f421b5ee6908f6376b1b), [`ae7bedc89c1968b4a035f421b5ee6908f6376b1b`](https://github.com/Shopify/hydrogen/commit/ae7bedc89c1968b4a035f421b5ee6908f6376b1b), [`75623a5bfdd8d6f0eab0d3547860341c20d9076c`](https://github.com/Shopify/hydrogen/commit/75623a5bfdd8d6f0eab0d3547860341c20d9076c), [`6681f92e84d42b5a6aca153fb49e31dcd8af84f6`](https://github.com/Shopify/hydrogen/commit/6681f92e84d42b5a6aca153fb49e31dcd8af84f6), [`4daf37ea291334b23bd543fdad5673ab7c9a6133`](https://github.com/Shopify/hydrogen/commit/4daf37ea291334b23bd543fdad5673ab7c9a6133)]:
86
+ - @shopify/hydrogen@2026.0.0
87
+
88
+ ## 2025.5.2
89
+
90
+ ### Patch Changes
91
+
92
+ - Fixing the skeleton's Vite Config ([#2958](https://github.com/Shopify/hydrogen/pull/2958)) by [@balazsbajorics](https://github.com/balazsbajorics)
93
+
94
+ ## 2025.5.1
95
+
96
+ ### Patch Changes
97
+
98
+ - Bumping the cli to 3.80.4 ([#2956](https://github.com/Shopify/hydrogen/pull/2956)) by [@balazsbajorics](https://github.com/balazsbajorics)
99
+
3
100
  ## 2025.5.0
4
101
 
5
102
  ### Patch Changes
@@ -28,7 +125,6 @@
28
125
  ### Patch Changes
29
126
 
30
127
  - Fix an issue with our starter template where duplicate content can exist on URLs that use internationalized handles. For example, if you have a product handle in english of `the-havoc` and translate it to `das-chaos` in German, duplicate content exists at both: ([#2821](https://github.com/Shopify/hydrogen/pull/2821)) by [@blittle](https://github.com/blittle)
31
-
32
128
  1. https://hydrogen.shop/de-de/products/das-chaos
33
129
  2. https://hydrogen.shop/de-de/products/the-havoc
34
130
 
@@ -48,7 +144,6 @@
48
144
  ### Patch Changes
49
145
 
50
146
  - Moved the `Layout` component back into `root.tsx` to avoid issues with styled errors. ([#2829](https://github.com/Shopify/hydrogen/pull/2829)) by [@ruggishop](https://github.com/ruggishop)
51
-
52
147
  1. If you have a separate `app/layout.tsx` file, delete it and move its default exported component into your `root.tsx`. For example:
53
148
 
54
149
  ```ts
@@ -102,7 +197,6 @@
102
197
  - Support for the Remix future flag `v3_routeConfig`. ([#2722](https://github.com/Shopify/hydrogen/pull/2722)) by [@seanparsons](https://github.com/seanparsons)
103
198
 
104
199
  Please refer to the Remix documentation for more details on `v3_routeConfig` future flag: [https://remix.run/docs/en/main/start/future-flags#v3_routeconfig](https://remix.run/docs/en/main/start/future-flags#v3_routeconfig)
105
-
106
200
  1. Update your `vite.config.ts`.
107
201
 
108
202
  ```diff
@@ -197,7 +291,6 @@
197
291
  Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single-fetch
198
292
 
199
293
  **Note:** If you have any routes that appends (or looks for) a search param named `_data`, make sure to rename it to something else.
200
-
201
294
  1. In your `vite.config.ts`, add the single fetch future flag.
202
295
 
203
296
  ```diff
@@ -495,7 +588,6 @@
495
588
  - Remove initial redirect from product display page ([#2643](https://github.com/Shopify/hydrogen/pull/2643)) by [@scottdixon](https://github.com/scottdixon)
496
589
 
497
590
  - Optional updates for the product route and product form to handle combined listing and 2000 variant limit. ([#2659](https://github.com/Shopify/hydrogen/pull/2659)) by [@wizardlyhel](https://github.com/wizardlyhel)
498
-
499
591
  1. Update your SFAPI product query to bring in the new query fields:
500
592
 
501
593
  ```diff
@@ -1307,7 +1399,6 @@
1307
1399
  ### Patch Changes
1308
1400
 
1309
1401
  - Stabilize `getSitemap`, `getSitemapIndex` and implement on skeleton ([#2589](https://github.com/Shopify/hydrogen/pull/2589)) by [@juanpprieto](https://github.com/juanpprieto)
1310
-
1311
1402
  1. Update the `getSitemapIndex` at `/app/routes/[sitemap.xml].tsx`
1312
1403
 
1313
1404
  ```diff
@@ -1417,7 +1508,6 @@
1417
1508
  - [**Breaking change**] ([#2585](https://github.com/Shopify/hydrogen/pull/2585)) by [@wizardlyhel](https://github.com/wizardlyhel)
1418
1509
 
1419
1510
  Deprecate usages of `product.options.values` and use `product.options.optionValues` instead.
1420
-
1421
1511
  1. Update your product graphql query to use the new `optionValues` field.
1422
1512
 
1423
1513
  ```diff
@@ -2098,7 +2188,6 @@
2098
2188
  ### Patch Changes
2099
2189
 
2100
2190
  - Improve performance of predictive search: ([#1823](https://github.com/Shopify/hydrogen/pull/1823)) by [@frandiox](https://github.com/frandiox)
2101
-
2102
2191
  - Change the request to be GET instead of POST to avoid Remix route revalidations.
2103
2192
  - Add Cache-Control headers to the response to get quicker results when typing.
2104
2193
 
@@ -2318,7 +2407,6 @@
2318
2407
  ### Major Changes
2319
2408
 
2320
2409
  - The Storefront API 2023-10 now returns menu item URLs that include the `primaryDomainUrl`, instead of defaulting to the Shopify store ID URL (example.myshopify.com). The skeleton template requires changes to check for the `primaryDomainUrl`: by [@blittle](https://github.com/blittle)
2321
-
2322
2410
  1. Update the `HeaderMenu` component to accept a `primaryDomainUrl` and include
2323
2411
  it in the internal url check
2324
2412
 
@@ -1,4 +1,4 @@
1
- import { type FetcherWithComponents } from 'react-router';
1
+ import {type FetcherWithComponents} from 'react-router';
2
2
  import {CartForm, type OptimisticCartLineInput} from '@shopify/hydrogen';
3
3
 
4
4
  export function AddToCartButton({
@@ -2,7 +2,7 @@ import type {CartLineUpdateInput} from '@shopify/hydrogen/storefront-api-types';
2
2
  import type {CartLayout} from '~/components/CartMain';
3
3
  import {CartForm, Image, type OptimisticCartLine} from '@shopify/hydrogen';
4
4
  import {useVariantUrl} from '~/lib/variants';
5
- import { Link } from 'react-router';
5
+ import {Link} from 'react-router';
6
6
  import {ProductPrice} from './ProductPrice';
7
7
  import {useAside} from './Aside';
8
8
  import type {CartApiQueryFragment} from 'storefrontapi.generated';
@@ -1,5 +1,5 @@
1
1
  import {useOptimisticCart} from '@shopify/hydrogen';
2
- import { Link } from 'react-router';
2
+ import {Link} from 'react-router';
3
3
  import type {CartApiQueryFragment} from 'storefrontapi.generated';
4
4
  import {useAside} from '~/components/Aside';
5
5
  import {CartLineItem} from '~/components/CartLineItem';
@@ -1,8 +1,9 @@
1
1
  import type {CartApiQueryFragment} from 'storefrontapi.generated';
2
2
  import type {CartLayout} from '~/components/CartMain';
3
3
  import {CartForm, Money, type OptimisticCart} from '@shopify/hydrogen';
4
- import {useRef} from 'react';
5
- import { FetcherWithComponents } from 'react-router';
4
+ import {useEffect, useRef} from 'react';
5
+ import {useFetcher} from 'react-router';
6
+ import type {FetcherWithComponents} from 'react-router';
6
7
 
7
8
  type CartSummaryProps = {
8
9
  cart: OptimisticCart<CartApiQueryFragment | null>;
@@ -19,19 +20,20 @@ export function CartSummary({cart, layout}: CartSummaryProps) {
19
20
  <dl className="cart-subtotal">
20
21
  <dt>Subtotal</dt>
21
22
  <dd>
22
- {cart.cost?.subtotalAmount?.amount ? (
23
- <Money data={cart.cost?.subtotalAmount} />
23
+ {cart?.cost?.subtotalAmount?.amount ? (
24
+ <Money data={cart?.cost?.subtotalAmount} />
24
25
  ) : (
25
26
  '-'
26
27
  )}
27
28
  </dd>
28
29
  </dl>
29
- <CartDiscounts discountCodes={cart.discountCodes} />
30
- <CartGiftCard giftCardCodes={cart.appliedGiftCards} />
31
- <CartCheckoutActions checkoutUrl={cart.checkoutUrl} />
30
+ <CartDiscounts discountCodes={cart?.discountCodes} />
31
+ <CartGiftCard giftCardCodes={cart?.appliedGiftCards} />
32
+ <CartCheckoutActions checkoutUrl={cart?.checkoutUrl} />
32
33
  </div>
33
34
  );
34
35
  }
36
+
35
37
  function CartCheckoutActions({checkoutUrl}: {checkoutUrl?: string}) {
36
38
  if (!checkoutUrl) return null;
37
39
 
@@ -110,41 +112,47 @@ function CartGiftCard({
110
112
  }) {
111
113
  const appliedGiftCardCodes = useRef<string[]>([]);
112
114
  const giftCardCodeInput = useRef<HTMLInputElement>(null);
113
- const codes: string[] =
114
- giftCardCodes?.map(({lastCharacters}) => `***${lastCharacters}`) || [];
115
+ const giftCardAddFetcher = useFetcher({key: 'gift-card-add'});
116
+
117
+ // Clear the gift card code input after the gift card is added
118
+ useEffect(() => {
119
+ if (giftCardAddFetcher.data) {
120
+ giftCardCodeInput.current!.value = '';
121
+ }
122
+ }, [giftCardAddFetcher.data]);
115
123
 
116
124
  function saveAppliedCode(code: string) {
117
125
  const formattedCode = code.replace(/\s/g, ''); // Remove spaces
118
126
  if (!appliedGiftCardCodes.current.includes(formattedCode)) {
119
127
  appliedGiftCardCodes.current.push(formattedCode);
120
128
  }
121
- giftCardCodeInput.current!.value = '';
122
- }
123
-
124
- function removeAppliedCode() {
125
- appliedGiftCardCodes.current = [];
126
129
  }
127
130
 
128
131
  return (
129
132
  <div>
130
- {/* Have existing gift card applied, display it with a remove option */}
131
- <dl hidden={!codes.length}>
132
- <div>
133
+ {/* Display applied gift cards with individual remove buttons */}
134
+ {giftCardCodes && giftCardCodes.length > 0 && (
135
+ <dl>
133
136
  <dt>Applied Gift Card(s)</dt>
134
- <UpdateGiftCardForm>
135
- <div className="cart-discount">
136
- <code>{codes?.join(', ')}</code>
137
- &nbsp;
138
- <button onSubmit={() => removeAppliedCode}>Remove</button>
139
- </div>
140
- </UpdateGiftCardForm>
141
- </div>
142
- </dl>
137
+ {giftCardCodes.map((giftCard) => (
138
+ <RemoveGiftCardForm key={giftCard.id} giftCardId={giftCard.id}>
139
+ <div className="cart-discount">
140
+ <code>***{giftCard.lastCharacters}</code>
141
+ &nbsp;
142
+ <Money data={giftCard.amountUsed} />
143
+ &nbsp;
144
+ <button type="submit">Remove</button>
145
+ </div>
146
+ </RemoveGiftCardForm>
147
+ ))}
148
+ </dl>
149
+ )}
143
150
 
144
- {/* Show an input to apply a discount */}
151
+ {/* Show an input to apply a gift card */}
145
152
  <UpdateGiftCardForm
146
153
  giftCardCodes={appliedGiftCardCodes.current}
147
154
  saveAppliedCode={saveAppliedCode}
155
+ fetcherKey="gift-card-add"
148
156
  >
149
157
  <div>
150
158
  <input
@@ -154,7 +162,9 @@ function CartGiftCard({
154
162
  ref={giftCardCodeInput}
155
163
  />
156
164
  &nbsp;
157
- <button type="submit">Apply</button>
165
+ <button type="submit" disabled={giftCardAddFetcher.state !== 'idle'}>
166
+ Apply
167
+ </button>
158
168
  </div>
159
169
  </UpdateGiftCardForm>
160
170
  </div>
@@ -164,15 +174,17 @@ function CartGiftCard({
164
174
  function UpdateGiftCardForm({
165
175
  giftCardCodes,
166
176
  saveAppliedCode,
177
+ fetcherKey,
167
178
  children,
168
179
  }: {
169
180
  giftCardCodes?: string[];
170
181
  saveAppliedCode?: (code: string) => void;
171
- removeAppliedCode?: () => void;
182
+ fetcherKey?: string;
172
183
  children: React.ReactNode;
173
184
  }) {
174
185
  return (
175
186
  <CartForm
187
+ fetcherKey={fetcherKey}
176
188
  route="/cart"
177
189
  action={CartForm.ACTIONS.GiftCardCodesUpdate}
178
190
  inputs={{
@@ -189,3 +201,24 @@ function UpdateGiftCardForm({
189
201
  </CartForm>
190
202
  );
191
203
  }
204
+
205
+ function RemoveGiftCardForm({
206
+ giftCardId,
207
+ children,
208
+ }: {
209
+ giftCardId: string;
210
+ children: React.ReactNode;
211
+ }) {
212
+ return (
213
+ <CartForm
214
+ route="/cart"
215
+ action={CartForm.ACTIONS.GiftCardCodesRemove}
216
+ inputs={{
217
+ giftCardCodes: [giftCardId],
218
+ }}
219
+ >
220
+ {children}
221
+ </CartForm>
222
+ );
223
+ }
224
+
@@ -1,5 +1,5 @@
1
1
  import {Suspense} from 'react';
2
- import { Await, NavLink, useAsyncValue } from 'react-router';
2
+ import {Await, NavLink, useAsyncValue} from 'react-router';
3
3
  import {
4
4
  type CartViewPayload,
5
5
  useAnalytics,
@@ -1,4 +1,4 @@
1
- import { Await, Link } from 'react-router';
1
+ import {Await, Link} from 'react-router';
2
2
  import {Suspense, useId} from 'react';
3
3
  import type {
4
4
  CartApiQueryFragment,
@@ -1,4 +1,4 @@
1
- import { Link, useNavigate } from 'react-router';
1
+ import {Link, useNavigate} from 'react-router';
2
2
  import {type MappedProductOptions} from '@shopify/hydrogen';
3
3
  import type {
4
4
  Maybe,
@@ -84,7 +84,7 @@ export function ProductForm({
84
84
  disabled={!exists}
85
85
  onClick={() => {
86
86
  if (!selected) {
87
- navigate(`?${variantUriQuery}`, {
87
+ void navigate(`?${variantUriQuery}`, {
88
88
  replace: true,
89
89
  preventScrollReset: true,
90
90
  });
@@ -1,5 +1,5 @@
1
1
  import {useRef, useEffect} from 'react';
2
- import { Form, type FormProps } from 'react-router';
2
+ import {Form, type FormProps} from 'react-router';
3
3
 
4
4
  type SearchFormProps = Omit<FormProps, 'children'> & {
5
5
  children: (args: {
@@ -1,4 +1,9 @@
1
- import { useFetcher, useNavigate, type FormProps, type Fetcher } from 'react-router';
1
+ import {
2
+ useFetcher,
3
+ useNavigate,
4
+ type FormProps,
5
+ type Fetcher,
6
+ } from 'react-router';
2
7
  import React, {useRef, useEffect} from 'react';
3
8
  import type {PredictiveSearchReturn} from '~/lib/search';
4
9
  import {useAside} from './Aside';
@@ -41,13 +46,13 @@ export function SearchFormPredictive({
41
46
  /** Navigate to the search page with the current input value */
42
47
  function goToSearch() {
43
48
  const term = inputRef?.current?.value;
44
- navigate(SEARCH_ENDPOINT + (term ? `?q=${term}` : ''));
49
+ void navigate(SEARCH_ENDPOINT + (term ? `?q=${term}` : ''));
45
50
  aside.close();
46
51
  }
47
52
 
48
53
  /** Fetch search results based on the input value */
49
54
  function fetchResults(event: React.ChangeEvent<HTMLInputElement>) {
50
- fetcher.submit(
55
+ void fetcher.submit(
51
56
  {q: event.target.value || '', limit: 5, predictive: true},
52
57
  {method: 'GET', action: SEARCH_ENDPOINT},
53
58
  );
@@ -1,4 +1,4 @@
1
- import { Link } from 'react-router';
1
+ import {Link} from 'react-router';
2
2
  import {Image, Money, Pagination} from '@shopify/hydrogen';
3
3
  import {urlWithTrackingParams, type RegularSearchReturn} from '~/lib/search';
4
4
 
@@ -120,19 +120,11 @@ function SearchResultsProducts({
120
120
  <div className="search-results-item" key={product.id}>
121
121
  <Link prefetch="intent" to={productUrl}>
122
122
  {image && (
123
- <Image
124
- data={image}
125
- alt={product.title}
126
- width={50}
127
- />
123
+ <Image data={image} alt={product.title} width={50} />
128
124
  )}
129
125
  <div>
130
126
  <p>{product.title}</p>
131
- <small>
132
- {price &&
133
- <Money data={price} />
134
- }
135
- </small>
127
+ <small>{price && <Money data={price} />}</small>
136
128
  </div>
137
129
  </Link>
138
130
  </div>
@@ -1,4 +1,4 @@
1
- import { Link, useFetcher, type Fetcher } from 'react-router';
1
+ import {Link, useFetcher, type Fetcher} from 'react-router';
2
2
  import {Image, Money} from '@shopify/hydrogen';
3
3
  import React, {useRef, useEffect} from 'react';
4
4
  import {
@@ -228,11 +228,7 @@ function SearchResultsPredictiveProducts({
228
228
  )}
229
229
  <div>
230
230
  <p>{product.title}</p>
231
- <small>
232
- {price && (
233
- <Money data={price} />
234
- )}
235
- </small>
231
+ <small>{price && <Money data={price} />}</small>
236
232
  </div>
237
233
  </Link>
238
234
  </li>
@@ -1,13 +1,21 @@
1
- import { HydratedRouter } from 'react-router/dom';
1
+ import {HydratedRouter} from 'react-router/dom';
2
2
  import {startTransition, StrictMode} from 'react';
3
3
  import {hydrateRoot} from 'react-dom/client';
4
+ import {NonceProvider} from '@shopify/hydrogen';
4
5
 
5
6
  if (!window.location.origin.includes('webcache.googleusercontent.com')) {
6
7
  startTransition(() => {
8
+ // Extract nonce from existing script tags
9
+ const existingNonce = document
10
+ .querySelector<HTMLScriptElement>('script[nonce]')
11
+ ?.nonce;
12
+
7
13
  hydrateRoot(
8
14
  document,
9
15
  <StrictMode>
10
- <HydratedRouter />
16
+ <NonceProvider value={existingNonce}>
17
+ <HydratedRouter />
18
+ </NonceProvider>
11
19
  </StrictMode>,
12
20
  );
13
21
  });
@@ -1,8 +1,10 @@
1
- import type {AppLoadContext} from '@shopify/remix-oxygen';
2
1
  import {ServerRouter} from 'react-router';
3
2
  import {isbot} from 'isbot';
4
3
  import {renderToReadableStream} from 'react-dom/server';
5
- import {createContentSecurityPolicy} from '@shopify/hydrogen';
4
+ import {
5
+ createContentSecurityPolicy,
6
+ type HydrogenRouterContextProvider,
7
+ } from '@shopify/hydrogen';
6
8
  import type {EntryContext} from 'react-router';
7
9
 
8
10
  export default async function handleRequest(
@@ -10,7 +12,7 @@ export default async function handleRequest(
10
12
  responseStatusCode: number,
11
13
  responseHeaders: Headers,
12
14
  reactRouterContext: EntryContext,
13
- context: AppLoadContext,
15
+ context: HydrogenRouterContextProvider,
14
16
  ) {
15
17
  const {nonce, header, NonceProvider} = createContentSecurityPolicy({
16
18
  shop: {
@@ -4,7 +4,8 @@ export const UPDATE_ADDRESS_MUTATION = `#graphql
4
4
  $address: CustomerAddressInput!
5
5
  $addressId: ID!
6
6
  $defaultAddress: Boolean
7
- ) {
7
+ $language: LanguageCode
8
+ ) @inContext(language: $language) {
8
9
  customerAddressUpdate(
9
10
  address: $address
10
11
  addressId: $addressId
@@ -25,8 +26,9 @@ export const UPDATE_ADDRESS_MUTATION = `#graphql
25
26
  // NOTE: https://shopify.dev/docs/api/customer/latest/mutations/customerAddressDelete
26
27
  export const DELETE_ADDRESS_MUTATION = `#graphql
27
28
  mutation customerAddressDelete(
28
- $addressId: ID!,
29
- ) {
29
+ $addressId: ID!
30
+ $language: LanguageCode
31
+ ) @inContext(language: $language) {
30
32
  customerAddressDelete(addressId: $addressId) {
31
33
  deletedAddressId
32
34
  userErrors {
@@ -43,7 +45,8 @@ export const CREATE_ADDRESS_MUTATION = `#graphql
43
45
  mutation customerAddressCreate(
44
46
  $address: CustomerAddressInput!
45
47
  $defaultAddress: Boolean
46
- ) {
48
+ $language: LanguageCode
49
+ ) @inContext(language: $language) {
47
50
  customerAddressCreate(
48
51
  address: $address
49
52
  defaultAddress: $defaultAddress
@@ -31,7 +31,7 @@ export const CUSTOMER_FRAGMENT = `#graphql
31
31
 
32
32
  // NOTE: https://shopify.dev/docs/api/customer/latest/queries/customer
33
33
  export const CUSTOMER_DETAILS_QUERY = `#graphql
34
- query CustomerDetails {
34
+ query CustomerDetails($language: LanguageCode) @inContext(language: $language) {
35
35
  customer {
36
36
  ...Customer
37
37
  }
@@ -45,7 +45,9 @@ export const CUSTOMER_ORDER_QUERY = `#graphql
45
45
  fragment Order on Order {
46
46
  id
47
47
  name
48
+ confirmationNumber
48
49
  statusPageUrl
50
+ fulfillmentStatus
49
51
  processedAt
50
52
  fulfillments(first: 1) {
51
53
  nodes {
@@ -77,7 +79,8 @@ export const CUSTOMER_ORDER_QUERY = `#graphql
77
79
  }
78
80
  }
79
81
  }
80
- query Order($orderId: ID!) {
82
+ query Order($orderId: ID!, $language: LanguageCode)
83
+ @inContext(language: $language) {
81
84
  order(id: $orderId) {
82
85
  ... on Order {
83
86
  ...Order
@@ -1,4 +1,4 @@
1
- // https://shopify.dev/docs/api/customer/latest/objects/Order
1
+ // NOTE: https://shopify.dev/docs/api/customer/latest/objects/Order
2
2
  export const ORDER_ITEM_FRAGMENT = `#graphql
3
3
  fragment OrderItem on Order {
4
4
  totalPrice {
@@ -6,6 +6,7 @@ export const ORDER_ITEM_FRAGMENT = `#graphql
6
6
  currencyCode
7
7
  }
8
8
  financialStatus
9
+ fulfillmentStatus
9
10
  fulfillments(first: 1) {
10
11
  nodes {
11
12
  status
@@ -13,11 +14,12 @@ export const ORDER_ITEM_FRAGMENT = `#graphql
13
14
  }
14
15
  id
15
16
  number
17
+ confirmationNumber
16
18
  processedAt
17
19
  }
18
20
  ` as const;
19
21
 
20
- // https://shopify.dev/docs/api/customer/latest/objects/Customer
22
+ // NOTE: https://shopify.dev/docs/api/customer/latest/objects/Customer
21
23
  export const CUSTOMER_ORDERS_FRAGMENT = `#graphql
22
24
  fragment CustomerOrders on Customer {
23
25
  orders(
@@ -26,7 +28,8 @@ export const CUSTOMER_ORDERS_FRAGMENT = `#graphql
26
28
  first: $first,
27
29
  last: $last,
28
30
  before: $startCursor,
29
- after: $endCursor
31
+ after: $endCursor,
32
+ query: $query
30
33
  ) {
31
34
  nodes {
32
35
  ...OrderItem
@@ -42,7 +45,7 @@ export const CUSTOMER_ORDERS_FRAGMENT = `#graphql
42
45
  ${ORDER_ITEM_FRAGMENT}
43
46
  ` as const;
44
47
 
45
- // https://shopify.dev/docs/api/customer/latest/queries/customer
48
+ // NOTE: https://shopify.dev/docs/api/customer/latest/queries/customer
46
49
  export const CUSTOMER_ORDERS_QUERY = `#graphql
47
50
  ${CUSTOMER_ORDERS_FRAGMENT}
48
51
  query CustomerOrders(
@@ -50,7 +53,9 @@ export const CUSTOMER_ORDERS_QUERY = `#graphql
50
53
  $first: Int
51
54
  $last: Int
52
55
  $startCursor: String
53
- ) {
56
+ $query: String
57
+ $language: LanguageCode
58
+ ) @inContext(language: $language) {
54
59
  customer {
55
60
  ...CustomerOrders
56
61
  }