@shopify/cli-hydrogen 8.0.4 → 8.1.1
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.
- package/dist/{lib/setups/i18n/templates → assets/hydrogen/i18n}/domains.ts +1 -1
- package/dist/assets/hydrogen/i18n/mock-i18n-types.ts +3 -0
- package/dist/{lib/setups/i18n/templates → assets/hydrogen/i18n}/subdomains.ts +1 -1
- package/dist/{lib/setups/i18n/templates → assets/hydrogen/i18n}/subfolders.ts +1 -1
- package/dist/{generator-templates → assets/hydrogen}/starter/.eslintrc.cjs +1 -0
- package/dist/{generator-templates → assets/hydrogen}/starter/CHANGELOG.md +31 -0
- package/dist/assets/hydrogen/starter/app/components/Aside.tsx +76 -0
- package/dist/{generator-templates → assets/hydrogen}/starter/app/components/Cart.tsx +38 -14
- package/dist/{generator-templates → assets/hydrogen}/starter/app/components/Footer.tsx +30 -13
- package/dist/{generator-templates → assets/hydrogen}/starter/app/components/Header.tsx +52 -12
- package/dist/{generator-templates/starter/app/components/Layout.tsx → assets/hydrogen/starter/app/components/PageLayout.tsx} +38 -28
- package/dist/{generator-templates → assets/hydrogen}/starter/app/components/Search.tsx +5 -3
- package/dist/{generator-templates → assets/hydrogen}/starter/app/entry.server.tsx +8 -2
- package/dist/{generator-templates → assets/hydrogen}/starter/app/lib/fragments.ts +70 -0
- package/dist/assets/hydrogen/starter/app/root.tsx +204 -0
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/_index.tsx +62 -25
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.addresses.tsx +0 -1
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.orders.$id.tsx +2 -2
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.profile.tsx +0 -1
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/api.predictive-search.tsx +6 -1
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +34 -8
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/blogs.$blogHandle._index.tsx +36 -10
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/blogs._index.tsx +35 -12
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/cart.tsx +7 -7
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/collections.$handle.tsx +49 -7
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/collections._index.tsx +32 -6
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/collections.all.tsx +31 -6
- package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +84 -0
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/products.$handle.tsx +82 -17
- package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/search.tsx +4 -1
- package/dist/{generator-templates → assets/hydrogen}/starter/app/styles/app.css +22 -4
- package/dist/{generator-templates → assets/hydrogen}/starter/env.d.ts +1 -0
- package/dist/{generator-templates → assets/hydrogen}/starter/package.json +10 -10
- package/dist/{generator-templates → assets/hydrogen}/starter/storefrontapi.generated.d.ts +3 -1
- package/dist/{generator-templates → assets/hydrogen}/starter/vite.config.ts +15 -0
- package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/routes/index.jsx +0 -10
- package/dist/{generator-templates/assets → assets/hydrogen}/vite/vite.config.js +15 -0
- package/dist/commands/hydrogen/build.js +119 -36
- package/dist/commands/hydrogen/debug/cpu.js +74 -64
- package/dist/commands/hydrogen/deploy.js +4 -4
- package/dist/commands/hydrogen/dev.js +133 -51
- package/dist/{init.d.ts → commands/hydrogen/init.d.ts} +5 -5
- package/dist/commands/hydrogen/init.js +17 -8
- package/dist/commands/hydrogen/preview.js +101 -13
- package/dist/commands/hydrogen/setup/vite.js +2 -2
- package/dist/commands/hydrogen/setup.js +1 -1
- package/dist/commands/hydrogen/upgrade.js +53 -50
- package/dist/index.d.ts +403 -0
- package/dist/index.js +61 -0
- package/dist/lib/build.js +48 -37
- package/dist/lib/bundle/analyzer.js +2 -4
- package/dist/lib/check-version.js +38 -18
- package/dist/lib/classic-compiler/build.js +11 -4
- package/dist/lib/classic-compiler/debug-cpu.js +52 -0
- package/dist/lib/classic-compiler/dev.js +12 -5
- package/dist/lib/codegen.js +34 -9
- package/dist/lib/cpu-profiler.js +29 -12
- package/dist/lib/defer.js +13 -7
- package/dist/lib/deps-optimizer.js +146 -0
- package/dist/lib/flags.js +2 -7
- package/dist/lib/format-code.js +1 -2
- package/dist/lib/import-utils.js +22 -0
- package/dist/lib/live-reload.js +15 -5
- package/dist/lib/log.js +24 -2
- package/dist/lib/mini-oxygen/index.js +6 -2
- package/dist/lib/mini-oxygen/node.js +18 -4
- package/dist/lib/mini-oxygen/workerd.js +18 -5
- package/dist/lib/onboarding/local.js +1 -1
- package/dist/lib/onboarding/setup-template.mocks.js +7 -11
- package/dist/lib/remix-config.js +11 -9
- package/dist/lib/remix-version-check.js +8 -13
- package/dist/lib/resource-cleanup.js +13 -0
- package/dist/lib/setups/css/assets.js +3 -3
- package/dist/lib/setups/css/css-modules.js +2 -2
- package/dist/lib/setups/css/postcss.js +2 -2
- package/dist/lib/setups/css/tailwind.js +2 -2
- package/dist/lib/setups/css/vanilla-extract.js +2 -2
- package/dist/lib/setups/i18n/index.js +3 -5
- package/dist/lib/setups/routes/generate.js +17 -16
- package/dist/lib/template-diff.js +120 -42
- package/dist/lib/template-downloader.js +6 -11
- package/dist/lib/transpile/morph/typedefs.js +17 -0
- package/dist/lib/virtual-routes.js +2 -2
- package/dist/lib/vite-config.js +2 -1
- package/oclif.manifest.json +89 -1
- package/package.json +13 -9
- package/dist/generator-templates/starter/app/components/Aside.tsx +0 -47
- package/dist/generator-templates/starter/app/lib/root-data.ts +0 -11
- package/dist/generator-templates/starter/app/root.tsx +0 -227
- package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +0 -56
- package/dist/lib/setups/i18n/mock-i18n-types.js +0 -1
- package/dist/lib/setups/i18n/templates/domains.js +0 -14
- package/dist/lib/setups/i18n/templates/subdomains.js +0 -14
- package/dist/lib/setups/i18n/templates/subfolders.js +0 -13
- package/dist/lib/setups/routes/templates/locale-check.js +0 -9
- package/dist/virtual-routes/components/IconDiscord.jsx +0 -21
- /package/dist/{lib/bundle/bundle-analyzer.html → assets/hydrogen/bundle/analyzer.html} +0 -0
- /package/dist/{generator-templates/assets → assets/hydrogen}/css-modules/package.json +0 -0
- /package/dist/{generator-templates/assets → assets/hydrogen}/postcss/package.json +0 -0
- /package/dist/{generator-templates/assets → assets/hydrogen}/postcss/postcss.config.js +0 -0
- /package/dist/{lib/setups/routes/templates → assets/hydrogen/routes}/locale-check.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/.eslintignore +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/.graphqlrc.yml +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/README.md +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/assets/favicon.svg +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/entry.client.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/graphql/customer-account/CustomerAddressMutations.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/graphql/customer-account/CustomerOrderQuery.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/lib/search.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/lib/session.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/lib/variants.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/$.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/[robots.txt].tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/[sitemap.xml].tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.$.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account._index.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.orders._index.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account_.authorize.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account_.login.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account_.logout.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/cart.$lines.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/discount.$code.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/policies.$handle.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/policies._index.tsx +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/app/styles/reset.css +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/customer-accountapi.generated.d.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/public/.gitkeep +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/server.ts +0 -0
- /package/dist/{generator-templates → assets/hydrogen}/starter/tsconfig.json +0 -0
- /package/dist/{generator-templates/assets → assets/hydrogen}/tailwind/package.json +0 -0
- /package/dist/{generator-templates/assets → assets/hydrogen}/tailwind/postcss.config.js +0 -0
- /package/dist/{generator-templates/assets → assets/hydrogen}/tailwind/tailwind.config.js +0 -0
- /package/dist/{generator-templates/assets → assets/hydrogen}/tailwind/tailwind.css +0 -0
- /package/dist/{generator-templates/assets → assets/hydrogen}/vanilla-extract/package.json +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/debug-network.css +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/favicon-dark.svg +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/favicon.svg +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/inter-variable-font.woff2 +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/jetbrainsmono-variable-font.woff2 +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/styles.css +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/FlameChartWrapper.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/HydrogenLogoBaseBW.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/HydrogenLogoBaseColor.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconBanner.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconClose.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconDiscard.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconError.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconGithub.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconTwitter.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/Layout.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/RequestDetails.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/RequestTable.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/RequestWaterfall.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/lib/useDebugNetworkServer.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/routes/graphiql.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/routes/subrequest-profiler.jsx +0 -0
- /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/virtual-root.jsx +0 -0
- /package/dist/{generator-templates/assets → assets/hydrogen}/vite/package.json +0 -0
|
@@ -102,3 +102,73 @@ export const CART_QUERY_FRAGMENT = `#graphql
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
` as const;
|
|
105
|
+
|
|
106
|
+
const MENU_FRAGMENT = `#graphql
|
|
107
|
+
fragment MenuItem on MenuItem {
|
|
108
|
+
id
|
|
109
|
+
resourceId
|
|
110
|
+
tags
|
|
111
|
+
title
|
|
112
|
+
type
|
|
113
|
+
url
|
|
114
|
+
}
|
|
115
|
+
fragment ChildMenuItem on MenuItem {
|
|
116
|
+
...MenuItem
|
|
117
|
+
}
|
|
118
|
+
fragment ParentMenuItem on MenuItem {
|
|
119
|
+
...MenuItem
|
|
120
|
+
items {
|
|
121
|
+
...ChildMenuItem
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
fragment Menu on Menu {
|
|
125
|
+
id
|
|
126
|
+
items {
|
|
127
|
+
...ParentMenuItem
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
` as const;
|
|
131
|
+
|
|
132
|
+
export const HEADER_QUERY = `#graphql
|
|
133
|
+
fragment Shop on Shop {
|
|
134
|
+
id
|
|
135
|
+
name
|
|
136
|
+
description
|
|
137
|
+
primaryDomain {
|
|
138
|
+
url
|
|
139
|
+
}
|
|
140
|
+
brand {
|
|
141
|
+
logo {
|
|
142
|
+
image {
|
|
143
|
+
url
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
query Header(
|
|
149
|
+
$country: CountryCode
|
|
150
|
+
$headerMenuHandle: String!
|
|
151
|
+
$language: LanguageCode
|
|
152
|
+
) @inContext(language: $language, country: $country) {
|
|
153
|
+
shop {
|
|
154
|
+
...Shop
|
|
155
|
+
}
|
|
156
|
+
menu(handle: $headerMenuHandle) {
|
|
157
|
+
...Menu
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
${MENU_FRAGMENT}
|
|
161
|
+
` as const;
|
|
162
|
+
|
|
163
|
+
export const FOOTER_QUERY = `#graphql
|
|
164
|
+
query Footer(
|
|
165
|
+
$country: CountryCode
|
|
166
|
+
$footerMenuHandle: String!
|
|
167
|
+
$language: LanguageCode
|
|
168
|
+
) @inContext(language: $language, country: $country) {
|
|
169
|
+
menu(handle: $footerMenuHandle) {
|
|
170
|
+
...Menu
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
${MENU_FRAGMENT}
|
|
174
|
+
` as const;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import {useNonce, getShopAnalytics, Analytics} from '@shopify/hydrogen';
|
|
2
|
+
import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
|
|
3
|
+
import {
|
|
4
|
+
Links,
|
|
5
|
+
Meta,
|
|
6
|
+
Outlet,
|
|
7
|
+
Scripts,
|
|
8
|
+
useRouteError,
|
|
9
|
+
useRouteLoaderData,
|
|
10
|
+
ScrollRestoration,
|
|
11
|
+
isRouteErrorResponse,
|
|
12
|
+
type ShouldRevalidateFunction,
|
|
13
|
+
} from '@remix-run/react';
|
|
14
|
+
import favicon from '~/assets/favicon.svg';
|
|
15
|
+
import resetStyles from '~/styles/reset.css?url';
|
|
16
|
+
import appStyles from '~/styles/app.css?url';
|
|
17
|
+
import {PageLayout} from '~/components/PageLayout';
|
|
18
|
+
import {FOOTER_QUERY, HEADER_QUERY} from '~/lib/fragments';
|
|
19
|
+
|
|
20
|
+
export type RootLoader = typeof loader;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* This is important to avoid re-fetching root queries on sub-navigations
|
|
24
|
+
*/
|
|
25
|
+
export const shouldRevalidate: ShouldRevalidateFunction = ({
|
|
26
|
+
formMethod,
|
|
27
|
+
currentUrl,
|
|
28
|
+
nextUrl,
|
|
29
|
+
}) => {
|
|
30
|
+
// revalidate when a mutation is performed e.g add to cart, login...
|
|
31
|
+
if (formMethod && formMethod !== 'GET') {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// revalidate when manually revalidating via useRevalidator
|
|
36
|
+
if (currentUrl.toString() === nextUrl.toString()) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return false;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export function links() {
|
|
44
|
+
return [
|
|
45
|
+
{rel: 'stylesheet', href: resetStyles},
|
|
46
|
+
{rel: 'stylesheet', href: appStyles},
|
|
47
|
+
{
|
|
48
|
+
rel: 'preconnect',
|
|
49
|
+
href: 'https://cdn.shopify.com',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
rel: 'preconnect',
|
|
53
|
+
href: 'https://shop.app',
|
|
54
|
+
},
|
|
55
|
+
{rel: 'icon', type: 'image/svg+xml', href: favicon},
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function loader(args: LoaderFunctionArgs) {
|
|
60
|
+
// Start fetching non-critical data without blocking time to first byte
|
|
61
|
+
const deferredData = loadDeferredData(args);
|
|
62
|
+
|
|
63
|
+
// Await the critical data required to render initial state of the page
|
|
64
|
+
const criticalData = await loadCriticalData(args);
|
|
65
|
+
|
|
66
|
+
const {storefront, env} = args.context;
|
|
67
|
+
|
|
68
|
+
return defer(
|
|
69
|
+
{
|
|
70
|
+
...deferredData,
|
|
71
|
+
...criticalData,
|
|
72
|
+
publicStoreDomain: env.PUBLIC_STORE_DOMAIN,
|
|
73
|
+
shop: getShopAnalytics({
|
|
74
|
+
storefront,
|
|
75
|
+
publicStorefrontId: env.PUBLIC_STOREFRONT_ID,
|
|
76
|
+
}),
|
|
77
|
+
consent: {
|
|
78
|
+
checkoutDomain: env.PUBLIC_CHECKOUT_DOMAIN,
|
|
79
|
+
storefrontAccessToken: env.PUBLIC_STOREFRONT_API_TOKEN,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
headers: {
|
|
84
|
+
'Set-Cookie': await args.context.session.commit(),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Load data necessary for rendering content above the fold. This is the critical data
|
|
92
|
+
* needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
|
|
93
|
+
*/
|
|
94
|
+
async function loadCriticalData({context}: LoaderFunctionArgs) {
|
|
95
|
+
const {storefront} = context;
|
|
96
|
+
|
|
97
|
+
const [header] = await Promise.all([
|
|
98
|
+
storefront.query(HEADER_QUERY, {
|
|
99
|
+
cache: storefront.CacheLong(),
|
|
100
|
+
variables: {
|
|
101
|
+
headerMenuHandle: 'main-menu', // Adjust to your header menu handle
|
|
102
|
+
},
|
|
103
|
+
}),
|
|
104
|
+
// Add other queries here, so that they are loaded in parallel
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
header,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Load data for rendering content below the fold. This data is deferred and will be
|
|
114
|
+
* fetched after the initial page load. If it's unavailable, the page should still 200.
|
|
115
|
+
* Make sure to not throw any errors here, as it will cause the page to 500.
|
|
116
|
+
*/
|
|
117
|
+
function loadDeferredData({context}: LoaderFunctionArgs) {
|
|
118
|
+
const {storefront, customerAccount, cart} = context;
|
|
119
|
+
|
|
120
|
+
// defer the footer query (below the fold)
|
|
121
|
+
const footer = storefront
|
|
122
|
+
.query(FOOTER_QUERY, {
|
|
123
|
+
cache: storefront.CacheLong(),
|
|
124
|
+
variables: {
|
|
125
|
+
footerMenuHandle: 'footer', // Adjust to your footer menu handle
|
|
126
|
+
},
|
|
127
|
+
})
|
|
128
|
+
.catch((error) => {
|
|
129
|
+
// Log query errors, but don't throw them so the page can still render
|
|
130
|
+
console.error(error);
|
|
131
|
+
return null;
|
|
132
|
+
});
|
|
133
|
+
return {
|
|
134
|
+
cart: cart.get(),
|
|
135
|
+
isLoggedIn: customerAccount.isLoggedIn(),
|
|
136
|
+
footer,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function Layout({children}: {children?: React.ReactNode}) {
|
|
141
|
+
const nonce = useNonce();
|
|
142
|
+
const data = useRouteLoaderData<RootLoader>('root');
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<html lang="en">
|
|
146
|
+
<head>
|
|
147
|
+
<meta charSet="utf-8" />
|
|
148
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
149
|
+
<Meta />
|
|
150
|
+
<Links />
|
|
151
|
+
</head>
|
|
152
|
+
<body>
|
|
153
|
+
{data ? (
|
|
154
|
+
<Analytics.Provider
|
|
155
|
+
cart={data.cart}
|
|
156
|
+
shop={data.shop}
|
|
157
|
+
consent={data.consent}
|
|
158
|
+
>
|
|
159
|
+
<PageLayout {...data}>{children}</PageLayout>
|
|
160
|
+
</Analytics.Provider>
|
|
161
|
+
) : (
|
|
162
|
+
children
|
|
163
|
+
)}
|
|
164
|
+
<ScrollRestoration nonce={nonce} />
|
|
165
|
+
<Scripts nonce={nonce} />
|
|
166
|
+
</body>
|
|
167
|
+
</html>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export default function App() {
|
|
172
|
+
return (
|
|
173
|
+
<Layout>
|
|
174
|
+
<Outlet />
|
|
175
|
+
</Layout>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function ErrorBoundary() {
|
|
180
|
+
const error = useRouteError();
|
|
181
|
+
let errorMessage = 'Unknown error';
|
|
182
|
+
let errorStatus = 500;
|
|
183
|
+
|
|
184
|
+
if (isRouteErrorResponse(error)) {
|
|
185
|
+
errorMessage = error?.data?.message ?? error.data;
|
|
186
|
+
errorStatus = error.status;
|
|
187
|
+
} else if (error instanceof Error) {
|
|
188
|
+
errorMessage = error.message;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<Layout>
|
|
193
|
+
<div className="route-error">
|
|
194
|
+
<h1>Oops</h1>
|
|
195
|
+
<h2>{errorStatus}</h2>
|
|
196
|
+
{errorMessage && (
|
|
197
|
+
<fieldset>
|
|
198
|
+
<pre>{errorMessage}</pre>
|
|
199
|
+
</fieldset>
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
</Layout>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
@@ -11,13 +11,48 @@ export const meta: MetaFunction = () => {
|
|
|
11
11
|
return [{title: 'Hydrogen | Home'}];
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
export async function loader(
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const featuredCollection = collections.nodes[0];
|
|
18
|
-
const recommendedProducts = storefront.query(RECOMMENDED_PRODUCTS_QUERY);
|
|
14
|
+
export async function loader(args: LoaderFunctionArgs) {
|
|
15
|
+
// Start fetching non-critical data without blocking time to first byte
|
|
16
|
+
const deferredData = loadDeferredData(args);
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
// Await the critical data required to render initial state of the page
|
|
19
|
+
const criticalData = await loadCriticalData(args);
|
|
20
|
+
|
|
21
|
+
return defer({...deferredData, ...criticalData});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Load data necessary for rendering content above the fold. This is the critical data
|
|
26
|
+
* needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
|
|
27
|
+
*/
|
|
28
|
+
async function loadCriticalData({context}: LoaderFunctionArgs) {
|
|
29
|
+
const [{collections}] = await Promise.all([
|
|
30
|
+
context.storefront.query(FEATURED_COLLECTION_QUERY),
|
|
31
|
+
// Add other queries here, so that they are loaded in parallel
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
featuredCollection: collections.nodes[0],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Load data for rendering content below the fold. This data is deferred and will be
|
|
41
|
+
* fetched after the initial page load. If it's unavailable, the page should still 200.
|
|
42
|
+
* Make sure to not throw any errors here, as it will cause the page to 500.
|
|
43
|
+
*/
|
|
44
|
+
function loadDeferredData({context}: LoaderFunctionArgs) {
|
|
45
|
+
const recommendedProducts = context.storefront
|
|
46
|
+
.query(RECOMMENDED_PRODUCTS_QUERY)
|
|
47
|
+
.catch((error) => {
|
|
48
|
+
// Log query errors, but don't throw them so the page can still render
|
|
49
|
+
console.error(error);
|
|
50
|
+
return null;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
recommendedProducts,
|
|
55
|
+
};
|
|
21
56
|
}
|
|
22
57
|
|
|
23
58
|
export default function Homepage() {
|
|
@@ -55,32 +90,34 @@ function FeaturedCollection({
|
|
|
55
90
|
function RecommendedProducts({
|
|
56
91
|
products,
|
|
57
92
|
}: {
|
|
58
|
-
products: Promise<RecommendedProductsQuery>;
|
|
93
|
+
products: Promise<RecommendedProductsQuery | null>;
|
|
59
94
|
}) {
|
|
60
95
|
return (
|
|
61
96
|
<div className="recommended-products">
|
|
62
97
|
<h2>Recommended Products</h2>
|
|
63
98
|
<Suspense fallback={<div>Loading...</div>}>
|
|
64
99
|
<Await resolve={products}>
|
|
65
|
-
{(
|
|
100
|
+
{(response) => (
|
|
66
101
|
<div className="recommended-products-grid">
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
102
|
+
{response
|
|
103
|
+
? response.products.nodes.map((product) => (
|
|
104
|
+
<Link
|
|
105
|
+
key={product.id}
|
|
106
|
+
className="recommended-product"
|
|
107
|
+
to={`/products/${product.handle}`}
|
|
108
|
+
>
|
|
109
|
+
<Image
|
|
110
|
+
data={product.images.nodes[0]}
|
|
111
|
+
aspectRatio="1/1"
|
|
112
|
+
sizes="(min-width: 45em) 20vw, 50vw"
|
|
113
|
+
/>
|
|
114
|
+
<h4>{product.title}</h4>
|
|
115
|
+
<small>
|
|
116
|
+
<Money data={product.priceRange.minVariantPrice} />
|
|
117
|
+
</small>
|
|
118
|
+
</Link>
|
|
119
|
+
))
|
|
120
|
+
: null}
|
|
84
121
|
</div>
|
|
85
122
|
)}
|
|
86
123
|
</Await>
|
package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.orders.$id.tsx
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {json, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
|
|
2
|
-
import {
|
|
2
|
+
import {useLoaderData, type MetaFunction} from '@remix-run/react';
|
|
3
3
|
import {Money, Image, flattenConnection} from '@shopify/hydrogen';
|
|
4
4
|
import type {OrderLineItemFullFragment} from 'customer-accountapi.generated';
|
|
5
5
|
import {CUSTOMER_ORDER_QUERY} from '~/graphql/customer-account/CustomerOrderQuery';
|
|
@@ -8,7 +8,7 @@ export const meta: MetaFunction<typeof loader> = ({data}) => {
|
|
|
8
8
|
return [{title: `Order ${data?.order?.name}`}];
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
export async function loader({params, context
|
|
11
|
+
export async function loader({params, context}: LoaderFunctionArgs) {
|
|
12
12
|
if (!params.id) {
|
|
13
13
|
return redirect('/account/orders');
|
|
14
14
|
}
|
|
@@ -3,7 +3,6 @@ import type {CustomerUpdateInput} from '@shopify/hydrogen/customer-account-api-t
|
|
|
3
3
|
import {CUSTOMER_UPDATE_MUTATION} from '~/graphql/customer-account/CustomerUpdateMutation';
|
|
4
4
|
import {
|
|
5
5
|
json,
|
|
6
|
-
redirect,
|
|
7
6
|
type ActionFunctionArgs,
|
|
8
7
|
type LoaderFunctionArgs,
|
|
9
8
|
} from '@shopify/remix-oxygen';
|
package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/api.predictive-search.tsx
RENAMED
|
@@ -30,6 +30,8 @@ const DEFAULT_SEARCH_TYPES: PredictiveSearchTypes[] = [
|
|
|
30
30
|
'QUERY',
|
|
31
31
|
];
|
|
32
32
|
|
|
33
|
+
export type PredictiveSearchAPILoader = typeof loader;
|
|
34
|
+
|
|
33
35
|
/**
|
|
34
36
|
* Fetches the search results from the predictive search API
|
|
35
37
|
* requested by the SearchForm component
|
|
@@ -207,7 +209,7 @@ export function normalizePredictiveSearchResults(
|
|
|
207
209
|
id: article.id,
|
|
208
210
|
image: article.image,
|
|
209
211
|
title: article.title,
|
|
210
|
-
url: `${localePrefix}/blog/${article.handle}
|
|
212
|
+
url: `${localePrefix}/blogs/${article.blog.handle}/${article.handle}/${trackingParams}`,
|
|
211
213
|
};
|
|
212
214
|
},
|
|
213
215
|
),
|
|
@@ -223,6 +225,9 @@ const PREDICTIVE_SEARCH_QUERY = `#graphql
|
|
|
223
225
|
id
|
|
224
226
|
title
|
|
225
227
|
handle
|
|
228
|
+
blog {
|
|
229
|
+
handle
|
|
230
|
+
}
|
|
226
231
|
image {
|
|
227
232
|
url
|
|
228
233
|
altText
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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(
|
|
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
|
|
17
|
-
|
|
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
|
|
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() {
|
|
@@ -40,9 +66,9 @@ export default function Article() {
|
|
|
40
66
|
<div className="article">
|
|
41
67
|
<h1>
|
|
42
68
|
{title}
|
|
43
|
-
<
|
|
69
|
+
<div>
|
|
44
70
|
{publishedDate} · {author?.name}
|
|
45
|
-
</
|
|
71
|
+
</div>
|
|
46
72
|
</h1>
|
|
47
73
|
|
|
48
74
|
{image && <Image data={image} sizes="90vw" loading="eager" />}
|
package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/blogs.$blogHandle._index.tsx
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
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 {
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
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>();
|