@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.
- package/dist/assets/hydrogen/bundle/analyzer.html +2045 -0
- package/dist/assets/hydrogen/i18n/domains.ts +28 -0
- package/dist/assets/hydrogen/i18n/mock-i18n-types.ts +3 -0
- package/dist/assets/hydrogen/i18n/subdomains.ts +27 -0
- package/dist/assets/hydrogen/i18n/subfolders.ts +29 -0
- package/dist/assets/hydrogen/routes/locale-check.ts +16 -0
- package/dist/assets/hydrogen/starter/.eslintignore +5 -0
- package/dist/assets/hydrogen/starter/.eslintrc.cjs +19 -0
- package/dist/assets/hydrogen/starter/.graphqlrc.yml +12 -0
- package/dist/assets/hydrogen/starter/CHANGELOG.md +709 -0
- package/dist/assets/hydrogen/starter/README.md +45 -0
- package/dist/assets/hydrogen/starter/app/assets/favicon.svg +28 -0
- package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +37 -0
- package/dist/assets/hydrogen/starter/app/components/Aside.tsx +76 -0
- package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +150 -0
- package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +68 -0
- package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +101 -0
- package/dist/assets/hydrogen/starter/app/components/Footer.tsx +129 -0
- package/dist/assets/hydrogen/starter/app/components/Header.tsx +230 -0
- package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +126 -0
- package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +80 -0
- package/dist/assets/hydrogen/starter/app/components/ProductImage.tsx +23 -0
- package/dist/assets/hydrogen/starter/app/components/ProductPrice.tsx +27 -0
- package/dist/assets/hydrogen/starter/app/components/Search.tsx +514 -0
- package/dist/assets/hydrogen/starter/app/entry.client.tsx +12 -0
- package/dist/assets/hydrogen/starter/app/entry.server.tsx +47 -0
- package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerAddressMutations.ts +61 -0
- package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +40 -0
- package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrderQuery.ts +87 -0
- package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +58 -0
- package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +24 -0
- package/dist/assets/hydrogen/starter/app/lib/fragments.ts +174 -0
- package/dist/assets/hydrogen/starter/app/lib/search.ts +29 -0
- package/dist/assets/hydrogen/starter/app/lib/session.ts +72 -0
- package/dist/assets/hydrogen/starter/app/lib/variants.ts +46 -0
- package/dist/assets/hydrogen/starter/app/root.tsx +191 -0
- package/dist/assets/hydrogen/starter/app/routes/$.tsx +11 -0
- package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +118 -0
- package/dist/assets/hydrogen/starter/app/routes/[sitemap.xml].tsx +177 -0
- package/dist/assets/hydrogen/starter/app/routes/_index.tsx +182 -0
- package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +8 -0
- package/dist/assets/hydrogen/starter/app/routes/account._index.tsx +5 -0
- package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +513 -0
- package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +195 -0
- package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +107 -0
- package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +136 -0
- package/dist/assets/hydrogen/starter/app/routes/account.tsx +88 -0
- package/dist/assets/hydrogen/starter/app/routes/account_.authorize.tsx +5 -0
- package/dist/assets/hydrogen/starter/app/routes/account_.login.tsx +5 -0
- package/dist/assets/hydrogen/starter/app/routes/account_.logout.tsx +10 -0
- package/dist/assets/hydrogen/starter/app/routes/api.predictive-search.tsx +318 -0
- package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +113 -0
- package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +188 -0
- package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +119 -0
- package/dist/assets/hydrogen/starter/app/routes/cart.$lines.tsx +69 -0
- package/dist/assets/hydrogen/starter/app/routes/cart.tsx +102 -0
- package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +225 -0
- package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +146 -0
- package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +185 -0
- package/dist/assets/hydrogen/starter/app/routes/discount.$code.tsx +47 -0
- package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +84 -0
- package/dist/assets/hydrogen/starter/app/routes/policies.$handle.tsx +93 -0
- package/dist/assets/hydrogen/starter/app/routes/policies._index.tsx +63 -0
- package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +299 -0
- package/dist/assets/hydrogen/starter/app/routes/search.tsx +177 -0
- package/dist/assets/hydrogen/starter/app/styles/app.css +486 -0
- package/dist/assets/hydrogen/starter/app/styles/reset.css +129 -0
- package/dist/assets/hydrogen/starter/customer-accountapi.generated.d.ts +509 -0
- package/dist/assets/hydrogen/starter/env.d.ts +54 -0
- package/dist/assets/hydrogen/starter/package.json +50 -0
- package/dist/assets/hydrogen/starter/public/.gitkeep +0 -0
- package/dist/assets/hydrogen/starter/server.ts +119 -0
- package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +1211 -0
- package/dist/assets/hydrogen/starter/tsconfig.json +23 -0
- package/dist/assets/hydrogen/starter/vite.config.ts +41 -0
- package/dist/assets/hydrogen/tailwind/package.json +8 -0
- package/dist/assets/hydrogen/tailwind/tailwind.css +6 -0
- package/dist/assets/hydrogen/vanilla-extract/package.json +8 -0
- package/dist/assets/hydrogen/virtual-routes/assets/debug-network.css +592 -0
- package/dist/assets/hydrogen/virtual-routes/assets/favicon-dark.svg +20 -0
- package/dist/assets/hydrogen/virtual-routes/assets/favicon.svg +28 -0
- package/dist/assets/hydrogen/virtual-routes/assets/inter-variable-font.woff2 +0 -0
- package/dist/assets/hydrogen/virtual-routes/assets/jetbrainsmono-variable-font.woff2 +0 -0
- package/dist/assets/hydrogen/virtual-routes/assets/styles.css +238 -0
- package/dist/assets/hydrogen/virtual-routes/components/FlameChartWrapper.jsx +123 -0
- package/dist/assets/hydrogen/virtual-routes/components/HydrogenLogoBaseBW.jsx +32 -0
- package/dist/assets/hydrogen/virtual-routes/components/HydrogenLogoBaseColor.jsx +47 -0
- package/dist/assets/hydrogen/virtual-routes/components/IconBanner.jsx +292 -0
- package/dist/assets/hydrogen/virtual-routes/components/IconClose.jsx +38 -0
- package/dist/assets/hydrogen/virtual-routes/components/IconDiscard.jsx +44 -0
- package/dist/assets/hydrogen/virtual-routes/components/IconError.jsx +61 -0
- package/dist/assets/hydrogen/virtual-routes/components/IconGithub.jsx +23 -0
- package/dist/assets/hydrogen/virtual-routes/components/IconTwitter.jsx +21 -0
- package/dist/assets/hydrogen/virtual-routes/components/PageLayout.jsx +7 -0
- package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +178 -0
- package/dist/assets/hydrogen/virtual-routes/components/RequestTable.jsx +91 -0
- package/dist/assets/hydrogen/virtual-routes/components/RequestWaterfall.jsx +151 -0
- package/dist/assets/hydrogen/virtual-routes/lib/useDebugNetworkServer.jsx +178 -0
- package/dist/assets/hydrogen/virtual-routes/routes/graphiql.jsx +5 -0
- package/dist/assets/hydrogen/virtual-routes/routes/index.jsx +265 -0
- package/dist/assets/hydrogen/virtual-routes/routes/subrequest-profiler.jsx +243 -0
- package/dist/assets/hydrogen/virtual-routes/virtual-root.jsx +64 -0
- package/dist/assets/hydrogen/vite/package.json +14 -0
- package/dist/assets/hydrogen/vite/vite.config.js +41 -0
- package/dist/chokidar-2CKIHN27.js +12 -0
- package/dist/chunk-EO6F7WJJ.js +2 -0
- package/dist/chunk-FB327AH7.js +5 -0
- package/dist/chunk-FJPX4XUR.js +2 -0
- package/dist/chunk-JKOXGRAA.js +10 -0
- package/dist/chunk-LNQWGFTB.js +45 -0
- package/dist/chunk-M6JXYI3V.js +23 -0
- package/dist/chunk-MNT4XW23.js +2 -0
- package/dist/chunk-N7HFZHSO.js +1145 -0
- package/dist/chunk-PMDMUCNY.js +2 -0
- package/dist/chunk-QGLB6FFL.js +3 -0
- package/dist/chunk-VMIOG46Y.js +2 -0
- package/dist/create-app.js +1867 -34
- package/dist/del-CZGKV5SQ.js +11 -0
- package/dist/devtools-ZCRGQE64.js +8 -0
- package/dist/error-handler-GEQXZJ25.js +2 -0
- package/dist/lib-NJYCLW6W.js +22 -0
- package/dist/morph-ZJCCGFNC.js +30499 -0
- package/dist/multipart-parser-6HGDQWV7.js +3 -0
- package/dist/open-OD6DRFEG.js +2 -0
- package/dist/out-7KAQXZLP.js +2 -0
- package/dist/yoga.wasm +0 -0
- package/package.json +7 -3
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// NOTE: https://shopify.dev/docs/api/customer/latest/queries/order
|
|
2
|
+
export const CUSTOMER_ORDER_QUERY = `#graphql
|
|
3
|
+
fragment OrderMoney on MoneyV2 {
|
|
4
|
+
amount
|
|
5
|
+
currencyCode
|
|
6
|
+
}
|
|
7
|
+
fragment DiscountApplication on DiscountApplication {
|
|
8
|
+
value {
|
|
9
|
+
__typename
|
|
10
|
+
... on MoneyV2 {
|
|
11
|
+
...OrderMoney
|
|
12
|
+
}
|
|
13
|
+
... on PricingPercentageValue {
|
|
14
|
+
percentage
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
fragment OrderLineItemFull on LineItem {
|
|
19
|
+
id
|
|
20
|
+
title
|
|
21
|
+
quantity
|
|
22
|
+
price {
|
|
23
|
+
...OrderMoney
|
|
24
|
+
}
|
|
25
|
+
discountAllocations {
|
|
26
|
+
allocatedAmount {
|
|
27
|
+
...OrderMoney
|
|
28
|
+
}
|
|
29
|
+
discountApplication {
|
|
30
|
+
...DiscountApplication
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
totalDiscount {
|
|
34
|
+
...OrderMoney
|
|
35
|
+
}
|
|
36
|
+
image {
|
|
37
|
+
altText
|
|
38
|
+
height
|
|
39
|
+
url
|
|
40
|
+
id
|
|
41
|
+
width
|
|
42
|
+
}
|
|
43
|
+
variantTitle
|
|
44
|
+
}
|
|
45
|
+
fragment Order on Order {
|
|
46
|
+
id
|
|
47
|
+
name
|
|
48
|
+
statusPageUrl
|
|
49
|
+
processedAt
|
|
50
|
+
fulfillments(first: 1) {
|
|
51
|
+
nodes {
|
|
52
|
+
status
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
totalTax {
|
|
56
|
+
...OrderMoney
|
|
57
|
+
}
|
|
58
|
+
totalPrice {
|
|
59
|
+
...OrderMoney
|
|
60
|
+
}
|
|
61
|
+
subtotal {
|
|
62
|
+
...OrderMoney
|
|
63
|
+
}
|
|
64
|
+
shippingAddress {
|
|
65
|
+
name
|
|
66
|
+
formatted(withName: true)
|
|
67
|
+
formattedArea
|
|
68
|
+
}
|
|
69
|
+
discountApplications(first: 100) {
|
|
70
|
+
nodes {
|
|
71
|
+
...DiscountApplication
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
lineItems(first: 100) {
|
|
75
|
+
nodes {
|
|
76
|
+
...OrderLineItemFull
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
query Order($orderId: ID!) {
|
|
81
|
+
order(id: $orderId) {
|
|
82
|
+
... on Order {
|
|
83
|
+
...Order
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
` as const;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// https://shopify.dev/docs/api/customer/latest/objects/Order
|
|
2
|
+
export const ORDER_ITEM_FRAGMENT = `#graphql
|
|
3
|
+
fragment OrderItem on Order {
|
|
4
|
+
totalPrice {
|
|
5
|
+
amount
|
|
6
|
+
currencyCode
|
|
7
|
+
}
|
|
8
|
+
financialStatus
|
|
9
|
+
fulfillments(first: 1) {
|
|
10
|
+
nodes {
|
|
11
|
+
status
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
id
|
|
15
|
+
number
|
|
16
|
+
processedAt
|
|
17
|
+
}
|
|
18
|
+
` as const;
|
|
19
|
+
|
|
20
|
+
// https://shopify.dev/docs/api/customer/latest/objects/Customer
|
|
21
|
+
export const CUSTOMER_ORDERS_FRAGMENT = `#graphql
|
|
22
|
+
fragment CustomerOrders on Customer {
|
|
23
|
+
orders(
|
|
24
|
+
sortKey: PROCESSED_AT,
|
|
25
|
+
reverse: true,
|
|
26
|
+
first: $first,
|
|
27
|
+
last: $last,
|
|
28
|
+
before: $startCursor,
|
|
29
|
+
after: $endCursor
|
|
30
|
+
) {
|
|
31
|
+
nodes {
|
|
32
|
+
...OrderItem
|
|
33
|
+
}
|
|
34
|
+
pageInfo {
|
|
35
|
+
hasPreviousPage
|
|
36
|
+
hasNextPage
|
|
37
|
+
endCursor
|
|
38
|
+
startCursor
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
${ORDER_ITEM_FRAGMENT}
|
|
43
|
+
` as const;
|
|
44
|
+
|
|
45
|
+
// https://shopify.dev/docs/api/customer/latest/queries/customer
|
|
46
|
+
export const CUSTOMER_ORDERS_QUERY = `#graphql
|
|
47
|
+
${CUSTOMER_ORDERS_FRAGMENT}
|
|
48
|
+
query CustomerOrders(
|
|
49
|
+
$endCursor: String
|
|
50
|
+
$first: Int
|
|
51
|
+
$last: Int
|
|
52
|
+
$startCursor: String
|
|
53
|
+
) {
|
|
54
|
+
customer {
|
|
55
|
+
...CustomerOrders
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
` as const;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const CUSTOMER_UPDATE_MUTATION = `#graphql
|
|
2
|
+
# https://shopify.dev/docs/api/customer/latest/mutations/customerUpdate
|
|
3
|
+
mutation customerUpdate(
|
|
4
|
+
$customer: CustomerUpdateInput!
|
|
5
|
+
){
|
|
6
|
+
customerUpdate(input: $customer) {
|
|
7
|
+
customer {
|
|
8
|
+
firstName
|
|
9
|
+
lastName
|
|
10
|
+
emailAddress {
|
|
11
|
+
emailAddress
|
|
12
|
+
}
|
|
13
|
+
phoneNumber {
|
|
14
|
+
phoneNumber
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
userErrors {
|
|
18
|
+
code
|
|
19
|
+
field
|
|
20
|
+
message
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
` as const;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// NOTE: https://shopify.dev/docs/api/storefront/latest/queries/cart
|
|
2
|
+
export const CART_QUERY_FRAGMENT = `#graphql
|
|
3
|
+
fragment Money on MoneyV2 {
|
|
4
|
+
currencyCode
|
|
5
|
+
amount
|
|
6
|
+
}
|
|
7
|
+
fragment CartLine on CartLine {
|
|
8
|
+
id
|
|
9
|
+
quantity
|
|
10
|
+
attributes {
|
|
11
|
+
key
|
|
12
|
+
value
|
|
13
|
+
}
|
|
14
|
+
cost {
|
|
15
|
+
totalAmount {
|
|
16
|
+
...Money
|
|
17
|
+
}
|
|
18
|
+
amountPerQuantity {
|
|
19
|
+
...Money
|
|
20
|
+
}
|
|
21
|
+
compareAtAmountPerQuantity {
|
|
22
|
+
...Money
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
merchandise {
|
|
26
|
+
... on ProductVariant {
|
|
27
|
+
id
|
|
28
|
+
availableForSale
|
|
29
|
+
compareAtPrice {
|
|
30
|
+
...Money
|
|
31
|
+
}
|
|
32
|
+
price {
|
|
33
|
+
...Money
|
|
34
|
+
}
|
|
35
|
+
requiresShipping
|
|
36
|
+
title
|
|
37
|
+
image {
|
|
38
|
+
id
|
|
39
|
+
url
|
|
40
|
+
altText
|
|
41
|
+
width
|
|
42
|
+
height
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
product {
|
|
46
|
+
handle
|
|
47
|
+
title
|
|
48
|
+
id
|
|
49
|
+
vendor
|
|
50
|
+
}
|
|
51
|
+
selectedOptions {
|
|
52
|
+
name
|
|
53
|
+
value
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
fragment CartApiQuery on Cart {
|
|
59
|
+
updatedAt
|
|
60
|
+
id
|
|
61
|
+
checkoutUrl
|
|
62
|
+
totalQuantity
|
|
63
|
+
buyerIdentity {
|
|
64
|
+
countryCode
|
|
65
|
+
customer {
|
|
66
|
+
id
|
|
67
|
+
email
|
|
68
|
+
firstName
|
|
69
|
+
lastName
|
|
70
|
+
displayName
|
|
71
|
+
}
|
|
72
|
+
email
|
|
73
|
+
phone
|
|
74
|
+
}
|
|
75
|
+
lines(first: $numCartLines) {
|
|
76
|
+
nodes {
|
|
77
|
+
...CartLine
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
cost {
|
|
81
|
+
subtotalAmount {
|
|
82
|
+
...Money
|
|
83
|
+
}
|
|
84
|
+
totalAmount {
|
|
85
|
+
...Money
|
|
86
|
+
}
|
|
87
|
+
totalDutyAmount {
|
|
88
|
+
...Money
|
|
89
|
+
}
|
|
90
|
+
totalTaxAmount {
|
|
91
|
+
...Money
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
note
|
|
95
|
+
attributes {
|
|
96
|
+
key
|
|
97
|
+
value
|
|
98
|
+
}
|
|
99
|
+
discountCodes {
|
|
100
|
+
code
|
|
101
|
+
applicable
|
|
102
|
+
}
|
|
103
|
+
}
|
|
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,29 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PredictiveQueryFragment,
|
|
3
|
+
SearchProductFragment,
|
|
4
|
+
PredictiveProductFragment,
|
|
5
|
+
PredictiveCollectionFragment,
|
|
6
|
+
PredictivePageFragment,
|
|
7
|
+
PredictiveArticleFragment,
|
|
8
|
+
} from 'storefrontapi.generated';
|
|
9
|
+
|
|
10
|
+
export function applyTrackingParams(
|
|
11
|
+
resource:
|
|
12
|
+
| PredictiveQueryFragment
|
|
13
|
+
| SearchProductFragment
|
|
14
|
+
| PredictiveProductFragment
|
|
15
|
+
| PredictiveCollectionFragment
|
|
16
|
+
| PredictiveArticleFragment
|
|
17
|
+
| PredictivePageFragment,
|
|
18
|
+
params?: string,
|
|
19
|
+
) {
|
|
20
|
+
if (params) {
|
|
21
|
+
return resource?.trackingParameters
|
|
22
|
+
? `?${params}&${resource.trackingParameters}`
|
|
23
|
+
: `?${params}`;
|
|
24
|
+
} else {
|
|
25
|
+
return resource?.trackingParameters
|
|
26
|
+
? `?${resource.trackingParameters}`
|
|
27
|
+
: '';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type {HydrogenSession} from '@shopify/hydrogen';
|
|
2
|
+
import {
|
|
3
|
+
createCookieSessionStorage,
|
|
4
|
+
type SessionStorage,
|
|
5
|
+
type Session,
|
|
6
|
+
} from '@shopify/remix-oxygen';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* This is a custom session implementation for your Hydrogen shop.
|
|
10
|
+
* Feel free to customize it to your needs, add helper methods, or
|
|
11
|
+
* swap out the cookie-based implementation with something else!
|
|
12
|
+
*/
|
|
13
|
+
export class AppSession implements HydrogenSession {
|
|
14
|
+
public isPending = false;
|
|
15
|
+
|
|
16
|
+
#sessionStorage;
|
|
17
|
+
#session;
|
|
18
|
+
|
|
19
|
+
constructor(sessionStorage: SessionStorage, session: Session) {
|
|
20
|
+
this.#sessionStorage = sessionStorage;
|
|
21
|
+
this.#session = session;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static async init(request: Request, secrets: string[]) {
|
|
25
|
+
const storage = createCookieSessionStorage({
|
|
26
|
+
cookie: {
|
|
27
|
+
name: 'session',
|
|
28
|
+
httpOnly: true,
|
|
29
|
+
path: '/',
|
|
30
|
+
sameSite: 'lax',
|
|
31
|
+
secrets,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const session = await storage
|
|
36
|
+
.getSession(request.headers.get('Cookie'))
|
|
37
|
+
.catch(() => storage.getSession());
|
|
38
|
+
|
|
39
|
+
return new this(storage, session);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get has() {
|
|
43
|
+
return this.#session.has;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get get() {
|
|
47
|
+
return this.#session.get;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get flash() {
|
|
51
|
+
return this.#session.flash;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get unset() {
|
|
55
|
+
this.isPending = true;
|
|
56
|
+
return this.#session.unset;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get set() {
|
|
60
|
+
this.isPending = true;
|
|
61
|
+
return this.#session.set;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
destroy() {
|
|
65
|
+
return this.#sessionStorage.destroySession(this.#session);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
commit() {
|
|
69
|
+
this.isPending = false;
|
|
70
|
+
return this.#sessionStorage.commitSession(this.#session);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {useLocation} from '@remix-run/react';
|
|
2
|
+
import type {SelectedOption} from '@shopify/hydrogen/storefront-api-types';
|
|
3
|
+
import {useMemo} from 'react';
|
|
4
|
+
|
|
5
|
+
export function useVariantUrl(
|
|
6
|
+
handle: string,
|
|
7
|
+
selectedOptions: SelectedOption[],
|
|
8
|
+
) {
|
|
9
|
+
const {pathname} = useLocation();
|
|
10
|
+
|
|
11
|
+
return useMemo(() => {
|
|
12
|
+
return getVariantUrl({
|
|
13
|
+
handle,
|
|
14
|
+
pathname,
|
|
15
|
+
searchParams: new URLSearchParams(),
|
|
16
|
+
selectedOptions,
|
|
17
|
+
});
|
|
18
|
+
}, [handle, selectedOptions, pathname]);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getVariantUrl({
|
|
22
|
+
handle,
|
|
23
|
+
pathname,
|
|
24
|
+
searchParams,
|
|
25
|
+
selectedOptions,
|
|
26
|
+
}: {
|
|
27
|
+
handle: string;
|
|
28
|
+
pathname: string;
|
|
29
|
+
searchParams: URLSearchParams;
|
|
30
|
+
selectedOptions: SelectedOption[];
|
|
31
|
+
}) {
|
|
32
|
+
const match = /(\/[a-zA-Z]{2}-[a-zA-Z]{2}\/)/g.exec(pathname);
|
|
33
|
+
const isLocalePathname = match && match.length > 0;
|
|
34
|
+
|
|
35
|
+
const path = isLocalePathname
|
|
36
|
+
? `${match![0]}products/${handle}`
|
|
37
|
+
: `/products/${handle}`;
|
|
38
|
+
|
|
39
|
+
selectedOptions.forEach((option) => {
|
|
40
|
+
searchParams.set(option.name, option.value);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const searchString = searchParams.toString();
|
|
44
|
+
|
|
45
|
+
return path + (searchString ? '?' + searchParams.toString() : '');
|
|
46
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
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
|
+
...deferredData,
|
|
70
|
+
...criticalData,
|
|
71
|
+
publicStoreDomain: env.PUBLIC_STORE_DOMAIN,
|
|
72
|
+
shop: getShopAnalytics({
|
|
73
|
+
storefront,
|
|
74
|
+
publicStorefrontId: env.PUBLIC_STOREFRONT_ID,
|
|
75
|
+
}),
|
|
76
|
+
consent: {
|
|
77
|
+
checkoutDomain: env.PUBLIC_CHECKOUT_DOMAIN,
|
|
78
|
+
storefrontAccessToken: env.PUBLIC_STOREFRONT_API_TOKEN,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Load data necessary for rendering content above the fold. This is the critical data
|
|
85
|
+
* needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
|
|
86
|
+
*/
|
|
87
|
+
async function loadCriticalData({context}: LoaderFunctionArgs) {
|
|
88
|
+
const {storefront} = context;
|
|
89
|
+
|
|
90
|
+
const [header] = await Promise.all([
|
|
91
|
+
storefront.query(HEADER_QUERY, {
|
|
92
|
+
cache: storefront.CacheLong(),
|
|
93
|
+
variables: {
|
|
94
|
+
headerMenuHandle: 'main-menu', // Adjust to your header menu handle
|
|
95
|
+
},
|
|
96
|
+
}),
|
|
97
|
+
// Add other queries here, so that they are loaded in parallel
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
header,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Load data for rendering content below the fold. This data is deferred and will be
|
|
107
|
+
* fetched after the initial page load. If it's unavailable, the page should still 200.
|
|
108
|
+
* Make sure to not throw any errors here, as it will cause the page to 500.
|
|
109
|
+
*/
|
|
110
|
+
function loadDeferredData({context}: LoaderFunctionArgs) {
|
|
111
|
+
const {storefront, customerAccount, cart} = context;
|
|
112
|
+
|
|
113
|
+
// defer the footer query (below the fold)
|
|
114
|
+
const footer = storefront
|
|
115
|
+
.query(FOOTER_QUERY, {
|
|
116
|
+
cache: storefront.CacheLong(),
|
|
117
|
+
variables: {
|
|
118
|
+
footerMenuHandle: 'footer', // Adjust to your footer menu handle
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
.catch((error) => {
|
|
122
|
+
// Log query errors, but don't throw them so the page can still render
|
|
123
|
+
console.error(error);
|
|
124
|
+
return null;
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
cart: cart.get(),
|
|
128
|
+
isLoggedIn: customerAccount.isLoggedIn(),
|
|
129
|
+
footer,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function Layout({children}: {children?: React.ReactNode}) {
|
|
134
|
+
const nonce = useNonce();
|
|
135
|
+
const data = useRouteLoaderData<RootLoader>('root');
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<html lang="en">
|
|
139
|
+
<head>
|
|
140
|
+
<meta charSet="utf-8" />
|
|
141
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
142
|
+
<Meta />
|
|
143
|
+
<Links />
|
|
144
|
+
</head>
|
|
145
|
+
<body>
|
|
146
|
+
{data ? (
|
|
147
|
+
<Analytics.Provider
|
|
148
|
+
cart={data.cart}
|
|
149
|
+
shop={data.shop}
|
|
150
|
+
consent={data.consent}
|
|
151
|
+
>
|
|
152
|
+
<PageLayout {...data}>{children}</PageLayout>
|
|
153
|
+
</Analytics.Provider>
|
|
154
|
+
) : (
|
|
155
|
+
children
|
|
156
|
+
)}
|
|
157
|
+
<ScrollRestoration nonce={nonce} />
|
|
158
|
+
<Scripts nonce={nonce} />
|
|
159
|
+
</body>
|
|
160
|
+
</html>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export default function App() {
|
|
165
|
+
return <Outlet />;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function ErrorBoundary() {
|
|
169
|
+
const error = useRouteError();
|
|
170
|
+
let errorMessage = 'Unknown error';
|
|
171
|
+
let errorStatus = 500;
|
|
172
|
+
|
|
173
|
+
if (isRouteErrorResponse(error)) {
|
|
174
|
+
errorMessage = error?.data?.message ?? error.data;
|
|
175
|
+
errorStatus = error.status;
|
|
176
|
+
} else if (error instanceof Error) {
|
|
177
|
+
errorMessage = error.message;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<div className="route-error">
|
|
182
|
+
<h1>Oops</h1>
|
|
183
|
+
<h2>{errorStatus}</h2>
|
|
184
|
+
{errorMessage && (
|
|
185
|
+
<fieldset>
|
|
186
|
+
<pre>{errorMessage}</pre>
|
|
187
|
+
</fieldset>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type {LoaderFunctionArgs} from '@shopify/remix-oxygen';
|
|
2
|
+
|
|
3
|
+
export async function loader({request}: LoaderFunctionArgs) {
|
|
4
|
+
throw new Response(`${new URL(request.url).pathname} not found`, {
|
|
5
|
+
status: 404,
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function CatchAllPage() {
|
|
10
|
+
return null;
|
|
11
|
+
}
|