@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
@@ -37,14 +37,7 @@ export const meta: MetaFunction = () => {
37
37
  export async function loader({context}: LoaderFunctionArgs) {
38
38
  await context.customerAccount.handleAuthStatus();
39
39
 
40
- return json(
41
- {},
42
- {
43
- headers: {
44
- 'Set-Cookie': await context.session.commit(),
45
- },
46
- },
47
- );
40
+ return json({});
48
41
  }
49
42
 
50
43
  export async function action({request, context}: ActionFunctionArgs) {
@@ -67,9 +60,6 @@ export async function action({request, context}: ActionFunctionArgs) {
67
60
  {error: {[addressId]: 'Unauthorized'}},
68
61
  {
69
62
  status: 401,
70
- headers: {
71
- 'Set-Cookie': await context.session.commit(),
72
- },
73
63
  },
74
64
  );
75
65
  }
@@ -121,27 +111,17 @@ export async function action({request, context}: ActionFunctionArgs) {
121
111
  throw new Error('Customer address create failed.');
122
112
  }
123
113
 
124
- return json(
125
- {
126
- error: null,
127
- createdAddress: data?.customerAddressCreate?.customerAddress,
128
- defaultAddress,
129
- },
130
- {
131
- headers: {
132
- 'Set-Cookie': await context.session.commit(),
133
- },
134
- },
135
- );
114
+ return json({
115
+ error: null,
116
+ createdAddress: data?.customerAddressCreate?.customerAddress,
117
+ defaultAddress,
118
+ });
136
119
  } catch (error: unknown) {
137
120
  if (error instanceof Error) {
138
121
  return json(
139
122
  {error: {[addressId]: error.message}},
140
123
  {
141
124
  status: 400,
142
- headers: {
143
- 'Set-Cookie': await context.session.commit(),
144
- },
145
125
  },
146
126
  );
147
127
  }
@@ -149,9 +129,6 @@ export async function action({request, context}: ActionFunctionArgs) {
149
129
  {error: {[addressId]: error}},
150
130
  {
151
131
  status: 400,
152
- headers: {
153
- 'Set-Cookie': await context.session.commit(),
154
- },
155
132
  },
156
133
  );
157
134
  }
@@ -183,27 +160,17 @@ export async function action({request, context}: ActionFunctionArgs) {
183
160
  throw new Error('Customer address update failed.');
184
161
  }
185
162
 
186
- return json(
187
- {
188
- error: null,
189
- updatedAddress: address,
190
- defaultAddress,
191
- },
192
- {
193
- headers: {
194
- 'Set-Cookie': await context.session.commit(),
195
- },
196
- },
197
- );
163
+ return json({
164
+ error: null,
165
+ updatedAddress: address,
166
+ defaultAddress,
167
+ });
198
168
  } catch (error: unknown) {
199
169
  if (error instanceof Error) {
200
170
  return json(
201
171
  {error: {[addressId]: error.message}},
202
172
  {
203
173
  status: 400,
204
- headers: {
205
- 'Set-Cookie': await context.session.commit(),
206
- },
207
174
  },
208
175
  );
209
176
  }
@@ -211,9 +178,6 @@ export async function action({request, context}: ActionFunctionArgs) {
211
178
  {error: {[addressId]: error}},
212
179
  {
213
180
  status: 400,
214
- headers: {
215
- 'Set-Cookie': await context.session.commit(),
216
- },
217
181
  },
218
182
  );
219
183
  }
@@ -241,23 +205,13 @@ export async function action({request, context}: ActionFunctionArgs) {
241
205
  throw new Error('Customer address delete failed.');
242
206
  }
243
207
 
244
- return json(
245
- {error: null, deletedAddress: addressId},
246
- {
247
- headers: {
248
- 'Set-Cookie': await context.session.commit(),
249
- },
250
- },
251
- );
208
+ return json({error: null, deletedAddress: addressId});
252
209
  } catch (error: unknown) {
253
210
  if (error instanceof Error) {
254
211
  return json(
255
212
  {error: {[addressId]: error.message}},
256
213
  {
257
214
  status: 400,
258
- headers: {
259
- 'Set-Cookie': await context.session.commit(),
260
- },
261
215
  },
262
216
  );
263
217
  }
@@ -265,9 +219,6 @@ export async function action({request, context}: ActionFunctionArgs) {
265
219
  {error: {[addressId]: error}},
266
220
  {
267
221
  status: 400,
268
- headers: {
269
- 'Set-Cookie': await context.session.commit(),
270
- },
271
222
  },
272
223
  );
273
224
  }
@@ -278,9 +229,6 @@ export async function action({request, context}: ActionFunctionArgs) {
278
229
  {error: {[addressId]: 'Method not allowed'}},
279
230
  {
280
231
  status: 405,
281
- headers: {
282
- 'Set-Cookie': await context.session.commit(),
283
- },
284
232
  },
285
233
  );
286
234
  }
@@ -291,9 +239,6 @@ export async function action({request, context}: ActionFunctionArgs) {
291
239
  {error: error.message},
292
240
  {
293
241
  status: 400,
294
- headers: {
295
- 'Set-Cookie': await context.session.commit(),
296
- },
297
242
  },
298
243
  );
299
244
  }
@@ -301,9 +246,6 @@ export async function action({request, context}: ActionFunctionArgs) {
301
246
  {error},
302
247
  {
303
248
  status: 400,
304
- headers: {
305
- 'Set-Cookie': await context.session.commit(),
306
- },
307
249
  },
308
250
  );
309
251
  }
@@ -40,20 +40,13 @@ export async function loader({params, context}: LoaderFunctionArgs) {
40
40
  firstDiscount?.__typename === 'PricingPercentageValue' &&
41
41
  firstDiscount?.percentage;
42
42
 
43
- return json(
44
- {
45
- order,
46
- lineItems,
47
- discountValue,
48
- discountPercentage,
49
- fulfillmentStatus,
50
- },
51
- {
52
- headers: {
53
- 'Set-Cookie': await context.session.commit(),
54
- },
55
- },
56
- );
43
+ return json({
44
+ order,
45
+ lineItems,
46
+ discountValue,
47
+ discountPercentage,
48
+ fulfillmentStatus,
49
+ });
57
50
  }
58
51
 
59
52
  export default function OrderRoute() {
@@ -34,14 +34,7 @@ export async function loader({request, context}: LoaderFunctionArgs) {
34
34
  throw Error('Customer orders not found');
35
35
  }
36
36
 
37
- return json(
38
- {customer: data.customer},
39
- {
40
- headers: {
41
- 'Set-Cookie': await context.session.commit(),
42
- },
43
- },
44
- );
37
+ return json({customer: data.customer});
45
38
  }
46
39
 
47
40
  export default function Orders() {
@@ -26,14 +26,7 @@ export const meta: MetaFunction = () => {
26
26
  export async function loader({context}: LoaderFunctionArgs) {
27
27
  await context.customerAccount.handleAuthStatus();
28
28
 
29
- return json(
30
- {},
31
- {
32
- headers: {
33
- 'Set-Cookie': await context.session.commit(),
34
- },
35
- },
36
- );
29
+ return json({});
37
30
  }
38
31
 
39
32
  export async function action({request, context}: ActionFunctionArgs) {
@@ -75,25 +68,15 @@ export async function action({request, context}: ActionFunctionArgs) {
75
68
  throw new Error('Customer profile update failed.');
76
69
  }
77
70
 
78
- return json(
79
- {
80
- error: null,
81
- customer: data?.customerUpdate?.customer,
82
- },
83
- {
84
- headers: {
85
- 'Set-Cookie': await context.session.commit(),
86
- },
87
- },
88
- );
71
+ return json({
72
+ error: null,
73
+ customer: data?.customerUpdate?.customer,
74
+ });
89
75
  } catch (error: any) {
90
76
  return json(
91
77
  {error: error.message, customer: null},
92
78
  {
93
79
  status: 400,
94
- headers: {
95
- 'Set-Cookie': await context.session.commit(),
96
- },
97
80
  },
98
81
  );
99
82
  }
@@ -20,7 +20,6 @@ export async function loader({context}: LoaderFunctionArgs) {
20
20
  {
21
21
  headers: {
22
22
  'Cache-Control': 'no-cache, no-store, must-revalidate',
23
- 'Set-Cookie': await context.session.commit(),
24
23
  },
25
24
  },
26
25
  );
@@ -1,4 +1,4 @@
1
- import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
1
+ import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
  import {useLoaderData, type MetaFunction} from '@remix-run/react';
3
3
  import {Image} from '@shopify/hydrogen';
4
4
 
@@ -6,16 +6,33 @@ export const meta: MetaFunction<typeof loader> = ({data}) => {
6
6
  return [{title: `Hydrogen | ${data?.article.title ?? ''} article`}];
7
7
  };
8
8
 
9
- export async function loader({params, context}: LoaderFunctionArgs) {
9
+ export async function loader(args: LoaderFunctionArgs) {
10
+ // Start fetching non-critical data without blocking time to first byte
11
+ const deferredData = loadDeferredData(args);
12
+
13
+ // Await the critical data required to render initial state of the page
14
+ const criticalData = await loadCriticalData(args);
15
+
16
+ return defer({...deferredData, ...criticalData});
17
+ }
18
+
19
+ /**
20
+ * Load data necessary for rendering content above the fold. This is the critical data
21
+ * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
22
+ */
23
+ async function loadCriticalData({context, params}: LoaderFunctionArgs) {
10
24
  const {blogHandle, articleHandle} = params;
11
25
 
12
26
  if (!articleHandle || !blogHandle) {
13
27
  throw new Response('Not found', {status: 404});
14
28
  }
15
29
 
16
- const {blog} = await context.storefront.query(ARTICLE_QUERY, {
17
- variables: {blogHandle, articleHandle},
18
- });
30
+ const [{blog}] = await Promise.all([
31
+ context.storefront.query(ARTICLE_QUERY, {
32
+ variables: {blogHandle, articleHandle},
33
+ }),
34
+ // Add other queries here, so that they are loaded in parallel
35
+ ]);
19
36
 
20
37
  if (!blog?.articleByHandle) {
21
38
  throw new Response(null, {status: 404});
@@ -23,7 +40,16 @@ export async function loader({params, context}: LoaderFunctionArgs) {
23
40
 
24
41
  const article = blog.articleByHandle;
25
42
 
26
- return json({article});
43
+ return {article};
44
+ }
45
+
46
+ /**
47
+ * Load data for rendering content below the fold. This data is deferred and will be
48
+ * fetched after the initial page load. If it's unavailable, the page should still 200.
49
+ * Make sure to not throw any errors here, as it will cause the page to 500.
50
+ */
51
+ function loadDeferredData({context}: LoaderFunctionArgs) {
52
+ return {};
27
53
  }
28
54
 
29
55
  export default function Article() {
@@ -1,4 +1,4 @@
1
- import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
1
+ import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
  import {Link, useLoaderData, type MetaFunction} from '@remix-run/react';
3
3
  import {Image, Pagination, getPaginationVariables} from '@shopify/hydrogen';
4
4
  import type {ArticleItemFragment} from 'storefrontapi.generated';
@@ -7,10 +7,24 @@ export const meta: MetaFunction<typeof loader> = ({data}) => {
7
7
  return [{title: `Hydrogen | ${data?.blog.title ?? ''} blog`}];
8
8
  };
9
9
 
10
- export async function loader({
10
+ export async function loader(args: LoaderFunctionArgs) {
11
+ // Start fetching non-critical data without blocking time to first byte
12
+ const deferredData = loadDeferredData(args);
13
+
14
+ // Await the critical data required to render initial state of the page
15
+ const criticalData = await loadCriticalData(args);
16
+
17
+ return defer({...deferredData, ...criticalData});
18
+ }
19
+
20
+ /**
21
+ * Load data necessary for rendering content above the fold. This is the critical data
22
+ * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
23
+ */
24
+ async function loadCriticalData({
25
+ context,
11
26
  request,
12
27
  params,
13
- context: {storefront},
14
28
  }: LoaderFunctionArgs) {
15
29
  const paginationVariables = getPaginationVariables(request, {
16
30
  pageBy: 4,
@@ -20,18 +34,30 @@ export async function loader({
20
34
  throw new Response(`blog not found`, {status: 404});
21
35
  }
22
36
 
23
- const {blog} = await storefront.query(BLOGS_QUERY, {
24
- variables: {
25
- blogHandle: params.blogHandle,
26
- ...paginationVariables,
27
- },
28
- });
37
+ const [{blog}] = await Promise.all([
38
+ context.storefront.query(BLOGS_QUERY, {
39
+ variables: {
40
+ blogHandle: params.blogHandle,
41
+ ...paginationVariables,
42
+ },
43
+ }),
44
+ // Add other queries here, so that they are loaded in parallel
45
+ ]);
29
46
 
30
47
  if (!blog?.articles) {
31
48
  throw new Response('Not found', {status: 404});
32
49
  }
33
50
 
34
- return json({blog});
51
+ return {blog};
52
+ }
53
+
54
+ /**
55
+ * Load data for rendering content below the fold. This data is deferred and will be
56
+ * fetched after the initial page load. If it's unavailable, the page should still 200.
57
+ * Make sure to not throw any errors here, as it will cause the page to 500.
58
+ */
59
+ function loadDeferredData({context}: LoaderFunctionArgs) {
60
+ return {};
35
61
  }
36
62
 
37
63
  export default function Blog() {
@@ -1,4 +1,4 @@
1
- import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
1
+ import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
  import {Link, useLoaderData, type MetaFunction} from '@remix-run/react';
3
3
  import {Pagination, getPaginationVariables} from '@shopify/hydrogen';
4
4
 
@@ -6,22 +6,45 @@ export const meta: MetaFunction = () => {
6
6
  return [{title: `Hydrogen | Blogs`}];
7
7
  };
8
8
 
9
- export const loader = async ({
10
- request,
11
- context: {storefront},
12
- }: LoaderFunctionArgs) => {
9
+ export async function loader(args: LoaderFunctionArgs) {
10
+ // Start fetching non-critical data without blocking time to first byte
11
+ const deferredData = loadDeferredData(args);
12
+
13
+ // Await the critical data required to render initial state of the page
14
+ const criticalData = await loadCriticalData(args);
15
+
16
+ return defer({...deferredData, ...criticalData});
17
+ }
18
+
19
+ /**
20
+ * Load data necessary for rendering content above the fold. This is the critical data
21
+ * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
22
+ */
23
+ async function loadCriticalData({context, request}: LoaderFunctionArgs) {
13
24
  const paginationVariables = getPaginationVariables(request, {
14
25
  pageBy: 10,
15
26
  });
16
27
 
17
- const {blogs} = await storefront.query(BLOGS_QUERY, {
18
- variables: {
19
- ...paginationVariables,
20
- },
21
- });
28
+ const [{blogs}] = await Promise.all([
29
+ context.storefront.query(BLOGS_QUERY, {
30
+ variables: {
31
+ ...paginationVariables,
32
+ },
33
+ }),
34
+ // Add other queries here, so that they are loaded in parallel
35
+ ]);
22
36
 
23
- return json({blogs});
24
- };
37
+ return {blogs};
38
+ }
39
+
40
+ /**
41
+ * Load data for rendering content below the fold. This data is deferred and will be
42
+ * fetched after the initial page load. If it's unavailable, the page should still 200.
43
+ * Make sure to not throw any errors here, as it will cause the page to 500.
44
+ */
45
+ function loadDeferredData({context}: LoaderFunctionArgs) {
46
+ return {};
47
+ }
25
48
 
26
49
  export default function Blogs() {
27
50
  const {blogs} = useLoaderData<typeof loader>();
@@ -68,8 +68,6 @@ export async function action({request, context}: ActionFunctionArgs) {
68
68
  headers.set('Location', redirectTo);
69
69
  }
70
70
 
71
- headers.append('Set-Cookie', await context.session.commit());
72
-
73
71
  return json(
74
72
  {
75
73
  cart: cartResult,
@@ -1,4 +1,4 @@
1
- import {json, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
1
+ import {defer, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
  import {useLoaderData, Link, type MetaFunction} from '@remix-run/react';
3
3
  import {
4
4
  Pagination,
@@ -14,7 +14,25 @@ export const meta: MetaFunction<typeof loader> = ({data}) => {
14
14
  return [{title: `Hydrogen | ${data?.collection.title ?? ''} Collection`}];
15
15
  };
16
16
 
17
- export async function loader({request, params, context}: LoaderFunctionArgs) {
17
+ export async function loader(args: LoaderFunctionArgs) {
18
+ // Start fetching non-critical data without blocking time to first byte
19
+ const deferredData = loadDeferredData(args);
20
+
21
+ // Await the critical data required to render initial state of the page
22
+ const criticalData = await loadCriticalData(args);
23
+
24
+ return defer({...deferredData, ...criticalData});
25
+ }
26
+
27
+ /**
28
+ * Load data necessary for rendering content above the fold. This is the critical data
29
+ * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
30
+ */
31
+ async function loadCriticalData({
32
+ context,
33
+ params,
34
+ request,
35
+ }: LoaderFunctionArgs) {
18
36
  const {handle} = params;
19
37
  const {storefront} = context;
20
38
  const paginationVariables = getPaginationVariables(request, {
@@ -22,19 +40,34 @@ export async function loader({request, params, context}: LoaderFunctionArgs) {
22
40
  });
23
41
 
24
42
  if (!handle) {
25
- return redirect('/collections');
43
+ throw redirect('/collections');
26
44
  }
27
45
 
28
- const {collection} = await storefront.query(COLLECTION_QUERY, {
29
- variables: {handle, ...paginationVariables},
30
- });
46
+ const [{collection}] = await Promise.all([
47
+ storefront.query(COLLECTION_QUERY, {
48
+ variables: {handle, ...paginationVariables},
49
+ // Add other queries here, so that they are loaded in parallel
50
+ }),
51
+ ]);
31
52
 
32
53
  if (!collection) {
33
54
  throw new Response(`Collection ${handle} not found`, {
34
55
  status: 404,
35
56
  });
36
57
  }
37
- return json({collection});
58
+
59
+ return {
60
+ collection,
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Load data for rendering content below the fold. This data is deferred and will be
66
+ * fetched after the initial page load. If it's unavailable, the page should still 200.
67
+ * Make sure to not throw any errors here, as it will cause the page to 500.
68
+ */
69
+ function loadDeferredData({context}: LoaderFunctionArgs) {
70
+ return {};
38
71
  }
39
72
 
40
73
  export default function Collection() {
@@ -1,18 +1,44 @@
1
1
  import {useLoaderData, Link} from '@remix-run/react';
2
- import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
+ import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
3
3
  import {Pagination, getPaginationVariables, Image} from '@shopify/hydrogen';
4
4
  import type {CollectionFragment} from 'storefrontapi.generated';
5
5
 
6
- export async function loader({context, request}: LoaderFunctionArgs) {
6
+ export async function loader(args: LoaderFunctionArgs) {
7
+ // Start fetching non-critical data without blocking time to first byte
8
+ const deferredData = loadDeferredData(args);
9
+
10
+ // Await the critical data required to render initial state of the page
11
+ const criticalData = await loadCriticalData(args);
12
+
13
+ return defer({...deferredData, ...criticalData});
14
+ }
15
+
16
+ /**
17
+ * Load data necessary for rendering content above the fold. This is the critical data
18
+ * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
19
+ */
20
+ async function loadCriticalData({context, request}: LoaderFunctionArgs) {
7
21
  const paginationVariables = getPaginationVariables(request, {
8
22
  pageBy: 4,
9
23
  });
10
24
 
11
- const {collections} = await context.storefront.query(COLLECTIONS_QUERY, {
12
- variables: paginationVariables,
13
- });
25
+ const [{collections}] = await Promise.all([
26
+ context.storefront.query(COLLECTIONS_QUERY, {
27
+ variables: paginationVariables,
28
+ }),
29
+ // Add other queries here, so that they are loaded in parallel
30
+ ]);
31
+
32
+ return {collections};
33
+ }
14
34
 
15
- return json({collections});
35
+ /**
36
+ * Load data for rendering content below the fold. This data is deferred and will be
37
+ * fetched after the initial page load. If it's unavailable, the page should still 200.
38
+ * Make sure to not throw any errors here, as it will cause the page to 500.
39
+ */
40
+ function loadDeferredData({context}: LoaderFunctionArgs) {
41
+ return {};
16
42
  }
17
43
 
18
44
  export default function Collections() {
@@ -1,4 +1,4 @@
1
- import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
1
+ import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
  import {useLoaderData, Link, type MetaFunction} from '@remix-run/react';
3
3
  import {
4
4
  Pagination,
@@ -13,17 +13,42 @@ export const meta: MetaFunction<typeof loader> = () => {
13
13
  return [{title: `Hydrogen | Products`}];
14
14
  };
15
15
 
16
- export async function loader({request, context}: LoaderFunctionArgs) {
16
+ export async function loader(args: LoaderFunctionArgs) {
17
+ // Start fetching non-critical data without blocking time to first byte
18
+ const deferredData = loadDeferredData(args);
19
+
20
+ // Await the critical data required to render initial state of the page
21
+ const criticalData = await loadCriticalData(args);
22
+
23
+ return defer({...deferredData, ...criticalData});
24
+ }
25
+
26
+ /**
27
+ * Load data necessary for rendering content above the fold. This is the critical data
28
+ * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
29
+ */
30
+ async function loadCriticalData({context, request}: LoaderFunctionArgs) {
17
31
  const {storefront} = context;
18
32
  const paginationVariables = getPaginationVariables(request, {
19
33
  pageBy: 8,
20
34
  });
21
35
 
22
- const {products} = await storefront.query(CATALOG_QUERY, {
23
- variables: {...paginationVariables},
24
- });
36
+ const [{products}] = await Promise.all([
37
+ storefront.query(CATALOG_QUERY, {
38
+ variables: {...paginationVariables},
39
+ }),
40
+ // Add other queries here, so that they are loaded in parallel
41
+ ]);
42
+ return {products};
43
+ }
25
44
 
26
- return json({products});
45
+ /**
46
+ * Load data for rendering content below the fold. This data is deferred and will be
47
+ * fetched after the initial page load. If it's unavailable, the page should still 200.
48
+ * Make sure to not throw any errors here, as it will cause the page to 500.
49
+ */
50
+ function loadDeferredData({context}: LoaderFunctionArgs) {
51
+ return {};
27
52
  }
28
53
 
29
54
  export default function Collection() {