@shopify/cli 3.65.1 → 3.65.3

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 (122) hide show
  1. package/dist/assets/hydrogen/i18n/domains.ts +4 -11
  2. package/dist/assets/hydrogen/i18n/mock-i18n-types.ts +4 -2
  3. package/dist/assets/hydrogen/i18n/subdomains.ts +4 -11
  4. package/dist/assets/hydrogen/i18n/subfolders.ts +4 -11
  5. package/dist/assets/hydrogen/starter/CHANGELOG.md +165 -0
  6. package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +5 -2
  7. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +2 -2
  8. package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +65 -19
  9. package/dist/assets/hydrogen/starter/app/components/PaginatedResourceSection.tsx +42 -0
  10. package/dist/assets/hydrogen/starter/app/components/SearchForm.tsx +68 -0
  11. package/dist/assets/hydrogen/starter/app/components/SearchFormPredictive.tsx +76 -0
  12. package/dist/assets/hydrogen/starter/app/components/SearchResults.tsx +164 -0
  13. package/dist/assets/hydrogen/starter/app/components/SearchResultsPredictive.tsx +322 -0
  14. package/dist/assets/hydrogen/starter/app/entry.client.tsx +10 -8
  15. package/dist/assets/hydrogen/starter/app/entry.server.tsx +1 -1
  16. package/dist/assets/hydrogen/starter/app/lib/context.ts +43 -0
  17. package/dist/assets/hydrogen/starter/app/lib/fragments.ts +53 -0
  18. package/dist/assets/hydrogen/starter/app/lib/search.ts +74 -24
  19. package/dist/assets/hydrogen/starter/app/root.tsx +4 -7
  20. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +2 -3
  21. package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +5 -19
  22. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +11 -24
  23. package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +14 -27
  24. package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +12 -30
  25. package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +13 -27
  26. package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +9 -31
  27. package/dist/assets/hydrogen/starter/app/routes/search.tsx +312 -73
  28. package/dist/assets/hydrogen/starter/app/styles/reset.css +12 -2
  29. package/dist/assets/hydrogen/starter/env.d.ts +11 -30
  30. package/dist/assets/hydrogen/starter/guides/predictiveSearch/predictiveSearch.jpg +0 -0
  31. package/dist/assets/hydrogen/starter/guides/predictiveSearch/predictiveSearch.md +391 -0
  32. package/dist/assets/hydrogen/starter/guides/search/search.jpg +0 -0
  33. package/dist/assets/hydrogen/starter/guides/search/search.md +333 -0
  34. package/dist/assets/hydrogen/starter/package.json +4 -4
  35. package/dist/assets/hydrogen/starter/server.ts +18 -74
  36. package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +242 -172
  37. package/dist/assets/hydrogen/virtual-routes/components/{PageLayout.jsx → Layout.jsx} +2 -2
  38. package/dist/assets/hydrogen/virtual-routes/virtual-root.jsx +7 -6
  39. package/dist/{chunk-H42RFZDD.js → chunk-3ABSSTBQ.js} +4 -4
  40. package/dist/{chunk-M7WMYV4S.js → chunk-3D4VZQOH.js} +2 -2
  41. package/dist/{chunk-J7BYFGNJ.js → chunk-3GSKXZGY.js} +2 -2
  42. package/dist/{chunk-TDWX3KIR.js → chunk-3LDWVYMD.js} +2 -2
  43. package/dist/{chunk-N2BXKOJG.js → chunk-646BIVHE.js} +4 -4
  44. package/dist/{chunk-CZ3SHYYH.js → chunk-7WAEFADN.js} +4 -4
  45. package/dist/{chunk-EKT2GUGH.js → chunk-7WGBIPDW.js} +2 -2
  46. package/dist/{chunk-M6KGRVDD.js → chunk-AX77SAMU.js} +3 -3
  47. package/dist/{chunk-4HAEQQTQ.js → chunk-BQBBVYYU.js} +4 -4
  48. package/dist/{chunk-5YD4FDOS.js → chunk-BZLNTDGG.js} +3 -3
  49. package/dist/{chunk-VWALMO2Z.js → chunk-CSCEGIBZ.js} +3 -3
  50. package/dist/{chunk-F2Y7KYHZ.js → chunk-EIUQV76I.js} +5 -5
  51. package/dist/{chunk-MODBIZ4R.js → chunk-GN74L7IW.js} +2 -2
  52. package/dist/{chunk-5EAVIJTQ.js → chunk-HYCRESCR.js} +2 -2
  53. package/dist/{chunk-GDARYUPU.js → chunk-K7KD247K.js} +188 -243
  54. package/dist/{chunk-PZM45AUI.js → chunk-KIUXMPTX.js} +3 -3
  55. package/dist/{chunk-PYMSCBPA.js → chunk-LAJ4OEME.js} +2 -2
  56. package/dist/{chunk-YVHV3H5H.js → chunk-MIQBXNSN.js} +4 -4
  57. package/dist/{chunk-BLKDGMHM.js → chunk-MV6A3QHA.js} +4 -4
  58. package/dist/{chunk-CFFAWVDL.js → chunk-N3YORLAS.js} +2 -2
  59. package/dist/{chunk-EU5ZOEUT.js → chunk-NBTEOGQW.js} +2 -2
  60. package/dist/{chunk-ZXJU6UP4.js → chunk-O3JOUAA5.js} +4 -4
  61. package/dist/{chunk-EZ5DG73H.js → chunk-PEAIOYXD.js} +4 -4
  62. package/dist/{chunk-YDS7NZBQ.js → chunk-R5GT4GBL.js} +4 -4
  63. package/dist/{chunk-6M65VRAT.js → chunk-S7FJTFYR.js} +5 -5
  64. package/dist/{chunk-DX2RXOQ5.js → chunk-S7RH664J.js} +3 -3
  65. package/dist/{chunk-WMECC32P.js → chunk-SKF2SKWO.js} +3 -3
  66. package/dist/{chunk-27HGZPUX.js → chunk-SMKCVFDT.js} +3 -3
  67. package/dist/{chunk-EID6L4PR.js → chunk-T4Y7NDNJ.js} +2 -2
  68. package/dist/{chunk-PY33KMCK.js → chunk-TWWJNMTO.js} +2 -2
  69. package/dist/{chunk-YXPGPWR2.js → chunk-U2PN6QZ2.js} +5 -5
  70. package/dist/{chunk-3REVOIEW.js → chunk-UBCH575K.js} +5 -5
  71. package/dist/{chunk-A4NQWDPT.js → chunk-XLURAR5E.js} +3 -3
  72. package/dist/{chunk-ZZKUI3DP.js → chunk-YPG7LXPN.js} +3 -3
  73. package/dist/cli/commands/auth/logout.js +10 -10
  74. package/dist/cli/commands/auth/logout.test.js +11 -11
  75. package/dist/cli/commands/debug/command-flags.js +9 -9
  76. package/dist/cli/commands/demo/catalog.js +10 -10
  77. package/dist/cli/commands/demo/generate-file.js +10 -10
  78. package/dist/cli/commands/demo/index.js +10 -10
  79. package/dist/cli/commands/demo/print-ai-prompt.js +10 -10
  80. package/dist/cli/commands/docs/generate.js +9 -9
  81. package/dist/cli/commands/docs/generate.test.js +9 -9
  82. package/dist/cli/commands/help.js +9 -9
  83. package/dist/cli/commands/kitchen-sink/async.js +10 -10
  84. package/dist/cli/commands/kitchen-sink/async.test.js +10 -10
  85. package/dist/cli/commands/kitchen-sink/index.js +12 -12
  86. package/dist/cli/commands/kitchen-sink/index.test.js +12 -12
  87. package/dist/cli/commands/kitchen-sink/prompts.js +10 -10
  88. package/dist/cli/commands/kitchen-sink/prompts.test.js +10 -10
  89. package/dist/cli/commands/kitchen-sink/static.js +10 -10
  90. package/dist/cli/commands/kitchen-sink/static.test.js +10 -10
  91. package/dist/cli/commands/search.js +10 -10
  92. package/dist/cli/commands/upgrade.js +9 -9
  93. package/dist/cli/commands/version.js +10 -10
  94. package/dist/cli/commands/version.test.js +10 -10
  95. package/dist/cli/services/commands/search.js +2 -2
  96. package/dist/cli/services/commands/search.test.js +2 -2
  97. package/dist/cli/services/commands/version.js +4 -4
  98. package/dist/cli/services/commands/version.test.js +5 -5
  99. package/dist/cli/services/demo.js +2 -2
  100. package/dist/cli/services/demo.test.js +2 -2
  101. package/dist/cli/services/kitchen-sink/async.js +2 -2
  102. package/dist/cli/services/kitchen-sink/prompts.js +2 -2
  103. package/dist/cli/services/kitchen-sink/static.js +2 -2
  104. package/dist/cli/services/upgrade.js +3 -3
  105. package/dist/cli/services/upgrade.test.js +5 -5
  106. package/dist/{custom-oclif-loader-JHNX2EGV.js → custom-oclif-loader-BT7EH2NN.js} +3 -3
  107. package/dist/{error-handler-4UJ6363X.js → error-handler-OSEY6KVA.js} +8 -8
  108. package/dist/hooks/postrun.js +6 -6
  109. package/dist/hooks/prerun.js +4 -4
  110. package/dist/index.js +1333 -1279
  111. package/dist/{local-V7RONWNU.js → local-OQXN5NM2.js} +2 -2
  112. package/dist/{morph-DN4AZJZW.js → morph-IQTWRBBT.js} +16 -12
  113. package/dist/{node-3H4OKRLA.js → node-YQVH3Y7J.js} +13 -13
  114. package/dist/{node-package-manager-XM7EXHQA.js → node-package-manager-VW2DN7R4.js} +3 -3
  115. package/dist/{system-F63VIZ5U.js → system-347PZWVP.js} +2 -2
  116. package/dist/tsconfig.tsbuildinfo +1 -1
  117. package/dist/{ui-BXWWRIFS.js → ui-S7L55PBH.js} +2 -2
  118. package/dist/{workerd-A5NCF6UA.js → workerd-OLKE7G4X.js} +12 -12
  119. package/oclif.manifest.json +39 -2
  120. package/package.json +7 -7
  121. package/dist/assets/hydrogen/starter/app/components/Search.tsx +0 -514
  122. package/dist/assets/hydrogen/starter/app/routes/api.predictive-search.tsx +0 -318
@@ -1,8 +1,17 @@
1
- import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
1
+ import {
2
+ json,
3
+ type LoaderFunctionArgs,
4
+ type ActionFunctionArgs,
5
+ } from '@shopify/remix-oxygen';
2
6
  import {useLoaderData, type MetaFunction} from '@remix-run/react';
3
7
  import {getPaginationVariables, Analytics} from '@shopify/hydrogen';
4
-
5
- import {SearchForm, SearchResults, NoSearchResults} from '~/components/Search';
8
+ import {SearchForm} from '~/components/SearchForm';
9
+ import {SearchResults} from '~/components/SearchResults';
10
+ import {
11
+ type RegularSearchReturn,
12
+ type PredictiveSearchReturn,
13
+ getEmptyPredictiveSearchResult,
14
+ } from '~/lib/search';
6
15
 
7
16
  export const meta: MetaFunction = () => {
8
17
  return [{title: `Hydrogen | Search`}];
@@ -10,66 +19,68 @@ export const meta: MetaFunction = () => {
10
19
 
11
20
  export async function loader({request, context}: LoaderFunctionArgs) {
12
21
  const url = new URL(request.url);
13
- const searchParams = new URLSearchParams(url.search);
14
- const variables = getPaginationVariables(request, {pageBy: 8});
15
- const searchTerm = String(searchParams.get('q') || '');
22
+ const isPredictive = url.searchParams.has('predictive');
23
+ const searchPromise = isPredictive
24
+ ? predictiveSearch({request, context})
25
+ : regularSearch({request, context});
16
26
 
17
- if (!searchTerm) {
18
- return {
19
- searchResults: {results: null, totalResults: 0},
20
- searchTerm,
21
- };
22
- }
23
-
24
- const {errors, ...data} = await context.storefront.query(SEARCH_QUERY, {
25
- variables: {
26
- query: searchTerm,
27
- ...variables,
28
- },
27
+ searchPromise.catch((error: Error) => {
28
+ console.error(error);
29
+ return {term: '', result: null, error: error.message};
29
30
  });
30
31
 
31
- if (!data) {
32
- throw new Error('No search data returned from Shopify API');
33
- }
34
-
35
- const totalResults = Object.values(data).reduce((total, value) => {
36
- return total + value.nodes.length;
37
- }, 0);
38
-
39
- const searchResults = {
40
- results: data,
41
- totalResults,
42
- };
43
-
44
- return defer({
45
- searchTerm,
46
- searchResults,
47
- });
32
+ return json(await searchPromise);
48
33
  }
49
34
 
35
+ /**
36
+ * Renders the /search route
37
+ */
50
38
  export default function SearchPage() {
51
- const {searchTerm, searchResults} = useLoaderData<typeof loader>();
39
+ const {type, term, result, error} = useLoaderData<typeof loader>();
40
+ if (type === 'predictive') return null;
52
41
 
53
42
  return (
54
43
  <div className="search">
55
44
  <h1>Search</h1>
56
- <SearchForm searchTerm={searchTerm} />
57
- {!searchTerm || !searchResults.totalResults ? (
58
- <NoSearchResults />
45
+ <SearchForm>
46
+ {({inputRef}) => (
47
+ <>
48
+ <input
49
+ defaultValue={term}
50
+ name="q"
51
+ placeholder="Search…"
52
+ ref={inputRef}
53
+ type="search"
54
+ />
55
+ &nbsp;
56
+ <button type="submit">Search</button>
57
+ </>
58
+ )}
59
+ </SearchForm>
60
+ {error && <p style={{color: 'red'}}>{error}</p>}
61
+ {!term || !result?.total ? (
62
+ <SearchResults.Empty />
59
63
  ) : (
60
- <SearchResults
61
- results={searchResults.results}
62
- searchTerm={searchTerm}
63
- />
64
+ <SearchResults result={result} term={term}>
65
+ {({articles, pages, products, term}) => (
66
+ <div>
67
+ <SearchResults.Products products={products} term={term} />
68
+ <SearchResults.Pages pages={pages} term={term} />
69
+ <SearchResults.Articles articles={articles} term={term} />
70
+ </div>
71
+ )}
72
+ </SearchResults>
64
73
  )}
65
- <Analytics.SearchView
66
- data={{searchTerm, searchResults}}
67
- />
74
+ <Analytics.SearchView data={{searchTerm: term, searchResults: result}} />
68
75
  </div>
69
76
  );
70
77
  }
71
78
 
72
- const SEARCH_QUERY = `#graphql
79
+ /**
80
+ * Regular search query and fragments
81
+ * (adjust as needed)
82
+ */
83
+ const SEARCH_PRODUCT_FRAGMENT = `#graphql
73
84
  fragment SearchProduct on Product {
74
85
  __typename
75
86
  handle
@@ -106,6 +117,9 @@ const SEARCH_QUERY = `#graphql
106
117
  }
107
118
  }
108
119
  }
120
+ ` as const;
121
+
122
+ const SEARCH_PAGE_FRAGMENT = `#graphql
109
123
  fragment SearchPage on Page {
110
124
  __typename
111
125
  handle
@@ -113,6 +127,9 @@ const SEARCH_QUERY = `#graphql
113
127
  title
114
128
  trackingParameters
115
129
  }
130
+ ` as const;
131
+
132
+ const SEARCH_ARTICLE_FRAGMENT = `#graphql
116
133
  fragment SearchArticle on Article {
117
134
  __typename
118
135
  handle
@@ -120,41 +137,43 @@ const SEARCH_QUERY = `#graphql
120
137
  title
121
138
  trackingParameters
122
139
  }
123
- query search(
140
+ ` as const;
141
+
142
+ const PAGE_INFO_FRAGMENT = `#graphql
143
+ fragment PageInfoFragment on PageInfo {
144
+ hasNextPage
145
+ hasPreviousPage
146
+ startCursor
147
+ endCursor
148
+ }
149
+ ` as const;
150
+
151
+ // NOTE: https://shopify.dev/docs/api/storefront/latest/queries/search
152
+ export const SEARCH_QUERY = `#graphql
153
+ query RegularSearch(
124
154
  $country: CountryCode
125
155
  $endCursor: String
126
156
  $first: Int
127
157
  $language: LanguageCode
128
158
  $last: Int
129
- $query: String!
159
+ $term: String!
130
160
  $startCursor: String
131
161
  ) @inContext(country: $country, language: $language) {
132
- products: search(
133
- query: $query,
134
- unavailableProducts: HIDE,
135
- types: [PRODUCT],
162
+ articles: search(
163
+ query: $term,
164
+ types: [ARTICLE],
136
165
  first: $first,
137
- sortKey: RELEVANCE,
138
- last: $last,
139
- before: $startCursor,
140
- after: $endCursor
141
166
  ) {
142
167
  nodes {
143
- ...on Product {
144
- ...SearchProduct
168
+ ...on Article {
169
+ ...SearchArticle
145
170
  }
146
171
  }
147
- pageInfo {
148
- hasNextPage
149
- hasPreviousPage
150
- startCursor
151
- endCursor
152
- }
153
172
  }
154
173
  pages: search(
155
- query: $query,
174
+ query: $term,
156
175
  types: [PAGE],
157
- first: 10
176
+ first: $first,
158
177
  ) {
159
178
  nodes {
160
179
  ...on Page {
@@ -162,16 +181,236 @@ const SEARCH_QUERY = `#graphql
162
181
  }
163
182
  }
164
183
  }
165
- articles: search(
166
- query: $query,
167
- types: [ARTICLE],
168
- first: 10
184
+ products: search(
185
+ after: $endCursor,
186
+ before: $startCursor,
187
+ first: $first,
188
+ last: $last,
189
+ query: $term,
190
+ sortKey: RELEVANCE,
191
+ types: [PRODUCT],
192
+ unavailableProducts: HIDE,
169
193
  ) {
170
194
  nodes {
171
- ...on Article {
172
- ...SearchArticle
195
+ ...on Product {
196
+ ...SearchProduct
173
197
  }
174
198
  }
199
+ pageInfo {
200
+ ...PageInfoFragment
201
+ }
202
+ }
203
+ }
204
+ ${SEARCH_PRODUCT_FRAGMENT}
205
+ ${SEARCH_PAGE_FRAGMENT}
206
+ ${SEARCH_ARTICLE_FRAGMENT}
207
+ ${PAGE_INFO_FRAGMENT}
208
+ ` as const;
209
+
210
+ /**
211
+ * Regular search fetcher
212
+ */
213
+ async function regularSearch({
214
+ request,
215
+ context,
216
+ }: Pick<
217
+ LoaderFunctionArgs,
218
+ 'request' | 'context'
219
+ >): Promise<RegularSearchReturn> {
220
+ const {storefront} = context;
221
+ const url = new URL(request.url);
222
+ const variables = getPaginationVariables(request, {pageBy: 8});
223
+ const term = String(url.searchParams.get('q') || '');
224
+
225
+ // Search articles, pages, and products for the `q` term
226
+ const {errors, ...items} = await storefront.query(SEARCH_QUERY, {
227
+ variables: {...variables, term},
228
+ });
229
+
230
+ if (!items) {
231
+ throw new Error('No search data returned from Shopify API');
232
+ }
233
+
234
+ const total = Object.values(items).reduce(
235
+ (acc, {nodes}) => acc + nodes.length,
236
+ 0,
237
+ );
238
+
239
+ const error = errors
240
+ ? errors.map(({message}) => message).join(', ')
241
+ : undefined;
242
+
243
+ return {type: 'regular', term, error, result: {total, items}};
244
+ }
245
+
246
+ /**
247
+ * Predictive search query and fragments
248
+ * (adjust as needed)
249
+ */
250
+ const PREDICTIVE_SEARCH_ARTICLE_FRAGMENT = `#graphql
251
+ fragment PredictiveArticle on Article {
252
+ __typename
253
+ id
254
+ title
255
+ handle
256
+ blog {
257
+ handle
175
258
  }
259
+ image {
260
+ url
261
+ altText
262
+ width
263
+ height
264
+ }
265
+ trackingParameters
176
266
  }
177
267
  ` as const;
268
+
269
+ const PREDICTIVE_SEARCH_COLLECTION_FRAGMENT = `#graphql
270
+ fragment PredictiveCollection on Collection {
271
+ __typename
272
+ id
273
+ title
274
+ handle
275
+ image {
276
+ url
277
+ altText
278
+ width
279
+ height
280
+ }
281
+ trackingParameters
282
+ }
283
+ ` as const;
284
+
285
+ const PREDICTIVE_SEARCH_PAGE_FRAGMENT = `#graphql
286
+ fragment PredictivePage on Page {
287
+ __typename
288
+ id
289
+ title
290
+ handle
291
+ trackingParameters
292
+ }
293
+ ` as const;
294
+
295
+ const PREDICTIVE_SEARCH_PRODUCT_FRAGMENT = `#graphql
296
+ fragment PredictiveProduct on Product {
297
+ __typename
298
+ id
299
+ title
300
+ handle
301
+ trackingParameters
302
+ variants(first: 1) {
303
+ nodes {
304
+ id
305
+ image {
306
+ url
307
+ altText
308
+ width
309
+ height
310
+ }
311
+ price {
312
+ amount
313
+ currencyCode
314
+ }
315
+ }
316
+ }
317
+ }
318
+ ` as const;
319
+
320
+ const PREDICTIVE_SEARCH_QUERY_FRAGMENT = `#graphql
321
+ fragment PredictiveQuery on SearchQuerySuggestion {
322
+ __typename
323
+ text
324
+ styledText
325
+ trackingParameters
326
+ }
327
+ ` as const;
328
+
329
+ // NOTE: https://shopify.dev/docs/api/storefront/latest/queries/predictiveSearch
330
+ const PREDICTIVE_SEARCH_QUERY = `#graphql
331
+ query PredictiveSearch(
332
+ $country: CountryCode
333
+ $language: LanguageCode
334
+ $limit: Int!
335
+ $limitScope: PredictiveSearchLimitScope!
336
+ $term: String!
337
+ $types: [PredictiveSearchType!]
338
+ ) @inContext(country: $country, language: $language) {
339
+ predictiveSearch(
340
+ limit: $limit,
341
+ limitScope: $limitScope,
342
+ query: $term,
343
+ types: $types,
344
+ ) {
345
+ articles {
346
+ ...PredictiveArticle
347
+ }
348
+ collections {
349
+ ...PredictiveCollection
350
+ }
351
+ pages {
352
+ ...PredictivePage
353
+ }
354
+ products {
355
+ ...PredictiveProduct
356
+ }
357
+ queries {
358
+ ...PredictiveQuery
359
+ }
360
+ }
361
+ }
362
+ ${PREDICTIVE_SEARCH_ARTICLE_FRAGMENT}
363
+ ${PREDICTIVE_SEARCH_COLLECTION_FRAGMENT}
364
+ ${PREDICTIVE_SEARCH_PAGE_FRAGMENT}
365
+ ${PREDICTIVE_SEARCH_PRODUCT_FRAGMENT}
366
+ ${PREDICTIVE_SEARCH_QUERY_FRAGMENT}
367
+ ` as const;
368
+
369
+ /**
370
+ * Predictive search fetcher
371
+ */
372
+ async function predictiveSearch({
373
+ request,
374
+ context,
375
+ }: Pick<
376
+ ActionFunctionArgs,
377
+ 'request' | 'context'
378
+ >): Promise<PredictiveSearchReturn> {
379
+ const {storefront} = context;
380
+ const url = new URL(request.url);
381
+ const term = String(url.searchParams.get('q') || '').trim();
382
+ const limit = Number(url.searchParams.get('limit') || 10);
383
+ const type = 'predictive';
384
+
385
+ if (!term) return {type, term, result: getEmptyPredictiveSearchResult()};
386
+
387
+ // Predictively search articles, collections, pages, products, and queries (suggestions)
388
+ const {predictiveSearch: items, errors} = await storefront.query(
389
+ PREDICTIVE_SEARCH_QUERY,
390
+ {
391
+ variables: {
392
+ // customize search options as needed
393
+ limit,
394
+ limitScope: 'EACH',
395
+ term,
396
+ },
397
+ },
398
+ );
399
+
400
+ if (errors) {
401
+ throw new Error(
402
+ `Shopify API errors: ${errors.map(({message}) => message).join(', ')}`,
403
+ );
404
+ }
405
+
406
+ if (!items) {
407
+ throw new Error('No predictive search data returned from Shopify API');
408
+ }
409
+
410
+ const total = Object.values(items).reduce(
411
+ (acc, item) => acc + item.length,
412
+ 0,
413
+ );
414
+
415
+ return {type, term, result: {items, total}};
416
+ }
@@ -1,6 +1,16 @@
1
1
  body {
2
- font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
3
- Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
2
+ font-family:
3
+ system-ui,
4
+ -apple-system,
5
+ BlinkMacSystemFont,
6
+ 'Segoe UI',
7
+ Roboto,
8
+ Oxygen,
9
+ Ubuntu,
10
+ Cantarell,
11
+ 'Open Sans',
12
+ 'Helvetica Neue',
13
+ sans-serif;
4
14
  margin: 0;
5
15
  padding: 0;
6
16
  }
@@ -6,12 +6,11 @@
6
6
  import '@total-typescript/ts-reset';
7
7
 
8
8
  import type {
9
- Storefront,
10
- CustomerAccount,
11
- HydrogenCart,
9
+ HydrogenContext,
12
10
  HydrogenSessionData,
11
+ HydrogenEnv,
13
12
  } from '@shopify/hydrogen';
14
- import type {AppSession} from '~/lib/session';
13
+ import type {createAppLoadContext} from '~/lib/context';
15
14
 
16
15
  declare global {
17
16
  /**
@@ -19,36 +18,18 @@ declare global {
19
18
  */
20
19
  const process: {env: {NODE_ENV: 'production' | 'development'}};
21
20
 
22
- /**
23
- * Declare expected Env parameter in fetch handler.
24
- */
25
- interface Env {
26
- SESSION_SECRET: string;
27
- PUBLIC_STOREFRONT_API_TOKEN: string;
28
- PRIVATE_STOREFRONT_API_TOKEN: string;
29
- PUBLIC_STORE_DOMAIN: string;
30
- PUBLIC_STOREFRONT_ID: string;
31
- PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID: string;
32
- PUBLIC_CUSTOMER_ACCOUNT_API_URL: string;
33
- PUBLIC_CHECKOUT_DOMAIN: string;
21
+ interface Env extends HydrogenEnv {
22
+ // declare additional Env parameter use in the fetch handler and Remix loader context here
34
23
  }
35
24
  }
36
25
 
37
26
  declare module '@shopify/remix-oxygen' {
38
- /**
39
- * Declare local additions to the Remix loader context.
40
- */
41
- interface AppLoadContext {
42
- env: Env;
43
- cart: HydrogenCart;
44
- storefront: Storefront;
45
- customerAccount: CustomerAccount;
46
- session: AppSession;
47
- waitUntil: ExecutionContext['waitUntil'];
27
+ interface AppLoadContext
28
+ extends Awaited<ReturnType<typeof createAppLoadContext>> {
29
+ // to change context type, change the return of createAppLoadContext() instead
48
30
  }
49
31
 
50
- /**
51
- * Declare local additions to the Remix session data.
52
- */
53
- interface SessionData extends HydrogenSessionData {}
32
+ interface SessionData extends HydrogenSessionData {
33
+ // declare local additions to the Remix session data here
34
+ }
54
35
  }