@shopify/create-hydrogen 5.0.28 → 5.0.30

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 (67) hide show
  1. package/dist/acorn-CAM24XUM.js +17 -0
  2. package/dist/angular-DNP7YRKH.js +5 -0
  3. package/dist/assets/hydrogen/starter/CHANGELOG.md +47 -0
  4. package/dist/assets/hydrogen/starter/app/components/Aside.tsx +4 -2
  5. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +6 -3
  6. package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +117 -23
  7. package/dist/assets/hydrogen/starter/app/components/Header.tsx +3 -3
  8. package/dist/assets/hydrogen/starter/app/components/MockShopNotice.tsx +20 -0
  9. package/dist/assets/hydrogen/starter/app/entry.client.tsx +2 -3
  10. package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +14 -53
  11. package/dist/assets/hydrogen/starter/app/routes/_index.tsx +15 -9
  12. package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +1 -1
  13. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +13 -13
  14. package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +1 -1
  15. package/dist/assets/hydrogen/starter/app/routes/account.tsx +4 -7
  16. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +2 -9
  17. package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +1 -4
  18. package/dist/assets/hydrogen/starter/app/routes/cart.tsx +1 -5
  19. package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +1 -3
  20. package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +2 -8
  21. package/dist/assets/hydrogen/starter/app/routes/policies.$handle.tsx +1 -4
  22. package/dist/assets/hydrogen/starter/app/routes/policies._index.tsx +1 -1
  23. package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +2 -9
  24. package/dist/assets/hydrogen/starter/app/routes/search.tsx +18 -12
  25. package/dist/assets/hydrogen/starter/app/styles/app.css +51 -0
  26. package/dist/assets/hydrogen/starter/guides/predictiveSearch/predictiveSearch.md +3 -3
  27. package/dist/assets/hydrogen/starter/guides/search/search.md +1 -1
  28. package/dist/assets/hydrogen/starter/package.json +10 -10
  29. package/dist/assets/hydrogen/starter/vite.config.ts +1 -1
  30. package/dist/assets/hydrogen/vite/vite.config.js +1 -1
  31. package/dist/babel-Q7WGFTYC.js +16 -0
  32. package/dist/chunk-6JMCCVMB.js +1178 -0
  33. package/dist/chunk-AMAQOLRU.js +51 -0
  34. package/dist/{chunk-RUCJI22O.js → chunk-FOFKID43.js} +1 -1
  35. package/dist/{chunk-6YUUFKYO.js → chunk-KP5HTXEK.js} +1 -1
  36. package/dist/{chunk-Y5VZE2FH.js → chunk-MLIZM3D3.js} +1 -1
  37. package/dist/{chunk-VXJIQGAB.js → chunk-SOYTF5NM.js} +1 -1
  38. package/dist/chunk-SSCFXJTG.js +2 -0
  39. package/dist/chunk-TDF32DPQ.js +5 -0
  40. package/dist/create-app.js +185 -934
  41. package/dist/{del-VDYQZFAQ.js → del-SDQ4S47S.js} +1 -1
  42. package/dist/devtools-RYOXA5H7.js +8 -0
  43. package/dist/error-handler-SA6VDSNI.js +2 -0
  44. package/dist/estree-V6MYQTSR.js +45 -0
  45. package/dist/flow-6QBD7VFZ.js +21 -0
  46. package/dist/glimmer-F25SYUSG.js +38 -0
  47. package/dist/graphql-DIAYZHIY.js +29 -0
  48. package/dist/html-AE7KHAJ7.js +26 -0
  49. package/dist/{is-wsl-52AELLDM.js → is-wsl-7NORHMI2.js} +1 -1
  50. package/dist/markdown-W7E2MOQ2.js +63 -0
  51. package/dist/meriyah-37WUJX22.js +6 -0
  52. package/dist/{morph-S2LU6PQ4.js → morph-IIMCOPW3.js} +114 -114
  53. package/dist/{multipart-parser-MX4R5XJM.js → multipart-parser-JT5WYXDW.js} +1 -1
  54. package/dist/open-VLBBYVPL.js +2 -0
  55. package/dist/{out-U7AI7XUQ.js → out-YA75DDJD.js} +1 -1
  56. package/dist/postcss-7X2D3S46.js +62 -0
  57. package/dist/typescript-AXPPFPUX.js +22 -0
  58. package/dist/yaml-U74YVCK4.js +160 -0
  59. package/package.json +10 -9
  60. package/dist/chunk-AUULK6IN.js +0 -5
  61. package/dist/chunk-CJKPLQJ7.js +0 -51
  62. package/dist/chunk-YKN6SEJT.js +0 -1189
  63. package/dist/chunk-ZLNTSIDN.js +0 -2
  64. package/dist/devtools-3BYEW2L2.js +0 -8
  65. package/dist/error-handler-2JZDGPAN.js +0 -2
  66. package/dist/open-PMJ32HTM.js +0 -2
  67. /package/dist/{chunk-LBUW5UHX.js → chunk-Y52UPHAG.js} +0 -0
@@ -136,7 +136,7 @@ function SearchToggle() {
136
136
  );
137
137
  }
138
138
 
139
- function CartBadge({count}: {count: number | null}) {
139
+ function CartBadge({count}: {count: number}) {
140
140
  const {open} = useAside();
141
141
  const {publish, shop, cart, prevCart} = useAnalytics();
142
142
 
@@ -154,14 +154,14 @@ function CartBadge({count}: {count: number | null}) {
154
154
  } as CartViewPayload);
155
155
  }}
156
156
  >
157
- Cart {count === null ? <span>&nbsp;</span> : count}
157
+ Cart <span aria-label={`(items: ${count})`}>{count}</span>
158
158
  </a>
159
159
  );
160
160
  }
161
161
 
162
162
  function CartToggle({cart}: Pick<HeaderProps, 'cart'>) {
163
163
  return (
164
- <Suspense fallback={<CartBadge count={null} />}>
164
+ <Suspense fallback={<CartBadge count={0} />}>
165
165
  <Await resolve={cart}>
166
166
  <CartBanner />
167
167
  </Await>
@@ -0,0 +1,20 @@
1
+ export function MockShopNotice() {
2
+ return (
3
+ <section
4
+ className="mock-shop-notice"
5
+ aria-labelledby="mock-shop-notice-heading"
6
+ >
7
+ <div className="inner">
8
+ <h2 id="mock-shop-notice-heading">Welcome to Hydrogen!</h2>
9
+ <p>
10
+ You&rsquo;re seeing mocked products because no store is connected to
11
+ this project yet.
12
+ </p>
13
+ <p>
14
+ Link a store by running <code>npx shopify hydrogen link</code> in your
15
+ terminal.
16
+ </p>
17
+ </div>
18
+ </section>
19
+ );
20
+ }
@@ -6,9 +6,8 @@ import {NonceProvider} from '@shopify/hydrogen';
6
6
  if (!window.location.origin.includes('webcache.googleusercontent.com')) {
7
7
  startTransition(() => {
8
8
  // Extract nonce from existing script tags
9
- const existingNonce = document
10
- .querySelector<HTMLScriptElement>('script[nonce]')
11
- ?.nonce;
9
+ const existingNonce =
10
+ document.querySelector<HTMLScriptElement>('script[nonce]')?.nonce;
12
11
 
13
12
  hydrateRoot(
14
13
  document,
@@ -1,13 +1,8 @@
1
1
  import type {Route} from './+types/[robots.txt]';
2
- import {parseGid} from '@shopify/hydrogen';
3
2
 
4
- export async function loader({request, context}: Route.LoaderArgs) {
3
+ export function loader({request}: Route.LoaderArgs) {
5
4
  const url = new URL(request.url);
6
-
7
- const {shop} = await context.storefront.query(ROBOTS_QUERY);
8
-
9
- const shopId = parseGid(shop.id).id;
10
- const body = robotsTxtData({url: url.origin, shopId});
5
+ const body = robotsTxtData({url: url.origin});
11
6
 
12
7
  return new Response(body, {
13
8
  status: 200,
@@ -19,35 +14,31 @@ export async function loader({request, context}: Route.LoaderArgs) {
19
14
  });
20
15
  }
21
16
 
22
- function robotsTxtData({url, shopId}: {shopId?: string; url?: string}) {
17
+ function robotsTxtData({url}: {url?: string}) {
23
18
  const sitemapUrl = url ? `${url}/sitemap.xml` : undefined;
24
19
 
25
20
  return `
26
21
  User-agent: *
27
- ${generalDisallowRules({sitemapUrl, shopId})}
22
+ ${generalDisallowRules({sitemapUrl})}
28
23
 
29
24
  # Google adsbot ignores robots.txt unless specifically named!
30
25
  User-agent: adsbot-google
31
- Disallow: /checkouts/
32
- Disallow: /checkout
33
- Disallow: /carts
34
- Disallow: /orders
35
- ${shopId ? `Disallow: /${shopId}/checkouts` : ''}
36
- ${shopId ? `Disallow: /${shopId}/orders` : ''}
37
- Disallow: /*?*oseid=*
38
- Disallow: /*preview_theme_id*
39
- Disallow: /*preview_script_id*
26
+ Disallow: /cart
27
+ Disallow: /account
28
+ Disallow: /search
29
+ Allow: /search/
30
+ Disallow: /search/?*
40
31
 
41
32
  User-agent: Nutch
42
33
  Disallow: /
43
34
 
44
35
  User-agent: AhrefsBot
45
36
  Crawl-delay: 10
46
- ${generalDisallowRules({sitemapUrl, shopId})}
37
+ ${generalDisallowRules({sitemapUrl})}
47
38
 
48
39
  User-agent: AhrefsSiteAudit
49
40
  Crawl-delay: 10
50
- ${generalDisallowRules({sitemapUrl, shopId})}
41
+ ${generalDisallowRules({sitemapUrl})}
51
42
 
52
43
  User-agent: MJ12bot
53
44
  Crawl-Delay: 10
@@ -61,21 +52,8 @@ Crawl-delay: 1
61
52
  * This function generates disallow rules that generally follow what Shopify's
62
53
  * Online Store has as defaults for their robots.txt
63
54
  */
64
- function generalDisallowRules({
65
- shopId,
66
- sitemapUrl,
67
- }: {
68
- shopId?: string;
69
- sitemapUrl?: string;
70
- }) {
71
- return `Disallow: /admin
72
- Disallow: /cart
73
- Disallow: /orders
74
- Disallow: /checkouts/
75
- Disallow: /checkout
76
- ${shopId ? `Disallow: /${shopId}/checkouts` : ''}
77
- ${shopId ? `Disallow: /${shopId}/orders` : ''}
78
- Disallow: /carts
55
+ function generalDisallowRules({sitemapUrl}: {sitemapUrl?: string}) {
56
+ return `Disallow: /cart
79
57
  Disallow: /account
80
58
  Disallow: /collections/*sort_by*
81
59
  Disallow: /*/collections/*sort_by*
@@ -85,33 +63,16 @@ Disallow: /collections/*%2b*
85
63
  Disallow: /*/collections/*+*
86
64
  Disallow: /*/collections/*%2B*
87
65
  Disallow: /*/collections/*%2b*
88
- Disallow: */collections/*filter*&*filter*
66
+ Disallow: /*/collections/*filter*&*filter*
89
67
  Disallow: /blogs/*+*
90
68
  Disallow: /blogs/*%2B*
91
69
  Disallow: /blogs/*%2b*
92
70
  Disallow: /*/blogs/*+*
93
71
  Disallow: /*/blogs/*%2B*
94
72
  Disallow: /*/blogs/*%2b*
95
- Disallow: /*?*oseid=*
96
- Disallow: /*preview_theme_id*
97
- Disallow: /*preview_script_id*
98
73
  Disallow: /policies/
99
- Disallow: /*/*?*ls=*&ls=*
100
- Disallow: /*/*?*ls%3D*%3Fls%3D*
101
- Disallow: /*/*?*ls%3d*%3fls%3d*
102
74
  Disallow: /search
103
75
  Allow: /search/
104
76
  Disallow: /search/?*
105
- Disallow: /apple-app-site-association
106
- Disallow: /.well-known/shopify/monorail
107
77
  ${sitemapUrl ? `Sitemap: ${sitemapUrl}` : ''}`;
108
78
  }
109
-
110
- const ROBOTS_QUERY = `#graphql
111
- query StoreRobots($country: CountryCode, $language: LanguageCode)
112
- @inContext(country: $country, language: $language) {
113
- shop {
114
- id
115
- }
116
- }
117
- ` as const;
@@ -1,8 +1,4 @@
1
- import {
2
- Await,
3
- useLoaderData,
4
- Link,
5
- } from 'react-router';
1
+ import {Await, useLoaderData, Link} from 'react-router';
6
2
  import type {Route} from './+types/_index';
7
3
  import {Suspense} from 'react';
8
4
  import {Image} from '@shopify/hydrogen';
@@ -11,6 +7,7 @@ import type {
11
7
  RecommendedProductsQuery,
12
8
  } from 'storefrontapi.generated';
13
9
  import {ProductItem} from '~/components/ProductItem';
10
+ import {MockShopNotice} from '~/components/MockShopNotice';
14
11
 
15
12
  export const meta: Route.MetaFunction = () => {
16
13
  return [{title: 'Hydrogen | Home'}];
@@ -37,6 +34,7 @@ async function loadCriticalData({context}: Route.LoaderArgs) {
37
34
  ]);
38
35
 
39
36
  return {
37
+ isShopLinked: Boolean(context.env.PUBLIC_STORE_DOMAIN),
40
38
  featuredCollection: collections.nodes[0],
41
39
  };
42
40
  }
@@ -64,6 +62,7 @@ export default function Homepage() {
64
62
  const data = useLoaderData<typeof loader>();
65
63
  return (
66
64
  <div className="home">
65
+ {data.isShopLinked ? null : <MockShopNotice />}
67
66
  <FeaturedCollection collection={data.featuredCollection} />
68
67
  <RecommendedProducts products={data.recommendedProducts} />
69
68
  </div>
@@ -84,7 +83,11 @@ function FeaturedCollection({
84
83
  >
85
84
  {image && (
86
85
  <div className="featured-collection-image">
87
- <Image data={image} sizes="100vw" />
86
+ <Image
87
+ data={image}
88
+ sizes="100vw"
89
+ alt={image.altText || collection.title}
90
+ />
88
91
  </div>
89
92
  )}
90
93
  <h1>{collection.title}</h1>
@@ -98,8 +101,11 @@ function RecommendedProducts({
98
101
  products: Promise<RecommendedProductsQuery | null>;
99
102
  }) {
100
103
  return (
101
- <div className="recommended-products">
102
- <h2>Recommended Products</h2>
104
+ <section
105
+ className="recommended-products"
106
+ aria-labelledby="recommended-products"
107
+ >
108
+ <h2 id="recommended-products">Recommended Products</h2>
103
109
  <Suspense fallback={<div>Loading...</div>}>
104
110
  <Await resolve={products}>
105
111
  {(response) => (
@@ -114,7 +120,7 @@ function RecommendedProducts({
114
120
  </Await>
115
121
  </Suspense>
116
122
  <br />
117
- </div>
123
+ </section>
118
124
  );
119
125
  }
120
126
 
@@ -3,7 +3,7 @@ import type {Route} from './+types/account.$';
3
3
 
4
4
  // fallback wild card for all unauthenticated routes in account section
5
5
  export async function loader({context}: Route.LoaderArgs) {
6
- context.customerAccount.handleAuthStatus();
6
+ await context.customerAccount.handleAuthStatus();
7
7
 
8
8
  return redirect('/account');
9
9
  }
@@ -32,7 +32,7 @@ export const meta: Route.MetaFunction = () => {
32
32
  };
33
33
 
34
34
  export async function loader({context}: Route.LoaderArgs) {
35
- context.customerAccount.handleAuthStatus();
35
+ await context.customerAccount.handleAuthStatus();
36
36
 
37
37
  return {};
38
38
  }
@@ -264,23 +264,23 @@ export default function Addresses() {
264
264
  <div className="account-addresses">
265
265
  <h2>Addresses</h2>
266
266
  <br />
267
- {!addresses.nodes.length ? (
268
- <p>You have no addresses saved.</p>
269
- ) : (
267
+ <div>
270
268
  <div>
271
- <div>
272
- <legend>Create address</legend>
273
- <NewAddressForm />
274
- </div>
275
- <br />
276
- <hr />
277
- <br />
269
+ <legend>Create address</legend>
270
+ <NewAddressForm key={addresses.nodes.length} />
271
+ </div>
272
+ <br />
273
+ <hr />
274
+ <br />
275
+ {!addresses.nodes.length ? (
276
+ <p>You have no addresses saved.</p>
277
+ ) : (
278
278
  <ExistingAddresses
279
279
  addresses={addresses}
280
280
  defaultAddress={defaultAddress}
281
281
  />
282
- </div>
283
- )}
282
+ )}
283
+ </div>
284
284
  </div>
285
285
  );
286
286
  }
@@ -20,7 +20,7 @@ export const meta: Route.MetaFunction = () => {
20
20
  };
21
21
 
22
22
  export async function loader({context}: Route.LoaderArgs) {
23
- context.customerAccount.handleAuthStatus();
23
+ await context.customerAccount.handleAuthStatus();
24
24
 
25
25
  return {};
26
26
  }
@@ -14,14 +14,11 @@ export function shouldRevalidate() {
14
14
 
15
15
  export async function loader({context}: Route.LoaderArgs) {
16
16
  const {customerAccount} = context;
17
- const {data, errors} = await customerAccount.query(
18
- CUSTOMER_DETAILS_QUERY,
19
- {
20
- variables: {
21
- language: customerAccount.i18n.language,
22
- },
17
+ const {data, errors} = await customerAccount.query(CUSTOMER_DETAILS_QUERY, {
18
+ variables: {
19
+ language: customerAccount.i18n.language,
23
20
  },
24
- );
21
+ });
25
22
 
26
23
  if (errors?.length || !data?.customer) {
27
24
  throw new Error('Customer not found');
@@ -1,7 +1,4 @@
1
- import {
2
- Link,
3
- useLoaderData,
4
- } from 'react-router';
1
+ import {Link, useLoaderData} from 'react-router';
5
2
  import type {Route} from './+types/blogs.$blogHandle._index';
6
3
  import {Image, getPaginationVariables} from '@shopify/hydrogen';
7
4
  import type {ArticleItemFragment} from 'storefrontapi.generated';
@@ -26,11 +23,7 @@ export async function loader(args: Route.LoaderArgs) {
26
23
  * Load data necessary for rendering content above the fold. This is the critical data
27
24
  * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
28
25
  */
29
- async function loadCriticalData({
30
- context,
31
- request,
32
- params,
33
- }: Route.LoaderArgs) {
26
+ async function loadCriticalData({context, request, params}: Route.LoaderArgs) {
34
27
  const paginationVariables = getPaginationVariables(request, {
35
28
  pageBy: 4,
36
29
  });
@@ -1,7 +1,4 @@
1
- import {
2
- Link,
3
- useLoaderData,
4
- } from 'react-router';
1
+ import {Link, useLoaderData} from 'react-router';
5
2
  import type {Route} from './+types/blogs._index';
6
3
  import {getPaginationVariables} from '@shopify/hydrogen';
7
4
  import {PaginatedResourceSection} from '~/components/PaginatedResourceSection';
@@ -1,8 +1,4 @@
1
- import {
2
- useLoaderData,
3
- data,
4
- type HeadersFunction,
5
- } from 'react-router';
1
+ import {useLoaderData, data, type HeadersFunction} from 'react-router';
6
2
  import type {Route} from './+types/cart';
7
3
  import type {CartQueryDataReturn} from '@shopify/hydrogen';
8
4
  import {CartForm} from '@shopify/hydrogen';
@@ -1,7 +1,5 @@
1
1
  import type {Route} from './+types/collections.all';
2
- import {
3
- useLoaderData,
4
- } from 'react-router';
2
+ import {useLoaderData} from 'react-router';
5
3
  import {getPaginationVariables, Image, Money} from '@shopify/hydrogen';
6
4
  import {PaginatedResourceSection} from '~/components/PaginatedResourceSection';
7
5
  import {ProductItem} from '~/components/ProductItem';
@@ -1,6 +1,4 @@
1
- import {
2
- useLoaderData,
3
- } from 'react-router';
1
+ import {useLoaderData} from 'react-router';
4
2
  import type {Route} from './+types/pages.$handle';
5
3
  import {redirectIfHandleIsLocalized} from '~/lib/redirect';
6
4
 
@@ -22,11 +20,7 @@ export async function loader(args: Route.LoaderArgs) {
22
20
  * Load data necessary for rendering content above the fold. This is the critical data
23
21
  * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
24
22
  */
25
- async function loadCriticalData({
26
- context,
27
- request,
28
- params,
29
- }: Route.LoaderArgs) {
23
+ async function loadCriticalData({context, request, params}: Route.LoaderArgs) {
30
24
  if (!params.handle) {
31
25
  throw new Error('Missing page handle');
32
26
  }
@@ -1,7 +1,4 @@
1
- import {
2
- Link,
3
- useLoaderData,
4
- } from 'react-router';
1
+ import {Link, useLoaderData} from 'react-router';
5
2
  import type {Route} from './+types/policies.$handle';
6
3
  import {type Shop} from '@shopify/hydrogen/storefront-api-types';
7
4
 
@@ -4,7 +4,7 @@ import type {PoliciesQuery, PolicyItemFragment} from 'storefrontapi.generated';
4
4
 
5
5
  export async function loader({context}: Route.LoaderArgs) {
6
6
  const data: PoliciesQuery = await context.storefront.query(POLICIES_QUERY);
7
-
7
+
8
8
  const shopPolicies = data.shop;
9
9
  const policies: PolicyItemFragment[] = [
10
10
  shopPolicies?.privacyPolicy,
@@ -1,7 +1,4 @@
1
- import {
2
- redirect,
3
- useLoaderData,
4
- } from 'react-router';
1
+ import {redirect, useLoaderData} from 'react-router';
5
2
  import type {Route} from './+types/products.$handle';
6
3
  import {
7
4
  getSelectedProductOptions,
@@ -40,11 +37,7 @@ export async function loader(args: Route.LoaderArgs) {
40
37
  * Load data necessary for rendering content above the fold. This is the critical data
41
38
  * needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
42
39
  */
43
- async function loadCriticalData({
44
- context,
45
- params,
46
- request,
47
- }: Route.LoaderArgs) {
40
+ async function loadCriticalData({context, params, request}: Route.LoaderArgs) {
48
41
  const {handle} = params;
49
42
  const {storefront} = context;
50
43
 
@@ -1,6 +1,4 @@
1
- import {
2
- useLoaderData,
3
- } from 'react-router';
1
+ import {useLoaderData} from 'react-router';
4
2
  import type {Route} from './+types/search';
5
3
  import {getPaginationVariables, Analytics} from '@shopify/hydrogen';
6
4
  import {SearchForm} from '~/components/SearchForm';
@@ -10,7 +8,10 @@ import {
10
8
  type PredictiveSearchReturn,
11
9
  getEmptyPredictiveSearchResult,
12
10
  } from '~/lib/search';
13
- import type {RegularSearchQuery, PredictiveSearchQuery} from 'storefrontapi.generated';
11
+ import type {
12
+ RegularSearchQuery,
13
+ PredictiveSearchQuery,
14
+ } from 'storefrontapi.generated';
14
15
 
15
16
  export const meta: Route.MetaFunction = () => {
16
17
  return [{title: `Hydrogen | Search`}];
@@ -225,9 +226,13 @@ async function regularSearch({
225
226
  const term = String(url.searchParams.get('q') || '');
226
227
 
227
228
  // Search articles, pages, and products for the `q` term
228
- const {errors, ...items}: {errors?: Array<{message: string}>} & RegularSearchQuery = await storefront.query(SEARCH_QUERY, {
229
- variables: {...variables, term},
230
- });
229
+ const {
230
+ errors,
231
+ ...items
232
+ }: {errors?: Array<{message: string}>} & RegularSearchQuery =
233
+ await storefront.query(SEARCH_QUERY, {
234
+ variables: {...variables, term},
235
+ });
231
236
 
232
237
  if (!items) {
233
238
  throw new Error('No search data returned from Shopify API');
@@ -389,17 +394,18 @@ async function predictiveSearch({
389
394
  if (!term) return {type, term, result: getEmptyPredictiveSearchResult()};
390
395
 
391
396
  // Predictively search articles, collections, pages, products, and queries (suggestions)
392
- const {predictiveSearch: items, errors}: PredictiveSearchQuery & {errors?: Array<{message: string}>} = await storefront.query(
393
- PREDICTIVE_SEARCH_QUERY,
394
- {
397
+ const {
398
+ predictiveSearch: items,
399
+ errors,
400
+ }: PredictiveSearchQuery & {errors?: Array<{message: string}>} =
401
+ await storefront.query(PREDICTIVE_SEARCH_QUERY, {
395
402
  variables: {
396
403
  // customize search options as needed
397
404
  limit,
398
405
  limitScope: 'EACH',
399
406
  term,
400
407
  },
401
- },
402
- );
408
+ });
403
409
 
404
410
  if (errors) {
405
411
  throw new Error(
@@ -22,6 +22,57 @@ img {
22
22
  cursor: pointer;
23
23
  }
24
24
 
25
+ /*
26
+ * --------------------------------------------------
27
+ * components/MockShopNotice
28
+ * --------------------------------------------------
29
+ */
30
+ .mock-shop-notice {
31
+ background: var(--color-light);
32
+ border: 1px solid var(--color-dark);
33
+ border-left-width: 0.5rem;
34
+ margin: 1rem 0;
35
+ padding: 0;
36
+
37
+ .inner {
38
+ padding: 0.875rem 1rem;
39
+ }
40
+
41
+ h2 {
42
+ font-size: 1.6rem;
43
+ font-weight: 700;
44
+ line-height: 1.4;
45
+ }
46
+
47
+ p {
48
+ font-size: 1rem;
49
+ line-height: 1.4;
50
+ margin-bottom: 0.5rem;
51
+ }
52
+
53
+ footer {
54
+ background: rgba(0, 0, 0, 0.06);
55
+ padding: 0.5rem 1rem;
56
+ }
57
+
58
+ .small {
59
+ font-size: 0.875rem;
60
+ line-height: 1.4;
61
+ margin-bottom: 0.5rem;
62
+ }
63
+
64
+ code {
65
+ background: rgba(0, 0, 0, 0.06);
66
+ border-radius: 0.25rem;
67
+ padding: 0.1em 0.3em;
68
+ }
69
+
70
+ a {
71
+ text-decoration: underline;
72
+ text-underline-offset: 6px;
73
+ }
74
+ }
75
+
25
76
  /*
26
77
  * --------------------------------------------------
27
78
  * components/Aside
@@ -15,7 +15,7 @@ This integration uses the storefront API (SFAPI) [predictiveSearch](https://shop
15
15
 
16
16
  | File | Description |
17
17
  | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
18
- | [`app/components/SearchFormPredictive.tsx`](../../app/components/SearchFormPredictive.tsx) | A fully customizable form component configured to make form `GET` requests to the `/search` route. |
18
+ | [`app/components/SearchFormPredictive.tsx`](../../app/components/SearchFormPredictive.tsx) | A fully customizable form component configured to make form `GET` requests to the `/search` route. |
19
19
  | [`app/components/SearchResultsPredictive.tsx`](../../app/components/SearchResultsPredictive.tsx) | A fully customizable search results wrapper, that provides compound components to render `articles`, `pages`, `products`, `collections` and `queries`. |
20
20
 
21
21
  ## Instructions
@@ -217,10 +217,10 @@ export async function loader({request, context}: LoaderFunctionArgs) {
217
217
  const isPredictive = url.searchParams.has('predictive');
218
218
 
219
219
  if (!isPredictive) {
220
- return {}
220
+ return {};
221
221
  }
222
222
 
223
- const searchPromise = predictiveSearch({request, context})
223
+ const searchPromise = predictiveSearch({request, context});
224
224
 
225
225
  searchPromise.catch((error: Error) => {
226
226
  console.error(error);
@@ -212,7 +212,7 @@ export async function loader({request, context}: LoaderFunctionArgs) {
212
212
  const isRegular = !url.searchParams.has('predictive');
213
213
 
214
214
  if (!isRegular) {
215
- return {}
215
+ return {};
216
216
  }
217
217
 
218
218
  const searchPromise = regularSearch({request, context});
@@ -2,7 +2,7 @@
2
2
  "name": "skeleton",
3
3
  "private": true,
4
4
  "sideEffects": false,
5
- "version": "2025.10.1",
5
+ "version": "2026.1.2",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "build": "shopify hydrogen build --codegen",
@@ -14,12 +14,12 @@
14
14
  },
15
15
  "prettier": "@shopify/prettier-config",
16
16
  "dependencies": {
17
- "@shopify/hydrogen": "2025.10.1",
17
+ "@shopify/hydrogen": "2026.1.2",
18
18
  "graphql": "^16.10.0",
19
19
  "graphql-tag": "^2.12.6",
20
20
  "isbot": "^5.1.22",
21
- "react": "18.3.1",
22
- "react-dom": "18.3.1",
21
+ "react": "^18.3.1",
22
+ "react-dom": "^18.3.1",
23
23
  "react-router": "7.12.0",
24
24
  "react-router-dom": "7.12.0"
25
25
  },
@@ -30,15 +30,15 @@
30
30
  "@graphql-codegen/cli": "5.0.2",
31
31
  "@react-router/dev": "7.12.0",
32
32
  "@react-router/fs-routes": "7.12.0",
33
- "@shopify/cli": "3.85.4",
34
- "@shopify/hydrogen-codegen": "^0.3.3",
35
- "@shopify/mini-oxygen": "^4.0.0",
33
+ "@shopify/cli": "3.91.1",
34
+ "@shopify/hydrogen-codegen": "0.3.3",
35
+ "@shopify/mini-oxygen": "4.0.1",
36
36
  "@shopify/oxygen-workers-types": "^4.1.6",
37
37
  "@shopify/prettier-config": "^1.1.2",
38
38
  "@total-typescript/ts-reset": "^0.6.1",
39
39
  "@types/eslint": "^9.6.1",
40
- "@types/react": "^18.2.22",
41
- "@types/react-dom": "^18.2.7",
40
+ "@types/react": "^18.3.28",
41
+ "@types/react-dom": "^18.3.7",
42
42
  "@typescript-eslint/eslint-plugin": "^8.21.0",
43
43
  "@typescript-eslint/parser": "^8.21.0",
44
44
  "eslint": "^9.18.0",
@@ -58,6 +58,6 @@
58
58
  "vite-tsconfig-paths": "^4.3.1"
59
59
  },
60
60
  "engines": {
61
- "node": ">=18.0.0"
61
+ "node": "^22 || ^24"
62
62
  }
63
63
  }
@@ -8,7 +8,7 @@ export default defineConfig({
8
8
  plugins: [hydrogen(), oxygen(), reactRouter(), tsconfigPaths()],
9
9
  build: {
10
10
  // Allow a strict Content-Security-Policy
11
- // withtout inlining assets as base64:
11
+ // without inlining assets as base64:
12
12
  assetsInlineLimit: 0,
13
13
  },
14
14
  ssr: {