@shopify/create-hydrogen 4.3.13 → 5.0.0

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 (127) hide show
  1. package/dist/assets/hydrogen/bundle/analyzer.html +2045 -0
  2. package/dist/assets/hydrogen/i18n/domains.ts +28 -0
  3. package/dist/assets/hydrogen/i18n/mock-i18n-types.ts +3 -0
  4. package/dist/assets/hydrogen/i18n/subdomains.ts +27 -0
  5. package/dist/assets/hydrogen/i18n/subfolders.ts +29 -0
  6. package/dist/assets/hydrogen/routes/locale-check.ts +16 -0
  7. package/dist/assets/hydrogen/starter/.eslintignore +5 -0
  8. package/dist/assets/hydrogen/starter/.eslintrc.cjs +19 -0
  9. package/dist/assets/hydrogen/starter/.graphqlrc.yml +12 -0
  10. package/dist/assets/hydrogen/starter/CHANGELOG.md +709 -0
  11. package/dist/assets/hydrogen/starter/README.md +45 -0
  12. package/dist/assets/hydrogen/starter/app/assets/favicon.svg +28 -0
  13. package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +37 -0
  14. package/dist/assets/hydrogen/starter/app/components/Aside.tsx +76 -0
  15. package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +150 -0
  16. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +68 -0
  17. package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +101 -0
  18. package/dist/assets/hydrogen/starter/app/components/Footer.tsx +129 -0
  19. package/dist/assets/hydrogen/starter/app/components/Header.tsx +230 -0
  20. package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +126 -0
  21. package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +80 -0
  22. package/dist/assets/hydrogen/starter/app/components/ProductImage.tsx +23 -0
  23. package/dist/assets/hydrogen/starter/app/components/ProductPrice.tsx +27 -0
  24. package/dist/assets/hydrogen/starter/app/components/Search.tsx +514 -0
  25. package/dist/assets/hydrogen/starter/app/entry.client.tsx +12 -0
  26. package/dist/assets/hydrogen/starter/app/entry.server.tsx +47 -0
  27. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerAddressMutations.ts +61 -0
  28. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +40 -0
  29. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrderQuery.ts +87 -0
  30. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +58 -0
  31. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +24 -0
  32. package/dist/assets/hydrogen/starter/app/lib/fragments.ts +174 -0
  33. package/dist/assets/hydrogen/starter/app/lib/search.ts +29 -0
  34. package/dist/assets/hydrogen/starter/app/lib/session.ts +72 -0
  35. package/dist/assets/hydrogen/starter/app/lib/variants.ts +46 -0
  36. package/dist/assets/hydrogen/starter/app/root.tsx +191 -0
  37. package/dist/assets/hydrogen/starter/app/routes/$.tsx +11 -0
  38. package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +118 -0
  39. package/dist/assets/hydrogen/starter/app/routes/[sitemap.xml].tsx +177 -0
  40. package/dist/assets/hydrogen/starter/app/routes/_index.tsx +182 -0
  41. package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +8 -0
  42. package/dist/assets/hydrogen/starter/app/routes/account._index.tsx +5 -0
  43. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +513 -0
  44. package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +195 -0
  45. package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +107 -0
  46. package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +136 -0
  47. package/dist/assets/hydrogen/starter/app/routes/account.tsx +88 -0
  48. package/dist/assets/hydrogen/starter/app/routes/account_.authorize.tsx +5 -0
  49. package/dist/assets/hydrogen/starter/app/routes/account_.login.tsx +5 -0
  50. package/dist/assets/hydrogen/starter/app/routes/account_.logout.tsx +10 -0
  51. package/dist/assets/hydrogen/starter/app/routes/api.predictive-search.tsx +318 -0
  52. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +113 -0
  53. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +188 -0
  54. package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +119 -0
  55. package/dist/assets/hydrogen/starter/app/routes/cart.$lines.tsx +69 -0
  56. package/dist/assets/hydrogen/starter/app/routes/cart.tsx +102 -0
  57. package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +225 -0
  58. package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +146 -0
  59. package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +185 -0
  60. package/dist/assets/hydrogen/starter/app/routes/discount.$code.tsx +47 -0
  61. package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +84 -0
  62. package/dist/assets/hydrogen/starter/app/routes/policies.$handle.tsx +93 -0
  63. package/dist/assets/hydrogen/starter/app/routes/policies._index.tsx +63 -0
  64. package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +299 -0
  65. package/dist/assets/hydrogen/starter/app/routes/search.tsx +177 -0
  66. package/dist/assets/hydrogen/starter/app/styles/app.css +486 -0
  67. package/dist/assets/hydrogen/starter/app/styles/reset.css +129 -0
  68. package/dist/assets/hydrogen/starter/customer-accountapi.generated.d.ts +509 -0
  69. package/dist/assets/hydrogen/starter/env.d.ts +54 -0
  70. package/dist/assets/hydrogen/starter/package.json +50 -0
  71. package/dist/assets/hydrogen/starter/public/.gitkeep +0 -0
  72. package/dist/assets/hydrogen/starter/server.ts +119 -0
  73. package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +1211 -0
  74. package/dist/assets/hydrogen/starter/tsconfig.json +23 -0
  75. package/dist/assets/hydrogen/starter/vite.config.ts +41 -0
  76. package/dist/assets/hydrogen/tailwind/package.json +8 -0
  77. package/dist/assets/hydrogen/tailwind/tailwind.css +6 -0
  78. package/dist/assets/hydrogen/vanilla-extract/package.json +8 -0
  79. package/dist/assets/hydrogen/virtual-routes/assets/debug-network.css +592 -0
  80. package/dist/assets/hydrogen/virtual-routes/assets/favicon-dark.svg +20 -0
  81. package/dist/assets/hydrogen/virtual-routes/assets/favicon.svg +28 -0
  82. package/dist/assets/hydrogen/virtual-routes/assets/inter-variable-font.woff2 +0 -0
  83. package/dist/assets/hydrogen/virtual-routes/assets/jetbrainsmono-variable-font.woff2 +0 -0
  84. package/dist/assets/hydrogen/virtual-routes/assets/styles.css +238 -0
  85. package/dist/assets/hydrogen/virtual-routes/components/FlameChartWrapper.jsx +123 -0
  86. package/dist/assets/hydrogen/virtual-routes/components/HydrogenLogoBaseBW.jsx +32 -0
  87. package/dist/assets/hydrogen/virtual-routes/components/HydrogenLogoBaseColor.jsx +47 -0
  88. package/dist/assets/hydrogen/virtual-routes/components/IconBanner.jsx +292 -0
  89. package/dist/assets/hydrogen/virtual-routes/components/IconClose.jsx +38 -0
  90. package/dist/assets/hydrogen/virtual-routes/components/IconDiscard.jsx +44 -0
  91. package/dist/assets/hydrogen/virtual-routes/components/IconError.jsx +61 -0
  92. package/dist/assets/hydrogen/virtual-routes/components/IconGithub.jsx +23 -0
  93. package/dist/assets/hydrogen/virtual-routes/components/IconTwitter.jsx +21 -0
  94. package/dist/assets/hydrogen/virtual-routes/components/PageLayout.jsx +7 -0
  95. package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +178 -0
  96. package/dist/assets/hydrogen/virtual-routes/components/RequestTable.jsx +91 -0
  97. package/dist/assets/hydrogen/virtual-routes/components/RequestWaterfall.jsx +151 -0
  98. package/dist/assets/hydrogen/virtual-routes/lib/useDebugNetworkServer.jsx +178 -0
  99. package/dist/assets/hydrogen/virtual-routes/routes/graphiql.jsx +5 -0
  100. package/dist/assets/hydrogen/virtual-routes/routes/index.jsx +265 -0
  101. package/dist/assets/hydrogen/virtual-routes/routes/subrequest-profiler.jsx +243 -0
  102. package/dist/assets/hydrogen/virtual-routes/virtual-root.jsx +64 -0
  103. package/dist/assets/hydrogen/vite/package.json +14 -0
  104. package/dist/assets/hydrogen/vite/vite.config.js +41 -0
  105. package/dist/chokidar-2CKIHN27.js +12 -0
  106. package/dist/chunk-EO6F7WJJ.js +2 -0
  107. package/dist/chunk-FB327AH7.js +5 -0
  108. package/dist/chunk-FJPX4XUR.js +2 -0
  109. package/dist/chunk-JKOXGRAA.js +10 -0
  110. package/dist/chunk-LNQWGFTB.js +45 -0
  111. package/dist/chunk-M6JXYI3V.js +23 -0
  112. package/dist/chunk-MNT4XW23.js +2 -0
  113. package/dist/chunk-N7HFZHSO.js +1145 -0
  114. package/dist/chunk-PMDMUCNY.js +2 -0
  115. package/dist/chunk-QGLB6FFL.js +3 -0
  116. package/dist/chunk-VMIOG46Y.js +2 -0
  117. package/dist/create-app.js +1867 -34
  118. package/dist/del-CZGKV5SQ.js +11 -0
  119. package/dist/devtools-ZCRGQE64.js +8 -0
  120. package/dist/error-handler-GEQXZJ25.js +2 -0
  121. package/dist/lib-NJYCLW6W.js +22 -0
  122. package/dist/morph-ZJCCGFNC.js +30499 -0
  123. package/dist/multipart-parser-6HGDQWV7.js +3 -0
  124. package/dist/open-OD6DRFEG.js +2 -0
  125. package/dist/out-7KAQXZLP.js +2 -0
  126. package/dist/yoga.wasm +0 -0
  127. package/package.json +7 -3
@@ -0,0 +1,118 @@
1
+ import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
+ import {useRouteError, isRouteErrorResponse} from '@remix-run/react';
3
+ import {parseGid} from '@shopify/hydrogen';
4
+
5
+ export async function loader({request, context}: LoaderFunctionArgs) {
6
+ const url = new URL(request.url);
7
+
8
+ const {shop} = await context.storefront.query(ROBOTS_QUERY);
9
+
10
+ const shopId = parseGid(shop.id).id;
11
+ const body = robotsTxtData({url: url.origin, shopId});
12
+
13
+ return new Response(body, {
14
+ status: 200,
15
+ headers: {
16
+ 'Content-Type': 'text/plain',
17
+
18
+ 'Cache-Control': `max-age=${60 * 60 * 24}`,
19
+ },
20
+ });
21
+ }
22
+
23
+ function robotsTxtData({url, shopId}: {shopId?: string; url?: string}) {
24
+ const sitemapUrl = url ? `${url}/sitemap.xml` : undefined;
25
+
26
+ return `
27
+ User-agent: *
28
+ ${generalDisallowRules({sitemapUrl, shopId})}
29
+
30
+ # Google adsbot ignores robots.txt unless specifically named!
31
+ User-agent: adsbot-google
32
+ Disallow: /checkouts/
33
+ Disallow: /checkout
34
+ Disallow: /carts
35
+ Disallow: /orders
36
+ ${shopId ? `Disallow: /${shopId}/checkouts` : ''}
37
+ ${shopId ? `Disallow: /${shopId}/orders` : ''}
38
+ Disallow: /*?*oseid=*
39
+ Disallow: /*preview_theme_id*
40
+ Disallow: /*preview_script_id*
41
+
42
+ User-agent: Nutch
43
+ Disallow: /
44
+
45
+ User-agent: AhrefsBot
46
+ Crawl-delay: 10
47
+ ${generalDisallowRules({sitemapUrl, shopId})}
48
+
49
+ User-agent: AhrefsSiteAudit
50
+ Crawl-delay: 10
51
+ ${generalDisallowRules({sitemapUrl, shopId})}
52
+
53
+ User-agent: MJ12bot
54
+ Crawl-Delay: 10
55
+
56
+ User-agent: Pinterest
57
+ Crawl-delay: 1
58
+ `.trim();
59
+ }
60
+
61
+ /**
62
+ * This function generates disallow rules that generally follow what Shopify's
63
+ * Online Store has as defaults for their robots.txt
64
+ */
65
+ function generalDisallowRules({
66
+ shopId,
67
+ sitemapUrl,
68
+ }: {
69
+ shopId?: string;
70
+ sitemapUrl?: string;
71
+ }) {
72
+ return `Disallow: /admin
73
+ Disallow: /cart
74
+ Disallow: /orders
75
+ Disallow: /checkouts/
76
+ Disallow: /checkout
77
+ ${shopId ? `Disallow: /${shopId}/checkouts` : ''}
78
+ ${shopId ? `Disallow: /${shopId}/orders` : ''}
79
+ Disallow: /carts
80
+ Disallow: /account
81
+ Disallow: /collections/*sort_by*
82
+ Disallow: /*/collections/*sort_by*
83
+ Disallow: /collections/*+*
84
+ Disallow: /collections/*%2B*
85
+ Disallow: /collections/*%2b*
86
+ Disallow: /*/collections/*+*
87
+ Disallow: /*/collections/*%2B*
88
+ Disallow: /*/collections/*%2b*
89
+ Disallow: */collections/*filter*&*filter*
90
+ Disallow: /blogs/*+*
91
+ Disallow: /blogs/*%2B*
92
+ Disallow: /blogs/*%2b*
93
+ Disallow: /*/blogs/*+*
94
+ Disallow: /*/blogs/*%2B*
95
+ Disallow: /*/blogs/*%2b*
96
+ Disallow: /*?*oseid=*
97
+ Disallow: /*preview_theme_id*
98
+ Disallow: /*preview_script_id*
99
+ Disallow: /policies/
100
+ Disallow: /*/*?*ls=*&ls=*
101
+ Disallow: /*/*?*ls%3D*%3Fls%3D*
102
+ Disallow: /*/*?*ls%3d*%3fls%3d*
103
+ Disallow: /search
104
+ Allow: /search/
105
+ Disallow: /search/?*
106
+ Disallow: /apple-app-site-association
107
+ Disallow: /.well-known/shopify/monorail
108
+ ${sitemapUrl ? `Sitemap: ${sitemapUrl}` : ''}`;
109
+ }
110
+
111
+ const ROBOTS_QUERY = `#graphql
112
+ query StoreRobots($country: CountryCode, $language: LanguageCode)
113
+ @inContext(country: $country, language: $language) {
114
+ shop {
115
+ id
116
+ }
117
+ }
118
+ ` as const;
@@ -0,0 +1,177 @@
1
+ import {flattenConnection} from '@shopify/hydrogen';
2
+ import type {LoaderFunctionArgs} from '@shopify/remix-oxygen';
3
+ import type {SitemapQuery} from 'storefrontapi.generated';
4
+
5
+ /**
6
+ * the google limit is 50K, however, the storefront API
7
+ * allows querying only 250 resources per pagination page
8
+ */
9
+ const MAX_URLS = 250;
10
+
11
+ type Entry = {
12
+ url: string;
13
+ lastMod?: string;
14
+ changeFreq?: string;
15
+ image?: {
16
+ url: string;
17
+ title?: string;
18
+ caption?: string;
19
+ };
20
+ };
21
+
22
+ export async function loader({
23
+ request,
24
+ context: {storefront},
25
+ }: LoaderFunctionArgs) {
26
+ const data = await storefront.query(SITEMAP_QUERY, {
27
+ variables: {
28
+ urlLimits: MAX_URLS,
29
+ language: storefront.i18n.language,
30
+ },
31
+ });
32
+
33
+ if (!data) {
34
+ throw new Response('No data found', {status: 404});
35
+ }
36
+
37
+ const sitemap = generateSitemap({data, baseUrl: new URL(request.url).origin});
38
+
39
+ return new Response(sitemap, {
40
+ headers: {
41
+ 'Content-Type': 'application/xml',
42
+
43
+ 'Cache-Control': `max-age=${60 * 60 * 24}`,
44
+ },
45
+ });
46
+ }
47
+
48
+ function xmlEncode(string: string) {
49
+ return string.replace(/[&<>'"]/g, (char) => `&#${char.charCodeAt(0)};`);
50
+ }
51
+
52
+ function generateSitemap({
53
+ data,
54
+ baseUrl,
55
+ }: {
56
+ data: SitemapQuery;
57
+ baseUrl: string;
58
+ }) {
59
+ const products = flattenConnection(data.products)
60
+ .filter((product) => product.onlineStoreUrl)
61
+ .map((product) => {
62
+ const url = `${baseUrl}/products/${xmlEncode(product.handle)}`;
63
+
64
+ const productEntry: Entry = {
65
+ url,
66
+ lastMod: product.updatedAt,
67
+ changeFreq: 'daily',
68
+ };
69
+
70
+ if (product.featuredImage?.url) {
71
+ productEntry.image = {
72
+ url: xmlEncode(product.featuredImage.url),
73
+ };
74
+
75
+ if (product.title) {
76
+ productEntry.image.title = xmlEncode(product.title);
77
+ }
78
+
79
+ if (product.featuredImage.altText) {
80
+ productEntry.image.caption = xmlEncode(product.featuredImage.altText);
81
+ }
82
+ }
83
+
84
+ return productEntry;
85
+ });
86
+
87
+ const collections = flattenConnection(data.collections)
88
+ .filter((collection) => collection.onlineStoreUrl)
89
+ .map((collection) => {
90
+ const url = `${baseUrl}/collections/${collection.handle}`;
91
+
92
+ return {
93
+ url,
94
+ lastMod: collection.updatedAt,
95
+ changeFreq: 'daily',
96
+ };
97
+ });
98
+
99
+ const pages = flattenConnection(data.pages)
100
+ .filter((page) => page.onlineStoreUrl)
101
+ .map((page) => {
102
+ const url = `${baseUrl}/pages/${page.handle}`;
103
+
104
+ return {
105
+ url,
106
+ lastMod: page.updatedAt,
107
+ changeFreq: 'weekly',
108
+ };
109
+ });
110
+
111
+ const urls = [...products, ...collections, ...pages];
112
+
113
+ return `
114
+ <urlset
115
+ xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
116
+ xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
117
+ >
118
+ ${urls.map(renderUrlTag).join('')}
119
+ </urlset>`;
120
+ }
121
+
122
+ function renderUrlTag({url, lastMod, changeFreq, image}: Entry) {
123
+ const imageTag = image
124
+ ? `<image:image>
125
+ <image:loc>${image.url}</image:loc>
126
+ <image:title>${image.title ?? ''}</image:title>
127
+ <image:caption>${image.caption ?? ''}</image:caption>
128
+ </image:image>`.trim()
129
+ : '';
130
+
131
+ return `
132
+ <url>
133
+ <loc>${url}</loc>
134
+ <lastmod>${lastMod}</lastmod>
135
+ <changefreq>${changeFreq}</changefreq>
136
+ ${imageTag}
137
+ </url>
138
+ `.trim();
139
+ }
140
+
141
+ const SITEMAP_QUERY = `#graphql
142
+ query Sitemap($urlLimits: Int, $language: LanguageCode)
143
+ @inContext(language: $language) {
144
+ products(
145
+ first: $urlLimits
146
+ query: "published_status:'online_store:visible'"
147
+ ) {
148
+ nodes {
149
+ updatedAt
150
+ handle
151
+ onlineStoreUrl
152
+ title
153
+ featuredImage {
154
+ url
155
+ altText
156
+ }
157
+ }
158
+ }
159
+ collections(
160
+ first: $urlLimits
161
+ query: "published_status:'online_store:visible'"
162
+ ) {
163
+ nodes {
164
+ updatedAt
165
+ handle
166
+ onlineStoreUrl
167
+ }
168
+ }
169
+ pages(first: $urlLimits, query: "published_status:'published'") {
170
+ nodes {
171
+ updatedAt
172
+ handle
173
+ onlineStoreUrl
174
+ }
175
+ }
176
+ }
177
+ ` as const;
@@ -0,0 +1,182 @@
1
+ import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
+ import {Await, useLoaderData, Link, type MetaFunction} from '@remix-run/react';
3
+ import {Suspense} from 'react';
4
+ import {Image, Money} from '@shopify/hydrogen';
5
+ import type {
6
+ FeaturedCollectionFragment,
7
+ RecommendedProductsQuery,
8
+ } from 'storefrontapi.generated';
9
+
10
+ export const meta: MetaFunction = () => {
11
+ return [{title: 'Hydrogen | Home'}];
12
+ };
13
+
14
+ export async function loader(args: LoaderFunctionArgs) {
15
+ // Start fetching non-critical data without blocking time to first byte
16
+ const deferredData = loadDeferredData(args);
17
+
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
+ };
56
+ }
57
+
58
+ export default function Homepage() {
59
+ const data = useLoaderData<typeof loader>();
60
+ return (
61
+ <div className="home">
62
+ <FeaturedCollection collection={data.featuredCollection} />
63
+ <RecommendedProducts products={data.recommendedProducts} />
64
+ </div>
65
+ );
66
+ }
67
+
68
+ function FeaturedCollection({
69
+ collection,
70
+ }: {
71
+ collection: FeaturedCollectionFragment;
72
+ }) {
73
+ if (!collection) return null;
74
+ const image = collection?.image;
75
+ return (
76
+ <Link
77
+ className="featured-collection"
78
+ to={`/collections/${collection.handle}`}
79
+ >
80
+ {image && (
81
+ <div className="featured-collection-image">
82
+ <Image data={image} sizes="100vw" />
83
+ </div>
84
+ )}
85
+ <h1>{collection.title}</h1>
86
+ </Link>
87
+ );
88
+ }
89
+
90
+ function RecommendedProducts({
91
+ products,
92
+ }: {
93
+ products: Promise<RecommendedProductsQuery | null>;
94
+ }) {
95
+ return (
96
+ <div className="recommended-products">
97
+ <h2>Recommended Products</h2>
98
+ <Suspense fallback={<div>Loading...</div>}>
99
+ <Await resolve={products}>
100
+ {(response) => (
101
+ <div className="recommended-products-grid">
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}
121
+ </div>
122
+ )}
123
+ </Await>
124
+ </Suspense>
125
+ <br />
126
+ </div>
127
+ );
128
+ }
129
+
130
+ const FEATURED_COLLECTION_QUERY = `#graphql
131
+ fragment FeaturedCollection on Collection {
132
+ id
133
+ title
134
+ image {
135
+ id
136
+ url
137
+ altText
138
+ width
139
+ height
140
+ }
141
+ handle
142
+ }
143
+ query FeaturedCollection($country: CountryCode, $language: LanguageCode)
144
+ @inContext(country: $country, language: $language) {
145
+ collections(first: 1, sortKey: UPDATED_AT, reverse: true) {
146
+ nodes {
147
+ ...FeaturedCollection
148
+ }
149
+ }
150
+ }
151
+ ` as const;
152
+
153
+ const RECOMMENDED_PRODUCTS_QUERY = `#graphql
154
+ fragment RecommendedProduct on Product {
155
+ id
156
+ title
157
+ handle
158
+ priceRange {
159
+ minVariantPrice {
160
+ amount
161
+ currencyCode
162
+ }
163
+ }
164
+ images(first: 1) {
165
+ nodes {
166
+ id
167
+ url
168
+ altText
169
+ width
170
+ height
171
+ }
172
+ }
173
+ }
174
+ query RecommendedProducts ($country: CountryCode, $language: LanguageCode)
175
+ @inContext(country: $country, language: $language) {
176
+ products(first: 4, sortKey: UPDATED_AT, reverse: true) {
177
+ nodes {
178
+ ...RecommendedProduct
179
+ }
180
+ }
181
+ }
182
+ ` as const;
@@ -0,0 +1,8 @@
1
+ import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
+
3
+ // fallback wild card for all unauthenticated routes in account section
4
+ export async function loader({context}: LoaderFunctionArgs) {
5
+ await context.customerAccount.handleAuthStatus();
6
+
7
+ return redirect('/account');
8
+ }
@@ -0,0 +1,5 @@
1
+ import {redirect} from '@shopify/remix-oxygen';
2
+
3
+ export async function loader() {
4
+ return redirect('/account/orders');
5
+ }