@shopify/cli 3.85.3 → 3.85.4

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 (155) hide show
  1. package/dist/assets/hydrogen/bundle/analyzer.html +155 -148
  2. package/dist/assets/hydrogen/starter/CHANGELOG.md +125 -49
  3. package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +1 -1
  4. package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +1 -1
  5. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +1 -1
  6. package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +62 -29
  7. package/dist/assets/hydrogen/starter/app/components/Header.tsx +1 -1
  8. package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +1 -1
  9. package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +2 -2
  10. package/dist/assets/hydrogen/starter/app/components/SearchForm.tsx +1 -1
  11. package/dist/assets/hydrogen/starter/app/components/SearchFormPredictive.tsx +8 -3
  12. package/dist/assets/hydrogen/starter/app/components/SearchResults.tsx +3 -11
  13. package/dist/assets/hydrogen/starter/app/components/SearchResultsPredictive.tsx +2 -6
  14. package/dist/assets/hydrogen/starter/app/entry.client.tsx +10 -2
  15. package/dist/assets/hydrogen/starter/app/entry.server.tsx +5 -3
  16. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerAddressMutations.ts +7 -4
  17. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -1
  18. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrderQuery.ts +4 -1
  19. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +10 -5
  20. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +3 -2
  21. package/dist/assets/hydrogen/starter/app/lib/context.ts +34 -17
  22. package/dist/assets/hydrogen/starter/app/lib/fragments.ts +1 -0
  23. package/dist/assets/hydrogen/starter/app/lib/orderFilters.ts +90 -0
  24. package/dist/assets/hydrogen/starter/app/lib/redirect.ts +1 -1
  25. package/dist/assets/hydrogen/starter/app/lib/session.ts +1 -1
  26. package/dist/assets/hydrogen/starter/app/lib/variants.ts +1 -1
  27. package/dist/assets/hydrogen/starter/app/root.tsx +23 -18
  28. package/dist/assets/hydrogen/starter/app/routes/$.tsx +2 -2
  29. package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +2 -2
  30. package/dist/assets/hydrogen/starter/app/routes/[sitemap.xml].tsx +2 -3
  31. package/dist/assets/hydrogen/starter/app/routes/_index.tsx +12 -8
  32. package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +4 -3
  33. package/dist/assets/hydrogen/starter/app/routes/account._index.tsx +1 -1
  34. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +15 -11
  35. package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +47 -22
  36. package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +152 -23
  37. package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +11 -8
  38. package/dist/assets/hydrogen/starter/app/routes/account.tsx +16 -4
  39. package/dist/assets/hydrogen/starter/app/routes/account_.authorize.tsx +2 -2
  40. package/dist/assets/hydrogen/starter/app/routes/account_.login.tsx +5 -3
  41. package/dist/assets/hydrogen/starter/app/routes/account_.logout.tsx +3 -2
  42. package/dist/assets/hydrogen/starter/app/routes/api.$version.[graphql.json].tsx +2 -2
  43. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +6 -10
  44. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +10 -7
  45. package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +13 -7
  46. package/dist/assets/hydrogen/starter/app/routes/cart.$lines.tsx +3 -2
  47. package/dist/assets/hydrogen/starter/app/routes/cart.tsx +13 -9
  48. package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +8 -11
  49. package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +6 -6
  50. package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +10 -7
  51. package/dist/assets/hydrogen/starter/app/routes/discount.$code.tsx +3 -2
  52. package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +8 -6
  53. package/dist/assets/hydrogen/starter/app/routes/policies.$handle.tsx +7 -4
  54. package/dist/assets/hydrogen/starter/app/routes/policies._index.tsx +19 -13
  55. package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +9 -6
  56. package/dist/assets/hydrogen/starter/app/routes/search.tsx +14 -14
  57. package/dist/assets/hydrogen/starter/app/routes/sitemap.$type.$page[.xml].tsx +2 -3
  58. package/dist/assets/hydrogen/starter/app/routes.ts +1 -1
  59. package/dist/assets/hydrogen/starter/app/styles/app.css +53 -1
  60. package/dist/assets/hydrogen/starter/customer-accountapi.generated.d.ts +47 -13
  61. package/dist/assets/hydrogen/starter/env.d.ts +1 -39
  62. package/dist/assets/hydrogen/starter/eslint.config.js +35 -52
  63. package/dist/assets/hydrogen/starter/package.json +14 -15
  64. package/dist/assets/hydrogen/starter/react-router.config.ts +9 -3
  65. package/dist/assets/hydrogen/starter/server.ts +7 -7
  66. package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +1 -1
  67. package/dist/assets/hydrogen/starter/tsconfig.json +17 -13
  68. package/dist/assets/hydrogen/starter/vite.config.ts +3 -0
  69. package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +13 -20
  70. package/dist/assets/hydrogen/virtual-routes/routes/[.]well-known.appspecific.com[.]chrome[.]devtools[.]json.jsx +37 -0
  71. package/dist/{chokidar-TTCYG5AA.js → chokidar-HTKREZL3.js} +482 -7
  72. package/dist/{chunk-I4ATBX6D.js → chunk-2C6DJIBX.js} +4 -4
  73. package/dist/{chunk-JAUHWNMJ.js → chunk-2H6AD4EL.js} +5 -5
  74. package/dist/{chunk-HTTZVPR6.js → chunk-2XTIBWBA.js} +3 -3
  75. package/dist/{chunk-HS2O5IHL.js → chunk-34TWZ632.js} +3 -4
  76. package/dist/{chunk-Y4H4HMEZ.js → chunk-3762O47V.js} +2 -2
  77. package/dist/{chunk-C45MKMJT.js → chunk-3JZIRVBB.js} +8 -11
  78. package/dist/{chunk-OQKAZQIC.js → chunk-5AUZG65U.js} +5 -5
  79. package/dist/{chunk-OURS5IQY.js → chunk-5JFU2PDH.js} +3 -3
  80. package/dist/{chunk-AMWSD3HH.js → chunk-BRYTLGQH.js} +3 -3
  81. package/dist/{chunk-QP2MOS2Y.js → chunk-CBUCMJTB.js} +2 -2
  82. package/dist/{chunk-IHXRXBUN.js → chunk-DBQFPT3U.js} +6 -6
  83. package/dist/{chunk-QHKSKL4E.js → chunk-DEMUK3QR.js} +4 -4
  84. package/dist/{chunk-EDXQ22O4.js → chunk-EPNYJUKL.js} +6 -6
  85. package/dist/{chunk-5RNGA7FX.js → chunk-GMOJ4MM6.js} +5 -5
  86. package/dist/{chunk-ZHKIKKU7.js → chunk-JDGRN7YI.js} +4 -4
  87. package/dist/{chunk-HBANZKAF.js → chunk-JXSJVKPK.js} +3 -3
  88. package/dist/{chunk-LJXXOFEJ.js → chunk-M6DL6PW7.js} +2 -2
  89. package/dist/{chunk-D5DJSKHK.js → chunk-NIJYJVQ7.js} +112 -113
  90. package/dist/{chunk-NLFRHIZY.js → chunk-ON22YJEB.js} +6 -6
  91. package/dist/{chunk-F7TU455C.js → chunk-OYGH5GC4.js} +2 -2
  92. package/dist/{chunk-VR6Z7LKU.js → chunk-P24ZSULK.js} +3 -3
  93. package/dist/{chunk-6RJ7HBOQ.js → chunk-QCCEM2W7.js} +3 -3
  94. package/dist/{chunk-CAONVM2S.js → chunk-QCVEBMJO.js} +3 -3
  95. package/dist/{chunk-XJBIASMX.js → chunk-QDQ64PMY.js} +3 -3
  96. package/dist/{chunk-3XWYM7QS.js → chunk-S5H4ZYTZ.js} +3 -3
  97. package/dist/{chunk-V5ONTA7N.js → chunk-SJ6XHMPR.js} +2 -2
  98. package/dist/{chunk-UW4ASA6Y.js → chunk-UTGZE4KZ.js} +4 -4
  99. package/dist/{chunk-FGEKMLLA.js → chunk-VOXGVQ4Y.js} +6 -6
  100. package/dist/{chunk-RPU6TIF2.js → chunk-VVHZTHIS.js} +7 -7
  101. package/dist/{chunk-GRVHLYQS.js → chunk-W7V5E5TL.js} +4 -4
  102. package/dist/{chunk-6A6GDV25.js → chunk-XEG67XNW.js} +4 -4
  103. package/dist/{chunk-EWEA4LRT.js → chunk-XQMRWA2D.js} +2 -2
  104. package/dist/{chunk-L54PNQGV.js → chunk-XXIM3XSO.js} +2 -2
  105. package/dist/{chunk-VVUZFYUK.js → chunk-Y2BC44O5.js} +4 -4
  106. package/dist/{chunk-EG6MBBEN.js → chunk-Y2JP6WFP.js} +2 -2
  107. package/dist/cli/commands/auth/login.js +11 -11
  108. package/dist/cli/commands/auth/login.test.js +12 -12
  109. package/dist/cli/commands/auth/logout.js +11 -11
  110. package/dist/cli/commands/auth/logout.test.js +12 -12
  111. package/dist/cli/commands/cache/clear.js +11 -11
  112. package/dist/cli/commands/debug/command-flags.js +11 -11
  113. package/dist/cli/commands/docs/generate.js +11 -11
  114. package/dist/cli/commands/docs/generate.test.js +11 -11
  115. package/dist/cli/commands/help.js +11 -11
  116. package/dist/cli/commands/kitchen-sink/async.js +12 -12
  117. package/dist/cli/commands/kitchen-sink/async.test.js +12 -12
  118. package/dist/cli/commands/kitchen-sink/index.js +14 -14
  119. package/dist/cli/commands/kitchen-sink/index.test.js +14 -14
  120. package/dist/cli/commands/kitchen-sink/prompts.js +12 -12
  121. package/dist/cli/commands/kitchen-sink/prompts.test.js +12 -12
  122. package/dist/cli/commands/kitchen-sink/static.js +12 -12
  123. package/dist/cli/commands/kitchen-sink/static.test.js +12 -12
  124. package/dist/cli/commands/notifications/generate.js +12 -12
  125. package/dist/cli/commands/notifications/list.js +12 -12
  126. package/dist/cli/commands/search.js +12 -12
  127. package/dist/cli/commands/upgrade.js +12 -12
  128. package/dist/cli/commands/version.js +12 -12
  129. package/dist/cli/commands/version.test.js +12 -12
  130. package/dist/cli/services/commands/notifications.js +7 -7
  131. package/dist/cli/services/commands/search.js +3 -3
  132. package/dist/cli/services/commands/search.test.js +3 -3
  133. package/dist/cli/services/commands/version.js +4 -4
  134. package/dist/cli/services/commands/version.test.js +5 -5
  135. package/dist/cli/services/kitchen-sink/async.js +3 -3
  136. package/dist/cli/services/kitchen-sink/prompts.js +3 -3
  137. package/dist/cli/services/kitchen-sink/static.js +3 -3
  138. package/dist/cli/services/upgrade.js +4 -4
  139. package/dist/cli/services/upgrade.test.js +5 -5
  140. package/dist/{custom-oclif-loader-YDKLB47A.js → custom-oclif-loader-OFIEXBNK.js} +3 -3
  141. package/dist/{error-handler-BYZU2C5C.js → error-handler-FHZMF7GS.js} +9 -9
  142. package/dist/hooks/postrun.js +7 -7
  143. package/dist/hooks/prerun.js +8 -8
  144. package/dist/index.js +1798 -3481
  145. package/dist/{local-XLJD5WYP.js → local-4MIY74VR.js} +3 -3
  146. package/dist/{morph-5D7H6MU2.js → morph-D4BXY376.js} +9 -9
  147. package/dist/{node-package-manager-I7AWSWJ4.js → node-package-manager-H6KF4PH6.js} +4 -4
  148. package/dist/{path-GB4VIEM6.js → path-COZT77T2.js} +2 -2
  149. package/dist/tsconfig.tsbuildinfo +1 -1
  150. package/dist/{ui-2AJAFJYY.js → ui-R3FDFL2U.js} +3 -3
  151. package/dist/{workerd-4DJKRJUB.js → workerd-JVZ57YOV.js} +13 -13
  152. package/oclif.manifest.json +11 -56
  153. package/package.json +8 -8
  154. package/dist/chokidar-XUA2BN3J.js +0 -1120
  155. package/dist/chunk-F7J5CUMZ.js +0 -497
@@ -1,5 +1,90 @@
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
+
3
88
  ## 2025.5.2
4
89
 
5
90
  ### Patch Changes
@@ -40,7 +125,6 @@
40
125
  ### Patch Changes
41
126
 
42
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)
43
-
44
128
  1. https://hydrogen.shop/de-de/products/das-chaos
45
129
  2. https://hydrogen.shop/de-de/products/the-havoc
46
130
 
@@ -60,7 +144,6 @@
60
144
  ### Patch Changes
61
145
 
62
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)
63
-
64
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:
65
148
 
66
149
  ```ts
@@ -114,7 +197,6 @@
114
197
  - Support for the Remix future flag `v3_routeConfig`. ([#2722](https://github.com/Shopify/hydrogen/pull/2722)) by [@seanparsons](https://github.com/seanparsons)
115
198
 
116
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)
117
-
118
200
  1. Update your `vite.config.ts`.
119
201
 
120
202
  ```diff
@@ -172,13 +254,13 @@
172
254
  1. Add a routes.ts file. This is your new Remix route configuration file.
173
255
 
174
256
  ```ts
175
- import { flatRoutes } from "@remix-run/fs-routes";
176
- import { layout, type RouteConfig } from "@remix-run/route-config";
177
- import { hydrogenRoutes } from "@shopify/hydrogen";
257
+ import {flatRoutes} from '@remix-run/fs-routes';
258
+ import {layout, type RouteConfig} from '@remix-run/route-config';
259
+ import {hydrogenRoutes} from '@shopify/hydrogen';
178
260
 
179
261
  export default hydrogenRoutes([
180
262
  // Your entire app reading from routes folder using Layout from layout.tsx
181
- layout("./layout.tsx", await flatRoutes()),
263
+ layout('./layout.tsx', await flatRoutes()),
182
264
  ]) satisfies RouteConfig;
183
265
  ```
184
266
 
@@ -209,7 +291,6 @@
209
291
  Remix single fetch migration guide: https://remix.run/docs/en/main/guides/single-fetch
210
292
 
211
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.
212
-
213
294
  1. In your `vite.config.ts`, add the single fetch future flag.
214
295
 
215
296
  ```diff
@@ -507,7 +588,6 @@
507
588
  - Remove initial redirect from product display page ([#2643](https://github.com/Shopify/hydrogen/pull/2643)) by [@scottdixon](https://github.com/scottdixon)
508
589
 
509
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)
510
-
511
591
  1. Update your SFAPI product query to bring in the new query fields:
512
592
 
513
593
  ```diff
@@ -769,25 +849,25 @@
769
849
  8. Update the `ProductForm` component.
770
850
 
771
851
  ```tsx
772
- import { Link, useNavigate } from "@remix-run/react";
773
- import { type MappedProductOptions } from "@shopify/hydrogen";
852
+ import {Link, useNavigate} from '@remix-run/react';
853
+ import {type MappedProductOptions} from '@shopify/hydrogen';
774
854
  import type {
775
855
  Maybe,
776
856
  ProductOptionValueSwatch,
777
- } from "@shopify/hydrogen/storefront-api-types";
778
- import { AddToCartButton } from "./AddToCartButton";
779
- import { useAside } from "./Aside";
780
- import type { ProductFragment } from "storefrontapi.generated";
857
+ } from '@shopify/hydrogen/storefront-api-types';
858
+ import {AddToCartButton} from './AddToCartButton';
859
+ import {useAside} from './Aside';
860
+ import type {ProductFragment} from 'storefrontapi.generated';
781
861
 
782
862
  export function ProductForm({
783
863
  productOptions,
784
864
  selectedVariant,
785
865
  }: {
786
866
  productOptions: MappedProductOptions[];
787
- selectedVariant: ProductFragment["selectedOrFirstAvailableVariant"];
867
+ selectedVariant: ProductFragment['selectedOrFirstAvailableVariant'];
788
868
  }) {
789
869
  const navigate = useNavigate();
790
- const { open } = useAside();
870
+ const {open} = useAside();
791
871
  return (
792
872
  <div className="product-form">
793
873
  {productOptions.map((option) => (
@@ -821,8 +901,8 @@
821
901
  to={`/products/${handle}?${variantUriQuery}`}
822
902
  style={{
823
903
  border: selected
824
- ? "1px solid black"
825
- : "1px solid transparent",
904
+ ? '1px solid black'
905
+ : '1px solid transparent',
826
906
  opacity: available ? 1 : 0.3,
827
907
  }}
828
908
  >
@@ -839,13 +919,13 @@
839
919
  <button
840
920
  type="button"
841
921
  className={`product-options-item${
842
- exists && !selected ? " link" : ""
922
+ exists && !selected ? ' link' : ''
843
923
  }`}
844
924
  key={option.name + name}
845
925
  style={{
846
926
  border: selected
847
- ? "1px solid black"
848
- : "1px solid transparent",
927
+ ? '1px solid black'
928
+ : '1px solid transparent',
849
929
  opacity: available ? 1 : 0.3,
850
930
  }}
851
931
  disabled={!exists}
@@ -869,7 +949,7 @@
869
949
  <AddToCartButton
870
950
  disabled={!selectedVariant || !selectedVariant.availableForSale}
871
951
  onClick={() => {
872
- open("cart");
952
+ open('cart');
873
953
  }}
874
954
  lines={
875
955
  selectedVariant
@@ -883,7 +963,7 @@
883
963
  : []
884
964
  }
885
965
  >
886
- {selectedVariant?.availableForSale ? "Add to cart" : "Sold out"}
966
+ {selectedVariant?.availableForSale ? 'Add to cart' : 'Sold out'}
887
967
  </AddToCartButton>
888
968
  </div>
889
969
  );
@@ -906,7 +986,7 @@
906
986
  aria-label={name}
907
987
  className="product-option-label-swatch"
908
988
  style={{
909
- backgroundColor: color || "transparent",
989
+ backgroundColor: color || 'transparent',
910
990
  }}
911
991
  >
912
992
  {!!image && <img src={image} alt={name} />}
@@ -1319,7 +1399,6 @@
1319
1399
  ### Patch Changes
1320
1400
 
1321
1401
  - Stabilize `getSitemap`, `getSitemapIndex` and implement on skeleton ([#2589](https://github.com/Shopify/hydrogen/pull/2589)) by [@juanpprieto](https://github.com/juanpprieto)
1322
-
1323
1402
  1. Update the `getSitemapIndex` at `/app/routes/[sitemap.xml].tsx`
1324
1403
 
1325
1404
  ```diff
@@ -1407,21 +1486,21 @@
1407
1486
  New `withCache.fetch` is for caching simple fetch requests. This method caches the responses if they are OK responses, and you can pass `shouldCacheResponse`, `cacheKey`, etc. to modify behavior. `data` is the consumed body of the response (we need to consume to cache it).
1408
1487
 
1409
1488
  ```ts
1410
- const withCache = createWithCache({ cache, waitUntil, request });
1489
+ const withCache = createWithCache({cache, waitUntil, request});
1411
1490
 
1412
- const { data, response } = await withCache.fetch<{ data: T; error: string }>(
1413
- "my-cms.com/api",
1491
+ const {data, response} = await withCache.fetch<{data: T; error: string}>(
1492
+ 'my-cms.com/api',
1414
1493
  {
1415
- method: "POST",
1416
- headers: { "Content-type": "application/json" },
1494
+ method: 'POST',
1495
+ headers: {'Content-type': 'application/json'},
1417
1496
  body,
1418
1497
  },
1419
1498
  {
1420
1499
  cacheStrategy: CacheLong(),
1421
1500
  // Cache if there are no data errors or a specific data that make this result not suited for caching
1422
1501
  shouldCacheResponse: (result) => !result?.error,
1423
- cacheKey: ["my-cms", body],
1424
- displayName: "My CMS query",
1502
+ cacheKey: ['my-cms', body],
1503
+ displayName: 'My CMS query',
1425
1504
  },
1426
1505
  );
1427
1506
  ```
@@ -1429,7 +1508,6 @@
1429
1508
  - [**Breaking change**] ([#2585](https://github.com/Shopify/hydrogen/pull/2585)) by [@wizardlyhel](https://github.com/wizardlyhel)
1430
1509
 
1431
1510
  Deprecate usages of `product.options.values` and use `product.options.optionValues` instead.
1432
-
1433
1511
  1. Update your product graphql query to use the new `optionValues` field.
1434
1512
 
1435
1513
  ```diff
@@ -1997,9 +2075,9 @@
1997
2075
 
1998
2076
  ```tsx
1999
2077
  // app/lib/root-data.ts
2000
- import { useMatches } from "@remix-run/react";
2001
- import type { SerializeFrom } from "@shopify/remix-oxygen";
2002
- import type { loader } from "~/root";
2078
+ import {useMatches} from '@remix-run/react';
2079
+ import type {SerializeFrom} from '@shopify/remix-oxygen';
2080
+ import type {loader} from '~/root';
2003
2081
 
2004
2082
  /**
2005
2083
  * Access the result of the root loader from a React component.
@@ -2110,7 +2188,6 @@
2110
2188
  ### Patch Changes
2111
2189
 
2112
2190
  - Improve performance of predictive search: ([#1823](https://github.com/Shopify/hydrogen/pull/1823)) by [@frandiox](https://github.com/frandiox)
2113
-
2114
2191
  - Change the request to be GET instead of POST to avoid Remix route revalidations.
2115
2192
  - Add Cache-Control headers to the response to get quicker results when typing.
2116
2193
 
@@ -2161,10 +2238,10 @@
2161
2238
  - This is an important fix to a bug with 404 routes and path-based i18n projects where some unknown routes would not properly render a 404. This fixes all new projects, but to fix existing projects, add a `($locale).tsx` route with the following contents: ([#1732](https://github.com/Shopify/hydrogen/pull/1732)) by [@blittle](https://github.com/blittle)
2162
2239
 
2163
2240
  ```ts
2164
- import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
2241
+ import {type LoaderFunctionArgs} from '@remix-run/server-runtime';
2165
2242
 
2166
- export async function loader({ params, context }: LoaderFunctionArgs) {
2167
- const { language, country } = context.storefront.i18n;
2243
+ export async function loader({params, context}: LoaderFunctionArgs) {
2244
+ const {language, country} = context.storefront.i18n;
2168
2245
 
2169
2246
  if (
2170
2247
  params.locale &&
@@ -2172,7 +2249,7 @@
2172
2249
  ) {
2173
2250
  // If the locale URL param is defined, yet we still are still at the default locale
2174
2251
  // then the the locale param must be invalid, send to the 404 page
2175
- throw new Response(null, { status: 404 });
2252
+ throw new Response(null, {status: 404});
2176
2253
  }
2177
2254
 
2178
2255
  return null;
@@ -2228,11 +2305,11 @@
2228
2305
  ```yaml
2229
2306
  projects:
2230
2307
  default:
2231
- schema: "node_modules/@shopify/hydrogen/storefront.schema.json"
2308
+ schema: 'node_modules/@shopify/hydrogen/storefront.schema.json'
2232
2309
  documents:
2233
- - "!*.d.ts"
2234
- - "*.{ts,tsx,js,jsx}"
2235
- - "app/**/*.{ts,tsx,js,jsx}"
2310
+ - '!*.d.ts'
2311
+ - '*.{ts,tsx,js,jsx}'
2312
+ - 'app/**/*.{ts,tsx,js,jsx}'
2236
2313
  ```
2237
2314
 
2238
2315
  - Improve resiliency of `HydrogenSession` ([#1583](https://github.com/Shopify/hydrogen/pull/1583)) by [@blittle](https://github.com/blittle)
@@ -2330,7 +2407,6 @@
2330
2407
  ### Major Changes
2331
2408
 
2332
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)
2333
-
2334
2410
  1. Update the `HeaderMenu` component to accept a `primaryDomainUrl` and include
2335
2411
  it in the internal url check
2336
2412
 
@@ -2447,8 +2523,8 @@
2447
2523
  ```ts
2448
2524
  // root.tsx
2449
2525
 
2450
- import { useMatches } from "@remix-run/react";
2451
- import { type SerializeFrom } from "@shopify/remix-oxygen";
2526
+ import {useMatches} from '@remix-run/react';
2527
+ import {type SerializeFrom} from '@shopify/remix-oxygen';
2452
2528
 
2453
2529
  export const useRootLoaderData = () => {
2454
2530
  const [root] = useMatches();
@@ -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>