@shopify/cli 3.61.0 → 3.61.2

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 (128) hide show
  1. package/dist/assets/hydrogen/starter/.eslintrc.cjs +1 -0
  2. package/dist/assets/hydrogen/starter/CHANGELOG.md +65 -65
  3. package/dist/assets/hydrogen/starter/app/components/Footer.tsx +3 -3
  4. package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +1 -1
  5. package/dist/assets/hydrogen/starter/app/lib/session.ts +5 -0
  6. package/dist/assets/hydrogen/starter/app/root.tsx +84 -51
  7. package/dist/assets/hydrogen/starter/app/routes/_index.tsx +62 -25
  8. package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +1 -5
  9. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +12 -70
  10. package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +7 -14
  11. package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +1 -8
  12. package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +5 -22
  13. package/dist/assets/hydrogen/starter/app/routes/account.tsx +0 -1
  14. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +32 -6
  15. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +36 -10
  16. package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +35 -12
  17. package/dist/assets/hydrogen/starter/app/routes/cart.tsx +0 -2
  18. package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +40 -7
  19. package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +32 -6
  20. package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +31 -6
  21. package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +36 -8
  22. package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +50 -11
  23. package/dist/assets/hydrogen/starter/package.json +6 -7
  24. package/dist/assets/hydrogen/starter/server.ts +4 -0
  25. package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +1 -2
  26. package/dist/assets/hydrogen/virtual-routes/components/RequestTable.jsx +1 -2
  27. package/dist/assets/hydrogen/virtual-routes/routes/index.jsx +1 -2
  28. package/dist/{chunk-ZOTASNQ2.js → chunk-3RM5SOXY.js} +1 -1
  29. package/dist/{chunk-Q7PY7LVL.js → chunk-4757GPBG.js} +3 -3
  30. package/dist/{chunk-FFHJRSIP.js → chunk-4L37JOS5.js} +1 -1
  31. package/dist/{chunk-V67RXPI6.js → chunk-4TTAP7NN.js} +3 -3
  32. package/dist/{chunk-IIVL4DYB.js → chunk-5B2D56W6.js} +2 -2
  33. package/dist/{chunk-QX3YP6O5.js → chunk-5KRK4DLX.js} +2598 -795
  34. package/dist/{chunk-FWKRA64T.js → chunk-5MODHNUE.js} +3 -3
  35. package/dist/{chunk-LXXUWZRO.js → chunk-7IZTLXJ5.js} +2 -2
  36. package/dist/{chunk-AL4KPZUN.js → chunk-BUWM6JUU.js} +2 -2
  37. package/dist/{chunk-WNXHHHLZ.js → chunk-BYFLATOA.js} +3 -3
  38. package/dist/{chunk-4IXC46ZA.js → chunk-CK7MF3LY.js} +4 -4
  39. package/dist/{chunk-2AH5EBXL.js → chunk-CTETXMMM.js} +4 -4
  40. package/dist/{chunk-7YJH7P7B.js → chunk-CWYZA7C7.js} +1 -1
  41. package/dist/{chunk-463OPQAS.js → chunk-EHF6FVWH.js} +1 -1
  42. package/dist/{chunk-CMFKWJDJ.js → chunk-ETSILRIY.js} +3 -3
  43. package/dist/{chunk-EDVQGTOL.js → chunk-G4EQLT5B.js} +4 -4
  44. package/dist/{chunk-DWXXFI3U.js → chunk-JQPQJN44.js} +6 -6
  45. package/dist/{chunk-YZIPQNOG.js → chunk-KBXWK6XB.js} +2 -2
  46. package/dist/{chunk-EFL5CN6M.js → chunk-M73YMW2C.js} +4 -4
  47. package/dist/{chunk-HJ2JCW7E.js → chunk-NBIE3DNN.js} +2 -2
  48. package/dist/{chunk-2RKUO75O.js → chunk-NJP5GFNN.js} +129 -125
  49. package/dist/{chunk-TXDYKQZT.js → chunk-NJTSDTDU.js} +4 -4
  50. package/dist/{chunk-UJQBYPRO.js → chunk-OC4AY5G7.js} +1 -1
  51. package/dist/{chunk-75BJ33EP.js → chunk-Q3KADKQA.js} +2 -2
  52. package/dist/{chunk-LY6UIYOG.js → chunk-QDZF6IG4.js} +2 -2
  53. package/dist/{chunk-XDCNK2SU.js → chunk-QLLSCNEC.js} +2 -2
  54. package/dist/{chunk-O53XNGJU.js → chunk-QQP6JS5H.js} +1 -1
  55. package/dist/{chunk-2Q7SKRRM.js → chunk-QSO3GQEK.js} +162 -132
  56. package/dist/{chunk-5NBS5LAW.js → chunk-QTBH3GO6.js} +1 -1
  57. package/dist/{chunk-JZ5DN7RX.js → chunk-RPRKZ6JC.js} +2509 -728
  58. package/dist/{chunk-ONJCU4C5.js → chunk-TXXQWILO.js} +2 -2
  59. package/dist/{chunk-B4KDQK3X.js → chunk-UPM6XNBH.js} +2 -2
  60. package/dist/{chunk-3W2QB2OC.js → chunk-VC6CC75R.js} +2 -2
  61. package/dist/{chunk-XRFOYWYI.js → chunk-WZBNLCJ5.js} +2 -2
  62. package/dist/{chunk-63QEL3SZ.js → chunk-XMJDBJPP.js} +4 -4
  63. package/dist/{chunk-OOAZSPYS.js → chunk-YP5UVIUW.js} +3 -3
  64. package/dist/{chunk-LUGC3D2G.js → chunk-YWPV7MV2.js} +6 -6
  65. package/dist/{chunk-ABZKHSZ5.js → chunk-YZGLFVKK.js} +1 -1
  66. package/dist/{chunk-RW6YXHBL.js → chunk-ZE2ZPLYI.js} +12 -12
  67. package/dist/cli/commands/auth/logout.js +18 -18
  68. package/dist/cli/commands/auth/logout.test.js +19 -19
  69. package/dist/cli/commands/debug/command-flags.js +17 -17
  70. package/dist/cli/commands/demo/catalog.js +18 -18
  71. package/dist/cli/commands/demo/generate-file.js +18 -18
  72. package/dist/cli/commands/demo/index.js +17 -17
  73. package/dist/cli/commands/demo/print-ai-prompt.js +18 -18
  74. package/dist/cli/commands/docs/generate.js +17 -17
  75. package/dist/cli/commands/docs/generate.test.js +17 -17
  76. package/dist/cli/commands/help.js +17 -17
  77. package/dist/cli/commands/kitchen-sink/async.js +18 -18
  78. package/dist/cli/commands/kitchen-sink/async.test.js +18 -18
  79. package/dist/cli/commands/kitchen-sink/index.js +20 -20
  80. package/dist/cli/commands/kitchen-sink/index.test.js +20 -20
  81. package/dist/cli/commands/kitchen-sink/prompts.js +18 -18
  82. package/dist/cli/commands/kitchen-sink/prompts.test.js +18 -18
  83. package/dist/cli/commands/kitchen-sink/static.js +18 -18
  84. package/dist/cli/commands/kitchen-sink/static.test.js +18 -18
  85. package/dist/cli/commands/search.js +18 -18
  86. package/dist/cli/commands/upgrade.js +17 -17
  87. package/dist/cli/commands/version.js +18 -18
  88. package/dist/cli/commands/version.test.js +18 -18
  89. package/dist/cli/services/commands/search.js +8 -8
  90. package/dist/cli/services/commands/search.test.js +8 -8
  91. package/dist/cli/services/commands/version.js +11 -11
  92. package/dist/cli/services/commands/version.test.js +12 -12
  93. package/dist/cli/services/demo.js +8 -8
  94. package/dist/cli/services/demo.test.js +8 -8
  95. package/dist/cli/services/kitchen-sink/async.js +8 -8
  96. package/dist/cli/services/kitchen-sink/prompts.js +8 -8
  97. package/dist/cli/services/kitchen-sink/static.js +8 -8
  98. package/dist/cli/services/upgrade.js +10 -10
  99. package/dist/cli/services/upgrade.test.js +11 -11
  100. package/dist/{custom-oclif-loader-KLTAWG4B-VOMMA3PO.js → custom-oclif-loader-4SQGUQ7D-P5C4NLSX.js} +4 -4
  101. package/dist/{chunk-5R5656YY.js → custom-oclif-loader-OD47SFAU.js} +18 -3
  102. package/dist/{error-handler-UJK7WNJE.js → error-handler-EPMFPTYX.js} +10 -10
  103. package/dist/{error-handler-ND7WBMC3-CRFJKAS4.js → error-handler-WI4YVJIS-U3BMMYNJ.js} +6 -6
  104. package/dist/{error-handler-FUQWOW4J.js → error-handler-WL3Q25NI.js} +15 -15
  105. package/dist/hooks/postrun.js +13 -13
  106. package/dist/hooks/prerun.js +10 -10
  107. package/dist/index.js +1137 -1104
  108. package/dist/{lib-76RCE6WZ-AR3SQY6Y.js → lib-76RCE6WZ-5IYGTKKK.js} +2 -2
  109. package/dist/{lib-MWSNLG5P.js → lib-UYK3ZLZK.js} +3 -3
  110. package/dist/{local-5OND5PI5.js → local-KISH2IRF.js} +7 -7
  111. package/dist/{local-3LWDOA7J-UFWE2V6L.js → local-YKOG4EUR-XRRBWLSI.js} +4 -4
  112. package/dist/{morph-FQNWYET7.js → morph-DCIZUK2Q.js} +25 -8
  113. package/dist/{node-WNJUHXTR.js → node-3DKQAOFU.js} +14 -14
  114. package/dist/{node-package-manager-ISP2P5R3.js → node-package-manager-HYTHPJBI.js} +9 -9
  115. package/dist/{node-package-manager-TFY2ROCP-DDJAU44T.js → node-package-manager-S7TSAUJZ-KKDY6KSF.js} +5 -5
  116. package/dist/{system-G445DF53.js → system-LIP7AECY.js} +7 -7
  117. package/dist/tsconfig.tsbuildinfo +1 -1
  118. package/dist/{ui-ITQVVPJU.js → ui-5LSDG3C5.js} +7 -7
  119. package/dist/{ui-MUGCNPLG-UG4N3GMV.js → ui-FUZYK6S3-PVVDPVD5.js} +4 -4
  120. package/dist/{workerd-EZVOCUQW.js → workerd-TFH7T6NI.js} +13 -13
  121. package/oclif.manifest.json +1 -1
  122. package/package.json +4 -5
  123. package/dist/custom-oclif-loader-PJJ3H2WB.js +0 -28
  124. package/dist/hooks/init.d.ts +0 -12
  125. package/dist/hooks/init.js +0 -66
  126. package/dist/hooks/init.js.map +0 -1
  127. package/dist/{chunk-67MDUPX5.js → chunk-I434ZJQI.js} +7 -7
  128. package/dist/{chunk-74JT4RJX.js → chunk-VF4VWJWS.js} +3 -3
@@ -14,5 +14,6 @@ module.exports = {
14
14
  'no-useless-escape': 'off',
15
15
  '@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
16
16
  'no-case-declarations': 'off',
17
+ 'no-console': ['warn', {allow: ['warn', 'error']}],
17
18
  },
18
19
  };
@@ -1,91 +1,91 @@
1
1
  # skeleton
2
2
 
3
- ## 0.0.0-next-ca7f288-20240530103543
3
+ ## 0.0.0-next-9eb60d7-20240607102913
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - Update remix to v2.9.2 ([#2135](https://github.com/Shopify/hydrogen/pull/2135)) by [@michenly](https://github.com/michenly)
7
+ - Remove manual setting of session in headers and recommend setting it in server after response is created. ([#2137](https://github.com/Shopify/hydrogen/pull/2137)) by [@michenly](https://github.com/michenly)
8
+
9
+ Step 1: Add `isPending` implementation in session
8
10
 
9
- - Refactor root to use [Remix's Layout Export pattern](https://remix.run/docs/en/main/file-conventions/root#layout-export). ([#2152](https://github.com/Shopify/hydrogen/pull/2152)) by [@michenly](https://github.com/michenly)
11
+ ```diff
12
+ // in app/lib/session.ts
13
+ export class AppSession implements HydrogenSession {
14
+ + public isPending = false;
10
15
 
11
- This will also fix below error ahead of Single Fetch future flag.
16
+ get unset() {
17
+ + this.isPending = true;
18
+ return this.#session.unset;
19
+ }
12
20
 
13
- > You cannot `useLoaderData` in an errorElement
21
+ get set() {
22
+ + this.isPending = true;
23
+ return this.#session.set;
24
+ }
25
+
26
+ commit() {
27
+ + this.isPending = false;
28
+ return this.#sessionStorage.commitSession(this.#session);
29
+ }
30
+ }
31
+ ```
14
32
 
15
- The diff below showcase how you can make this refactor in your existing application.
33
+ Step 2: update response header if `session.isPending` is true
16
34
 
17
35
  ```diff
18
- import {
19
- Outlet,
20
- - useLoaderData,
21
- + useRouteLoaderData,
22
- } from '@remix-run/react';
23
- -import {Layout} from '~/components/Layout';
24
- +import {PageLayout} from '~/components/PageLayout';
25
-
26
- -export default function App() {
27
- +export function Layout({children}: {children?: React.ReactNode}) {
28
- const nonce = useNonce();
29
- - const data = useLoaderData<typeof loader>();
30
- + const data = useRouteLoaderData<typeof loader>('root');
36
+ // in server.ts
37
+ export default {
38
+ async fetch(request: Request): Promise<Response> {
39
+ try {
40
+ const response = await handleRequest(request);
41
+
42
+ + if (session.isPending) {
43
+ + response.headers.set('Set-Cookie', await session.commit());
44
+ + }
45
+
46
+ return response;
47
+ } catch (error) {
48
+ ...
49
+ }
50
+ },
51
+ };
52
+ ```
31
53
 
32
- return (
33
- <html>
34
- ...
35
- <body>
36
- - <Layout {...data}>
37
- - <Outlet />
38
- - </Layout>
39
- + {data? (
40
- + <PageLayout {...data}>
41
- + {children}
42
- + </PageLayout>
43
- + ) : (
44
- + children
45
- + )}
46
- </body>
47
- </html>
54
+ Step 3: remove setting cookie with session.commit() in routes
55
+
56
+ ```diff
57
+ // in route files
58
+ export async function loader({context}: LoaderFunctionArgs) {
59
+ return json({},
60
+ - {
61
+ - headers: {
62
+ - 'Set-Cookie': await context.session.commit(),
63
+ - },
64
+ },
48
65
  );
49
66
  }
67
+ ```
50
68
 
51
- +export default function App() {
52
- + return <Outlet />;
53
- +}
69
+ - Trigger changeset for all packages for next release
54
70
 
55
- export function ErrorBoundary() {
56
- const rootData = useLoaderData<typeof loader>();
71
+ - Updated dependencies [[`4337200c`](https://github.com/Shopify/hydrogen/commit/4337200c7908d56c039171c283a4d92c31a8b7b6), [`10a419bf`](https://github.com/Shopify/hydrogen/commit/10a419bf1db79cdfd8c41c0223ce695959f60da9), [`4337200c`](https://github.com/Shopify/hydrogen/commit/4337200c7908d56c039171c283a4d92c31a8b7b6), [`9eb60d73`](https://github.com/Shopify/hydrogen/commit/9eb60d73e552c3d22b9325ecbcd5878810893ad3)]:
72
+ - @shopify/hydrogen@0.0.0-next-9eb60d7-20240607102913
73
+ - @shopify/cli-hydrogen@0.0.0-next-9eb60d7-20240607102913
74
+ - @shopify/remix-oxygen@0.0.0-next-9eb60d7-20240607102913
57
75
 
58
- return (
59
- - <html>
60
- - ...
61
- - <body>
62
- - <Layout {...rootData}>
63
- - <div className="route-error">
64
- - <h1>Error</h1>
65
- - ...
66
- - </div>
67
- - </Layout>
68
- - </body>
69
- - </html>
70
- + <div className="route-error">
71
- + <h1>Error</h1>
72
- + ...
73
- + </div>
74
- );
75
- }
76
+ ## 2024.4.5
76
77
 
77
- ```
78
+ ### Patch Changes
78
79
 
79
- - `<Analytics>` and `useAnalytics` are now stable. ([#2141](https://github.com/Shopify/hydrogen/pull/2141)) by [@wizardlyhel](https://github.com/wizardlyhel)
80
+ - Update remix to v2.9.2 ([#2135](https://github.com/Shopify/hydrogen/pull/2135)) by [@michenly](https://github.com/michenly)
80
81
 
81
- - Trigger changeset for all packages for next release
82
+ - `<Analytics>` and `useAnalytics` are now stable. ([#2141](https://github.com/Shopify/hydrogen/pull/2141)) by [@wizardlyhel](https://github.com/wizardlyhel)
82
83
 
83
84
  - Update the skeleton template to use React state in the aside dialogs ([#2088](https://github.com/Shopify/hydrogen/pull/2088)) by [@blittle](https://github.com/blittle)
84
85
 
85
- - Updated dependencies [[`fe82119f`](https://github.com/Shopify/hydrogen/commit/fe82119f5e75df5a0f727bab6a2186e679abc73d), [`32d4c33e`](https://github.com/Shopify/hydrogen/commit/32d4c33e4421a9c56f62a8c392f5417edddd0402), [`8eea75ec`](https://github.com/Shopify/hydrogen/commit/8eea75ec5ead4de98d7d1b2baedce8511029bcae), [`27e51abf`](https://github.com/Shopify/hydrogen/commit/27e51abfc1f5444afa952c503886bfa12fc55c7e), [`f29c9085`](https://github.com/Shopify/hydrogen/commit/f29c9085eb1adbde9e01226484eba8a85b5074ed), [`7b838beb`](https://github.com/Shopify/hydrogen/commit/7b838beb7c43380ffc9c32c2bb9f34291912fa42), [`ca4cf045`](https://github.com/Shopify/hydrogen/commit/ca4cf045f7fb72ad98b62af7bd172ff8cf553de2), [`5a554b2e`](https://github.com/Shopify/hydrogen/commit/5a554b2e9d91894c2db8032f0c29666dce1ea3f2), [`27e51abf`](https://github.com/Shopify/hydrogen/commit/27e51abfc1f5444afa952c503886bfa12fc55c7e), [`e29ecf03`](https://github.com/Shopify/hydrogen/commit/e29ecf03e158242d09045917dbef556bd456889a), [`5d6465b3`](https://github.com/Shopify/hydrogen/commit/5d6465b32d90052e5580fcb81d98427bcb8ad528), [`608389d6`](https://github.com/Shopify/hydrogen/commit/608389d6d69c6a9801935d528507c165d1dd4ccf), [`9dfd1cfe`](https://github.com/Shopify/hydrogen/commit/9dfd1cfeb3e96c6d3426427a4b5d97ef475dab6d), [`7def3e9f`](https://github.com/Shopify/hydrogen/commit/7def3e9fa6e28f4fde7af43e2f346aa32267c38e), [`65239c76`](https://github.com/Shopify/hydrogen/commit/65239c76ca1d0b294b59b1ad53624485859c4da5), [`ca7f2888`](https://github.com/Shopify/hydrogen/commit/ca7f28887367a4882e57a67c4e248b0f0bba1c9b)]:
86
- - @shopify/hydrogen@0.0.0-next-ca7f288-20240530103543
87
- - @shopify/cli-hydrogen@0.0.0-next-ca7f288-20240530103543
88
- - @shopify/remix-oxygen@0.0.0-next-ca7f288-20240530103543
86
+ - Updated dependencies [[`fe82119f`](https://github.com/Shopify/hydrogen/commit/fe82119f5e75df5a0f727bab6a2186e679abc73d), [`32d4c33e`](https://github.com/Shopify/hydrogen/commit/32d4c33e4421a9c56f62a8c392f5417edddd0402), [`8eea75ec`](https://github.com/Shopify/hydrogen/commit/8eea75ec5ead4de98d7d1b2baedce8511029bcae), [`27e51abf`](https://github.com/Shopify/hydrogen/commit/27e51abfc1f5444afa952c503886bfa12fc55c7e), [`f29c9085`](https://github.com/Shopify/hydrogen/commit/f29c9085eb1adbde9e01226484eba8a85b5074ed), [`7b838beb`](https://github.com/Shopify/hydrogen/commit/7b838beb7c43380ffc9c32c2bb9f34291912fa42), [`d702aec2`](https://github.com/Shopify/hydrogen/commit/d702aec2214646a78cdafc2c25d510489db73f6d), [`ca4cf045`](https://github.com/Shopify/hydrogen/commit/ca4cf045f7fb72ad98b62af7bd172ff8cf553de2), [`5a554b2e`](https://github.com/Shopify/hydrogen/commit/5a554b2e9d91894c2db8032f0c29666dce1ea3f2), [`27e51abf`](https://github.com/Shopify/hydrogen/commit/27e51abfc1f5444afa952c503886bfa12fc55c7e), [`5d6465b3`](https://github.com/Shopify/hydrogen/commit/5d6465b32d90052e5580fcb81d98427bcb8ad528), [`608389d6`](https://github.com/Shopify/hydrogen/commit/608389d6d69c6a9801935d528507c165d1dd4ccf), [`9dfd1cfe`](https://github.com/Shopify/hydrogen/commit/9dfd1cfeb3e96c6d3426427a4b5d97ef475dab6d), [`7def3e9f`](https://github.com/Shopify/hydrogen/commit/7def3e9fa6e28f4fde7af43e2f346aa32267c38e), [`65239c76`](https://github.com/Shopify/hydrogen/commit/65239c76ca1d0b294b59b1ad53624485859c4da5), [`ca7f2888`](https://github.com/Shopify/hydrogen/commit/ca7f28887367a4882e57a67c4e248b0f0bba1c9b)]:
87
+ - @shopify/hydrogen@2024.4.3
88
+ - @shopify/cli-hydrogen@8.1.0
89
89
 
90
90
  ## 2024.4.4
91
91
 
@@ -3,7 +3,7 @@ import {Await, NavLink} from '@remix-run/react';
3
3
  import type {FooterQuery, HeaderQuery} from 'storefrontapi.generated';
4
4
 
5
5
  interface FooterProps {
6
- footer: Promise<FooterQuery>;
6
+ footer: Promise<FooterQuery | null>;
7
7
  header: HeaderQuery;
8
8
  publicStoreDomain: string;
9
9
  }
@@ -18,7 +18,7 @@ export function Footer({
18
18
  <Await resolve={footerPromise}>
19
19
  {(footer) => (
20
20
  <footer className="footer">
21
- {footer.menu && header.shop.primaryDomain?.url && (
21
+ {footer?.menu && header.shop.primaryDomain?.url && (
22
22
  <FooterMenu
23
23
  menu={footer.menu}
24
24
  primaryDomainUrl={header.shop.primaryDomain.url}
@@ -37,7 +37,7 @@ function FooterMenu({
37
37
  primaryDomainUrl,
38
38
  publicStoreDomain,
39
39
  }: {
40
- menu: Awaited<FooterProps['footer']>['menu'];
40
+ menu: FooterQuery['menu'];
41
41
  primaryDomainUrl: FooterProps['header']['shop']['primaryDomain']['url'];
42
42
  publicStoreDomain: string;
43
43
  }) {
@@ -16,7 +16,7 @@ import {
16
16
 
17
17
  interface PageLayoutProps {
18
18
  cart: Promise<CartApiQueryFragment | null>;
19
- footer: Promise<FooterQuery>;
19
+ footer: Promise<FooterQuery | null>;
20
20
  header: HeaderQuery;
21
21
  isLoggedIn: Promise<boolean>;
22
22
  publicStoreDomain: string;
@@ -11,6 +11,8 @@ import {
11
11
  * swap out the cookie-based implementation with something else!
12
12
  */
13
13
  export class AppSession implements HydrogenSession {
14
+ public isPending = false;
15
+
14
16
  #sessionStorage;
15
17
  #session;
16
18
 
@@ -50,10 +52,12 @@ export class AppSession implements HydrogenSession {
50
52
  }
51
53
 
52
54
  get unset() {
55
+ this.isPending = true;
53
56
  return this.#session.unset;
54
57
  }
55
58
 
56
59
  get set() {
60
+ this.isPending = true;
57
61
  return this.#session.set;
58
62
  }
59
63
 
@@ -62,6 +66,7 @@ export class AppSession implements HydrogenSession {
62
66
  }
63
67
 
64
68
  commit() {
69
+ this.isPending = false;
65
70
  return this.#sessionStorage.commitSession(this.#session);
66
71
  }
67
72
  }
@@ -56,54 +56,81 @@ export function links() {
56
56
  ];
57
57
  }
58
58
 
59
- export async function loader({context}: LoaderFunctionArgs) {
60
- const {storefront, customerAccount, cart, env} = context;
61
- const publicStoreDomain = env.PUBLIC_STORE_DOMAIN;
62
-
63
- const isLoggedInPromise = customerAccount.isLoggedIn();
64
- const cartPromise = cart.get();
65
-
66
- // defer the footer query (below the fold)
67
- const footerPromise = storefront.query(FOOTER_QUERY, {
68
- cache: storefront.CacheLong(),
69
- variables: {
70
- footerMenuHandle: 'footer', // Adjust to your footer menu handle
71
- },
72
- });
73
-
74
- // await the header query (above the fold)
75
- const headerPromise = storefront.query(HEADER_QUERY, {
76
- cache: storefront.CacheLong(),
77
- variables: {
78
- headerMenuHandle: 'main-menu', // Adjust to your header menu handle
59
+ export async function loader(args: LoaderFunctionArgs) {
60
+ // Start fetching non-critical data without blocking time to first byte
61
+ const deferredData = loadDeferredData(args);
62
+
63
+ // Await the critical data required to render initial state of the page
64
+ const criticalData = await loadCriticalData(args);
65
+
66
+ const {storefront, env} = args.context;
67
+
68
+ return defer({
69
+ ...deferredData,
70
+ ...criticalData,
71
+ publicStoreDomain: env.PUBLIC_STORE_DOMAIN,
72
+ shop: getShopAnalytics({
73
+ storefront,
74
+ publicStorefrontId: env.PUBLIC_STOREFRONT_ID,
75
+ }),
76
+ consent: {
77
+ checkoutDomain: env.PUBLIC_CHECKOUT_DOMAIN,
78
+ storefrontAccessToken: env.PUBLIC_STOREFRONT_API_TOKEN,
79
79
  },
80
80
  });
81
+ }
81
82
 
82
- return defer(
83
- {
84
- cart: cartPromise,
85
- footer: footerPromise,
86
- header: await headerPromise,
87
- isLoggedIn: isLoggedInPromise,
88
- publicStoreDomain,
89
- shop: getShopAnalytics({
90
- storefront,
91
- publicStorefrontId: env.PUBLIC_STOREFRONT_ID,
92
- }),
93
- consent: {
94
- checkoutDomain: env.PUBLIC_CHECKOUT_DOMAIN,
95
- storefrontAccessToken: env.PUBLIC_STOREFRONT_API_TOKEN,
83
+ /**
84
+ * Load data necessary for rendering content above the fold. This is the critical data
85
+ * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
86
+ */
87
+ async function loadCriticalData({context}: LoaderFunctionArgs) {
88
+ const {storefront} = context;
89
+
90
+ const [header] = await Promise.all([
91
+ storefront.query(HEADER_QUERY, {
92
+ cache: storefront.CacheLong(),
93
+ variables: {
94
+ headerMenuHandle: 'main-menu', // Adjust to your header menu handle
96
95
  },
97
- },
98
- {
99
- headers: {
100
- 'Set-Cookie': await context.session.commit(),
96
+ }),
97
+ // Add other queries here, so that they are loaded in parallel
98
+ ]);
99
+
100
+ return {
101
+ header,
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Load data for rendering content below the fold. This data is deferred and will be
107
+ * fetched after the initial page load. If it's unavailable, the page should still 200.
108
+ * Make sure to not throw any errors here, as it will cause the page to 500.
109
+ */
110
+ function loadDeferredData({context}: LoaderFunctionArgs) {
111
+ const {storefront, customerAccount, cart} = context;
112
+
113
+ // defer the footer query (below the fold)
114
+ const footer = storefront
115
+ .query(FOOTER_QUERY, {
116
+ cache: storefront.CacheLong(),
117
+ variables: {
118
+ footerMenuHandle: 'footer', // Adjust to your footer menu handle
101
119
  },
102
- },
103
- );
120
+ })
121
+ .catch((error) => {
122
+ // Log query errors, but don't throw them so the page can still render
123
+ console.error(error);
124
+ return null;
125
+ });
126
+ return {
127
+ cart: cart.get(),
128
+ isLoggedIn: customerAccount.isLoggedIn(),
129
+ footer,
130
+ };
104
131
  }
105
132
 
106
- export function Layout({children}: {children?: React.ReactNode}) {
133
+ function Layout({children}: {children?: React.ReactNode}) {
107
134
  const nonce = useNonce();
108
135
  const data = useRouteLoaderData<RootLoader>('root');
109
136
 
@@ -135,7 +162,11 @@ export function Layout({children}: {children?: React.ReactNode}) {
135
162
  }
136
163
 
137
164
  export default function App() {
138
- return <Outlet />;
165
+ return (
166
+ <Layout>
167
+ <Outlet />
168
+ </Layout>
169
+ );
139
170
  }
140
171
 
141
172
  export function ErrorBoundary() {
@@ -151,14 +182,16 @@ export function ErrorBoundary() {
151
182
  }
152
183
 
153
184
  return (
154
- <div className="route-error">
155
- <h1>Oops</h1>
156
- <h2>{errorStatus}</h2>
157
- {errorMessage && (
158
- <fieldset>
159
- <pre>{errorMessage}</pre>
160
- </fieldset>
161
- )}
162
- </div>
185
+ <Layout>
186
+ <div className="route-error">
187
+ <h1>Oops</h1>
188
+ <h2>{errorStatus}</h2>
189
+ {errorMessage && (
190
+ <fieldset>
191
+ <pre>{errorMessage}</pre>
192
+ </fieldset>
193
+ )}
194
+ </div>
195
+ </Layout>
163
196
  );
164
197
  }
@@ -11,13 +11,48 @@ export const meta: MetaFunction = () => {
11
11
  return [{title: 'Hydrogen | Home'}];
12
12
  };
13
13
 
14
- export async function loader({context}: LoaderFunctionArgs) {
15
- const {storefront} = context;
16
- const {collections} = await storefront.query(FEATURED_COLLECTION_QUERY);
17
- const featuredCollection = collections.nodes[0];
18
- const recommendedProducts = storefront.query(RECOMMENDED_PRODUCTS_QUERY);
14
+ export async function loader(args: LoaderFunctionArgs) {
15
+ // Start fetching non-critical data without blocking time to first byte
16
+ const deferredData = loadDeferredData(args);
19
17
 
20
- return defer({featuredCollection, recommendedProducts});
18
+ // Await the critical data required to render initial state of the page
19
+ const criticalData = await loadCriticalData(args);
20
+
21
+ return defer({...deferredData, ...criticalData});
22
+ }
23
+
24
+ /**
25
+ * Load data necessary for rendering content above the fold. This is the critical data
26
+ * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
27
+ */
28
+ async function loadCriticalData({context}: LoaderFunctionArgs) {
29
+ const [{collections}] = await Promise.all([
30
+ context.storefront.query(FEATURED_COLLECTION_QUERY),
31
+ // Add other queries here, so that they are loaded in parallel
32
+ ]);
33
+
34
+ return {
35
+ featuredCollection: collections.nodes[0],
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Load data for rendering content below the fold. This data is deferred and will be
41
+ * fetched after the initial page load. If it's unavailable, the page should still 200.
42
+ * Make sure to not throw any errors here, as it will cause the page to 500.
43
+ */
44
+ function loadDeferredData({context}: LoaderFunctionArgs) {
45
+ const recommendedProducts = context.storefront
46
+ .query(RECOMMENDED_PRODUCTS_QUERY)
47
+ .catch((error) => {
48
+ // Log query errors, but don't throw them so the page can still render
49
+ console.error(error);
50
+ return null;
51
+ });
52
+
53
+ return {
54
+ recommendedProducts,
55
+ };
21
56
  }
22
57
 
23
58
  export default function Homepage() {
@@ -55,32 +90,34 @@ function FeaturedCollection({
55
90
  function RecommendedProducts({
56
91
  products,
57
92
  }: {
58
- products: Promise<RecommendedProductsQuery>;
93
+ products: Promise<RecommendedProductsQuery | null>;
59
94
  }) {
60
95
  return (
61
96
  <div className="recommended-products">
62
97
  <h2>Recommended Products</h2>
63
98
  <Suspense fallback={<div>Loading...</div>}>
64
99
  <Await resolve={products}>
65
- {({products}) => (
100
+ {(response) => (
66
101
  <div className="recommended-products-grid">
67
- {products.nodes.map((product) => (
68
- <Link
69
- key={product.id}
70
- className="recommended-product"
71
- to={`/products/${product.handle}`}
72
- >
73
- <Image
74
- data={product.images.nodes[0]}
75
- aspectRatio="1/1"
76
- sizes="(min-width: 45em) 20vw, 50vw"
77
- />
78
- <h4>{product.title}</h4>
79
- <small>
80
- <Money data={product.priceRange.minVariantPrice} />
81
- </small>
82
- </Link>
83
- ))}
102
+ {response
103
+ ? response.products.nodes.map((product) => (
104
+ <Link
105
+ key={product.id}
106
+ className="recommended-product"
107
+ to={`/products/${product.handle}`}
108
+ >
109
+ <Image
110
+ data={product.images.nodes[0]}
111
+ aspectRatio="1/1"
112
+ sizes="(min-width: 45em) 20vw, 50vw"
113
+ />
114
+ <h4>{product.title}</h4>
115
+ <small>
116
+ <Money data={product.priceRange.minVariantPrice} />
117
+ </small>
118
+ </Link>
119
+ ))
120
+ : null}
84
121
  </div>
85
122
  )}
86
123
  </Await>
@@ -4,9 +4,5 @@ import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
4
4
  export async function loader({context}: LoaderFunctionArgs) {
5
5
  await context.customerAccount.handleAuthStatus();
6
6
 
7
- return redirect('/account', {
8
- headers: {
9
- 'Set-Cookie': await context.session.commit(),
10
- },
11
- });
7
+ return redirect('/account');
12
8
  }