@shopify/cli 3.85.3 → 3.85.5
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 +155 -148
- package/dist/assets/hydrogen/starter/CHANGELOG.md +125 -49
- package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +62 -29
- package/dist/assets/hydrogen/starter/app/components/Header.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +2 -2
- package/dist/assets/hydrogen/starter/app/components/SearchForm.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/components/SearchFormPredictive.tsx +8 -3
- package/dist/assets/hydrogen/starter/app/components/SearchResults.tsx +3 -11
- package/dist/assets/hydrogen/starter/app/components/SearchResultsPredictive.tsx +2 -6
- package/dist/assets/hydrogen/starter/app/entry.client.tsx +10 -2
- package/dist/assets/hydrogen/starter/app/entry.server.tsx +5 -3
- package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerAddressMutations.ts +7 -4
- package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -1
- package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrderQuery.ts +4 -1
- package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +10 -5
- package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +3 -2
- package/dist/assets/hydrogen/starter/app/lib/context.ts +34 -17
- package/dist/assets/hydrogen/starter/app/lib/fragments.ts +1 -0
- package/dist/assets/hydrogen/starter/app/lib/orderFilters.ts +90 -0
- package/dist/assets/hydrogen/starter/app/lib/redirect.ts +1 -1
- package/dist/assets/hydrogen/starter/app/lib/session.ts +1 -1
- package/dist/assets/hydrogen/starter/app/lib/variants.ts +1 -1
- package/dist/assets/hydrogen/starter/app/root.tsx +23 -18
- package/dist/assets/hydrogen/starter/app/routes/$.tsx +2 -2
- package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +2 -2
- package/dist/assets/hydrogen/starter/app/routes/[sitemap.xml].tsx +2 -3
- package/dist/assets/hydrogen/starter/app/routes/_index.tsx +12 -8
- package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +4 -3
- package/dist/assets/hydrogen/starter/app/routes/account._index.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +15 -11
- package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +47 -22
- package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +152 -23
- package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +11 -8
- package/dist/assets/hydrogen/starter/app/routes/account.tsx +16 -4
- package/dist/assets/hydrogen/starter/app/routes/account_.authorize.tsx +2 -2
- package/dist/assets/hydrogen/starter/app/routes/account_.login.tsx +5 -3
- package/dist/assets/hydrogen/starter/app/routes/account_.logout.tsx +3 -2
- package/dist/assets/hydrogen/starter/app/routes/api.$version.[graphql.json].tsx +2 -2
- package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +6 -10
- package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +10 -7
- package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +13 -7
- package/dist/assets/hydrogen/starter/app/routes/cart.$lines.tsx +3 -2
- package/dist/assets/hydrogen/starter/app/routes/cart.tsx +13 -9
- package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +8 -11
- package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +6 -6
- package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +10 -7
- package/dist/assets/hydrogen/starter/app/routes/discount.$code.tsx +3 -2
- package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +8 -6
- package/dist/assets/hydrogen/starter/app/routes/policies.$handle.tsx +7 -4
- package/dist/assets/hydrogen/starter/app/routes/policies._index.tsx +19 -13
- package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +9 -6
- package/dist/assets/hydrogen/starter/app/routes/search.tsx +14 -14
- package/dist/assets/hydrogen/starter/app/routes/sitemap.$type.$page[.xml].tsx +2 -3
- package/dist/assets/hydrogen/starter/app/routes.ts +1 -1
- package/dist/assets/hydrogen/starter/app/styles/app.css +53 -1
- package/dist/assets/hydrogen/starter/customer-accountapi.generated.d.ts +47 -13
- package/dist/assets/hydrogen/starter/env.d.ts +1 -39
- package/dist/assets/hydrogen/starter/eslint.config.js +35 -52
- package/dist/assets/hydrogen/starter/package.json +14 -15
- package/dist/assets/hydrogen/starter/react-router.config.ts +9 -3
- package/dist/assets/hydrogen/starter/server.ts +7 -7
- package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +1 -1
- package/dist/assets/hydrogen/starter/tsconfig.json +17 -13
- package/dist/assets/hydrogen/starter/vite.config.ts +3 -0
- package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +13 -20
- package/dist/assets/hydrogen/virtual-routes/routes/[.]well-known.appspecific.com[.]chrome[.]devtools[.]json.jsx +37 -0
- package/dist/{chokidar-TTCYG5AA.js → chokidar-HTKREZL3.js} +482 -7
- package/dist/{chunk-RPU6TIF2.js → chunk-3ULYQTX3.js} +7 -7
- package/dist/{chunk-IHXRXBUN.js → chunk-AVSSZ6MY.js} +6 -6
- package/dist/{chunk-I4ATBX6D.js → chunk-CHLX44DR.js} +4 -4
- package/dist/{chunk-UW4ASA6Y.js → chunk-CQSO2B3C.js} +2 -2
- package/dist/{chunk-OQKAZQIC.js → chunk-DHW4GI57.js} +5 -5
- package/dist/{chunk-HTTZVPR6.js → chunk-FGSQR4VQ.js} +3 -3
- package/dist/{chunk-3XWYM7QS.js → chunk-FMD2ETAR.js} +3 -3
- package/dist/{chunk-FGEKMLLA.js → chunk-FRHZJBJZ.js} +5 -5
- package/dist/{chunk-Y4H4HMEZ.js → chunk-G2I4CD5D.js} +2 -2
- package/dist/{chunk-HS2O5IHL.js → chunk-GG2ELY5O.js} +2 -3
- package/dist/{chunk-F7TU455C.js → chunk-GQ63RYWN.js} +2 -2
- package/dist/{chunk-VVUZFYUK.js → chunk-IAX3IWUQ.js} +4 -4
- package/dist/{chunk-EWEA4LRT.js → chunk-J6T7KVRL.js} +2 -2
- package/dist/{chunk-GRVHLYQS.js → chunk-JF6DQIJR.js} +3 -3
- package/dist/{chunk-JAUHWNMJ.js → chunk-MDDWB46W.js} +5 -5
- package/dist/{chunk-QHKSKL4E.js → chunk-MR763CPY.js} +3 -3
- package/dist/{chunk-6A6GDV25.js → chunk-MUZ7NPCX.js} +4 -4
- package/dist/{chunk-NLFRHIZY.js → chunk-NMGJYSTC.js} +5 -5
- package/dist/{chunk-D5DJSKHK.js → chunk-NTCXWD2Q.js} +111 -112
- package/dist/{chunk-HBANZKAF.js → chunk-O2O5GRI6.js} +3 -3
- package/dist/{chunk-CAONVM2S.js → chunk-ODJ7LSLO.js} +3 -3
- package/dist/{chunk-5RNGA7FX.js → chunk-P46WMXBU.js} +5 -5
- package/dist/{chunk-LJXXOFEJ.js → chunk-PFBQBDNU.js} +2 -2
- package/dist/{chunk-OURS5IQY.js → chunk-PZUWEJO3.js} +3 -3
- package/dist/{chunk-VR6Z7LKU.js → chunk-R6BNSDGA.js} +3 -3
- package/dist/{chunk-AMWSD3HH.js → chunk-RAZVOMJW.js} +3 -3
- package/dist/{chunk-C45MKMJT.js → chunk-RZPYG7LO.js} +28 -25
- package/dist/{chunk-EDXQ22O4.js → chunk-SMBX232U.js} +6 -6
- package/dist/{chunk-QP2MOS2Y.js → chunk-TE4CUUT4.js} +2 -2
- package/dist/{chunk-V5ONTA7N.js → chunk-TXX6R3WL.js} +2 -2
- package/dist/{chunk-L54PNQGV.js → chunk-TYHJPUOR.js} +2 -2
- package/dist/{chunk-XJBIASMX.js → chunk-UF2X5VHR.js} +3 -3
- package/dist/{chunk-ZHKIKKU7.js → chunk-UQT7B7DM.js} +3 -3
- package/dist/{chunk-6RJ7HBOQ.js → chunk-VVDGGMKJ.js} +3 -3
- package/dist/cli/commands/auth/login.js +10 -10
- package/dist/cli/commands/auth/login.test.js +11 -11
- package/dist/cli/commands/auth/logout.js +10 -10
- package/dist/cli/commands/auth/logout.test.js +11 -11
- package/dist/cli/commands/cache/clear.js +10 -10
- package/dist/cli/commands/debug/command-flags.js +10 -10
- package/dist/cli/commands/docs/generate.js +10 -10
- package/dist/cli/commands/docs/generate.test.js +10 -10
- package/dist/cli/commands/help.js +10 -10
- package/dist/cli/commands/kitchen-sink/async.js +11 -11
- package/dist/cli/commands/kitchen-sink/async.test.js +11 -11
- package/dist/cli/commands/kitchen-sink/index.js +13 -13
- package/dist/cli/commands/kitchen-sink/index.test.js +13 -13
- package/dist/cli/commands/kitchen-sink/prompts.js +11 -11
- package/dist/cli/commands/kitchen-sink/prompts.test.js +11 -11
- package/dist/cli/commands/kitchen-sink/static.js +11 -11
- package/dist/cli/commands/kitchen-sink/static.test.js +11 -11
- package/dist/cli/commands/notifications/generate.js +11 -11
- package/dist/cli/commands/notifications/list.js +11 -11
- package/dist/cli/commands/search.js +11 -11
- package/dist/cli/commands/upgrade.js +11 -11
- package/dist/cli/commands/version.js +11 -11
- package/dist/cli/commands/version.test.js +11 -11
- package/dist/cli/services/commands/notifications.js +6 -6
- package/dist/cli/services/commands/search.js +2 -2
- package/dist/cli/services/commands/search.test.js +2 -2
- package/dist/cli/services/commands/version.js +3 -3
- package/dist/cli/services/commands/version.test.js +4 -4
- package/dist/cli/services/kitchen-sink/async.js +2 -2
- package/dist/cli/services/kitchen-sink/prompts.js +2 -2
- package/dist/cli/services/kitchen-sink/static.js +2 -2
- package/dist/cli/services/upgrade.js +3 -3
- package/dist/cli/services/upgrade.test.js +4 -4
- package/dist/{custom-oclif-loader-YDKLB47A.js → custom-oclif-loader-X7GLA66E.js} +2 -2
- package/dist/{error-handler-BYZU2C5C.js → error-handler-QOTWDLMF.js} +8 -8
- package/dist/hooks/postrun.js +6 -6
- package/dist/hooks/prerun.js +7 -7
- package/dist/index.js +1800 -3480
- package/dist/{local-XLJD5WYP.js → local-FS3UI7PE.js} +2 -2
- package/dist/{morph-5D7H6MU2.js → morph-D4BXY376.js} +9 -9
- package/dist/{node-package-manager-I7AWSWJ4.js → node-package-manager-TX3WZQGI.js} +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/{ui-2AJAFJYY.js → ui-PUN5STUM.js} +2 -2
- package/dist/{workerd-4DJKRJUB.js → workerd-GJFM5MYN.js} +12 -12
- package/oclif.manifest.json +11 -56
- package/package.json +8 -8
- package/dist/chokidar-XUA2BN3J.js +0 -1120
- package/dist/chunk-F7J5CUMZ.js +0 -497
|
@@ -1,13 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {HydratedRouter} from 'react-router/dom';
|
|
2
2
|
import {startTransition, StrictMode} from 'react';
|
|
3
3
|
import {hydrateRoot} from 'react-dom/client';
|
|
4
|
+
import {NonceProvider} from '@shopify/hydrogen';
|
|
4
5
|
|
|
5
6
|
if (!window.location.origin.includes('webcache.googleusercontent.com')) {
|
|
6
7
|
startTransition(() => {
|
|
8
|
+
// Extract nonce from existing script tags
|
|
9
|
+
const existingNonce = document
|
|
10
|
+
.querySelector<HTMLScriptElement>('script[nonce]')
|
|
11
|
+
?.nonce;
|
|
12
|
+
|
|
7
13
|
hydrateRoot(
|
|
8
14
|
document,
|
|
9
15
|
<StrictMode>
|
|
10
|
-
<
|
|
16
|
+
<NonceProvider value={existingNonce}>
|
|
17
|
+
<HydratedRouter />
|
|
18
|
+
</NonceProvider>
|
|
11
19
|
</StrictMode>,
|
|
12
20
|
);
|
|
13
21
|
});
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import type {AppLoadContext} from '@shopify/remix-oxygen';
|
|
2
1
|
import {ServerRouter} from 'react-router';
|
|
3
2
|
import {isbot} from 'isbot';
|
|
4
3
|
import {renderToReadableStream} from 'react-dom/server';
|
|
5
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
createContentSecurityPolicy,
|
|
6
|
+
type HydrogenRouterContextProvider,
|
|
7
|
+
} from '@shopify/hydrogen';
|
|
6
8
|
import type {EntryContext} from 'react-router';
|
|
7
9
|
|
|
8
10
|
export default async function handleRequest(
|
|
@@ -10,7 +12,7 @@ export default async function handleRequest(
|
|
|
10
12
|
responseStatusCode: number,
|
|
11
13
|
responseHeaders: Headers,
|
|
12
14
|
reactRouterContext: EntryContext,
|
|
13
|
-
context:
|
|
15
|
+
context: HydrogenRouterContextProvider,
|
|
14
16
|
) {
|
|
15
17
|
const {nonce, header, NonceProvider} = createContentSecurityPolicy({
|
|
16
18
|
shop: {
|
package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerAddressMutations.ts
CHANGED
|
@@ -4,7 +4,8 @@ export const UPDATE_ADDRESS_MUTATION = `#graphql
|
|
|
4
4
|
$address: CustomerAddressInput!
|
|
5
5
|
$addressId: ID!
|
|
6
6
|
$defaultAddress: Boolean
|
|
7
|
-
|
|
7
|
+
$language: LanguageCode
|
|
8
|
+
) @inContext(language: $language) {
|
|
8
9
|
customerAddressUpdate(
|
|
9
10
|
address: $address
|
|
10
11
|
addressId: $addressId
|
|
@@ -25,8 +26,9 @@ export const UPDATE_ADDRESS_MUTATION = `#graphql
|
|
|
25
26
|
// NOTE: https://shopify.dev/docs/api/customer/latest/mutations/customerAddressDelete
|
|
26
27
|
export const DELETE_ADDRESS_MUTATION = `#graphql
|
|
27
28
|
mutation customerAddressDelete(
|
|
28
|
-
$addressId: ID
|
|
29
|
-
|
|
29
|
+
$addressId: ID!
|
|
30
|
+
$language: LanguageCode
|
|
31
|
+
) @inContext(language: $language) {
|
|
30
32
|
customerAddressDelete(addressId: $addressId) {
|
|
31
33
|
deletedAddressId
|
|
32
34
|
userErrors {
|
|
@@ -43,7 +45,8 @@ export const CREATE_ADDRESS_MUTATION = `#graphql
|
|
|
43
45
|
mutation customerAddressCreate(
|
|
44
46
|
$address: CustomerAddressInput!
|
|
45
47
|
$defaultAddress: Boolean
|
|
46
|
-
|
|
48
|
+
$language: LanguageCode
|
|
49
|
+
) @inContext(language: $language) {
|
|
47
50
|
customerAddressCreate(
|
|
48
51
|
address: $address
|
|
49
52
|
defaultAddress: $defaultAddress
|
|
@@ -31,7 +31,7 @@ export const CUSTOMER_FRAGMENT = `#graphql
|
|
|
31
31
|
|
|
32
32
|
// NOTE: https://shopify.dev/docs/api/customer/latest/queries/customer
|
|
33
33
|
export const CUSTOMER_DETAILS_QUERY = `#graphql
|
|
34
|
-
query CustomerDetails {
|
|
34
|
+
query CustomerDetails($language: LanguageCode) @inContext(language: $language) {
|
|
35
35
|
customer {
|
|
36
36
|
...Customer
|
|
37
37
|
}
|
|
@@ -45,7 +45,9 @@ export const CUSTOMER_ORDER_QUERY = `#graphql
|
|
|
45
45
|
fragment Order on Order {
|
|
46
46
|
id
|
|
47
47
|
name
|
|
48
|
+
confirmationNumber
|
|
48
49
|
statusPageUrl
|
|
50
|
+
fulfillmentStatus
|
|
49
51
|
processedAt
|
|
50
52
|
fulfillments(first: 1) {
|
|
51
53
|
nodes {
|
|
@@ -77,7 +79,8 @@ export const CUSTOMER_ORDER_QUERY = `#graphql
|
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
}
|
|
80
|
-
query Order($orderId: ID
|
|
82
|
+
query Order($orderId: ID!, $language: LanguageCode)
|
|
83
|
+
@inContext(language: $language) {
|
|
81
84
|
order(id: $orderId) {
|
|
82
85
|
... on Order {
|
|
83
86
|
...Order
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// https://shopify.dev/docs/api/customer/latest/objects/Order
|
|
1
|
+
// NOTE: https://shopify.dev/docs/api/customer/latest/objects/Order
|
|
2
2
|
export const ORDER_ITEM_FRAGMENT = `#graphql
|
|
3
3
|
fragment OrderItem on Order {
|
|
4
4
|
totalPrice {
|
|
@@ -6,6 +6,7 @@ export const ORDER_ITEM_FRAGMENT = `#graphql
|
|
|
6
6
|
currencyCode
|
|
7
7
|
}
|
|
8
8
|
financialStatus
|
|
9
|
+
fulfillmentStatus
|
|
9
10
|
fulfillments(first: 1) {
|
|
10
11
|
nodes {
|
|
11
12
|
status
|
|
@@ -13,11 +14,12 @@ export const ORDER_ITEM_FRAGMENT = `#graphql
|
|
|
13
14
|
}
|
|
14
15
|
id
|
|
15
16
|
number
|
|
17
|
+
confirmationNumber
|
|
16
18
|
processedAt
|
|
17
19
|
}
|
|
18
20
|
` as const;
|
|
19
21
|
|
|
20
|
-
// https://shopify.dev/docs/api/customer/latest/objects/Customer
|
|
22
|
+
// NOTE: https://shopify.dev/docs/api/customer/latest/objects/Customer
|
|
21
23
|
export const CUSTOMER_ORDERS_FRAGMENT = `#graphql
|
|
22
24
|
fragment CustomerOrders on Customer {
|
|
23
25
|
orders(
|
|
@@ -26,7 +28,8 @@ export const CUSTOMER_ORDERS_FRAGMENT = `#graphql
|
|
|
26
28
|
first: $first,
|
|
27
29
|
last: $last,
|
|
28
30
|
before: $startCursor,
|
|
29
|
-
after: $endCursor
|
|
31
|
+
after: $endCursor,
|
|
32
|
+
query: $query
|
|
30
33
|
) {
|
|
31
34
|
nodes {
|
|
32
35
|
...OrderItem
|
|
@@ -42,7 +45,7 @@ export const CUSTOMER_ORDERS_FRAGMENT = `#graphql
|
|
|
42
45
|
${ORDER_ITEM_FRAGMENT}
|
|
43
46
|
` as const;
|
|
44
47
|
|
|
45
|
-
// https://shopify.dev/docs/api/customer/latest/queries/customer
|
|
48
|
+
// NOTE: https://shopify.dev/docs/api/customer/latest/queries/customer
|
|
46
49
|
export const CUSTOMER_ORDERS_QUERY = `#graphql
|
|
47
50
|
${CUSTOMER_ORDERS_FRAGMENT}
|
|
48
51
|
query CustomerOrders(
|
|
@@ -50,7 +53,9 @@ export const CUSTOMER_ORDERS_QUERY = `#graphql
|
|
|
50
53
|
$first: Int
|
|
51
54
|
$last: Int
|
|
52
55
|
$startCursor: String
|
|
53
|
-
|
|
56
|
+
$query: String
|
|
57
|
+
$language: LanguageCode
|
|
58
|
+
) @inContext(language: $language) {
|
|
54
59
|
customer {
|
|
55
60
|
...CustomerOrders
|
|
56
61
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
// NOTE: https://shopify.dev/docs/api/customer/latest/mutations/customerUpdate
|
|
1
2
|
export const CUSTOMER_UPDATE_MUTATION = `#graphql
|
|
2
|
-
# https://shopify.dev/docs/api/customer/latest/mutations/customerUpdate
|
|
3
3
|
mutation customerUpdate(
|
|
4
4
|
$customer: CustomerUpdateInput!
|
|
5
|
-
|
|
5
|
+
$language: LanguageCode
|
|
6
|
+
) @inContext(language: $language) {
|
|
6
7
|
customerUpdate(input: $customer) {
|
|
7
8
|
customer {
|
|
8
9
|
firstName
|
|
@@ -2,11 +2,27 @@ import {createHydrogenContext} from '@shopify/hydrogen';
|
|
|
2
2
|
import {AppSession} from '~/lib/session';
|
|
3
3
|
import {CART_QUERY_FRAGMENT} from '~/lib/fragments';
|
|
4
4
|
|
|
5
|
+
// Define the additional context object
|
|
6
|
+
const additionalContext = {
|
|
7
|
+
// Additional context for custom properties, CMS clients, 3P SDKs, etc.
|
|
8
|
+
// These will be available as both context.propertyName and context.get(propertyContext)
|
|
9
|
+
// Example of complex objects that could be added:
|
|
10
|
+
// cms: await createCMSClient(env),
|
|
11
|
+
// reviews: await createReviewsClient(env),
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
// Automatically augment HydrogenAdditionalContext with the additional context type
|
|
15
|
+
type AdditionalContextType = typeof additionalContext;
|
|
16
|
+
|
|
17
|
+
declare global {
|
|
18
|
+
interface HydrogenAdditionalContext extends AdditionalContextType {}
|
|
19
|
+
}
|
|
20
|
+
|
|
5
21
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
22
|
+
* Creates Hydrogen context for React Router 7.9.x
|
|
23
|
+
* Returns HydrogenRouterContextProvider with hybrid access patterns
|
|
8
24
|
* */
|
|
9
|
-
export async function
|
|
25
|
+
export async function createHydrogenRouterContext(
|
|
10
26
|
request: Request,
|
|
11
27
|
env: Env,
|
|
12
28
|
executionContext: ExecutionContext,
|
|
@@ -24,20 +40,21 @@ export async function createAppLoadContext(
|
|
|
24
40
|
AppSession.init(request, [env.SESSION_SECRET]),
|
|
25
41
|
]);
|
|
26
42
|
|
|
27
|
-
const hydrogenContext = createHydrogenContext(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
const hydrogenContext = createHydrogenContext(
|
|
44
|
+
{
|
|
45
|
+
env,
|
|
46
|
+
request,
|
|
47
|
+
cache,
|
|
48
|
+
waitUntil,
|
|
49
|
+
session,
|
|
50
|
+
// Or detect from URL path based on locale subpath, cookies, or any other strategy
|
|
51
|
+
i18n: {language: 'EN', country: 'US'},
|
|
52
|
+
cart: {
|
|
53
|
+
queryFragment: CART_QUERY_FRAGMENT,
|
|
54
|
+
},
|
|
36
55
|
},
|
|
37
|
-
|
|
56
|
+
additionalContext,
|
|
57
|
+
);
|
|
38
58
|
|
|
39
|
-
return
|
|
40
|
-
...hydrogenContext,
|
|
41
|
-
// declare additional Remix loader context
|
|
42
|
-
};
|
|
59
|
+
return hydrogenContext;
|
|
43
60
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field name constants for order filtering
|
|
3
|
+
*/
|
|
4
|
+
export const ORDER_FILTER_FIELDS = {
|
|
5
|
+
NAME: 'name',
|
|
6
|
+
CONFIRMATION_NUMBER: 'confirmation_number',
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Parameters for filtering customer orders, see: https://shopify.dev/docs/api/customer/latest/queries/customer#returns-Customer.fields.orders.arguments.query
|
|
11
|
+
*/
|
|
12
|
+
export interface OrderFilterParams {
|
|
13
|
+
/** Order name or number (e.g., "#1001" or "1001") */
|
|
14
|
+
name?: string;
|
|
15
|
+
/** Order confirmation number */
|
|
16
|
+
confirmationNumber?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Sanitizes a filter value to prevent injection attacks or malformed queries.
|
|
21
|
+
* Allows only alphanumeric characters, underscore, and dash.
|
|
22
|
+
* @param value - The input string to sanitize
|
|
23
|
+
* @returns The sanitized string
|
|
24
|
+
*/
|
|
25
|
+
function sanitizeFilterValue(value: string): string {
|
|
26
|
+
// Only allow alphanumeric, underscore, and dash
|
|
27
|
+
// Remove anything else to prevent injection
|
|
28
|
+
return value.replace(/[^a-zA-Z0-9_\-]/g, '');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Builds a query string for filtering customer orders using the Customer Account API
|
|
33
|
+
* @param filters - The filter parameters
|
|
34
|
+
* @returns A formatted query string for the GraphQL query parameter, or undefined if no filters
|
|
35
|
+
* @example
|
|
36
|
+
* buildOrderSearchQuery(\{ name: '1001' \}) // returns "name:1001"
|
|
37
|
+
* buildOrderSearchQuery(\{ name: '1001', confirmationNumber: 'ABC123' \}) // returns "name:1001 AND confirmation_number:ABC123"
|
|
38
|
+
*/
|
|
39
|
+
export function buildOrderSearchQuery(
|
|
40
|
+
filters: OrderFilterParams,
|
|
41
|
+
): string | undefined {
|
|
42
|
+
const queryParts: string[] = [];
|
|
43
|
+
|
|
44
|
+
if (filters.name) {
|
|
45
|
+
// Remove # if present and trim
|
|
46
|
+
const cleanName = filters.name.replace(/^#/, '').trim();
|
|
47
|
+
const sanitizedName = sanitizeFilterValue(cleanName);
|
|
48
|
+
if (sanitizedName) {
|
|
49
|
+
queryParts.push(`name:${sanitizedName}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (filters.confirmationNumber) {
|
|
54
|
+
const cleanConfirmation = filters.confirmationNumber.trim();
|
|
55
|
+
const sanitizedConfirmation = sanitizeFilterValue(cleanConfirmation);
|
|
56
|
+
if (sanitizedConfirmation) {
|
|
57
|
+
queryParts.push(`confirmation_number:${sanitizedConfirmation}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return queryParts.length > 0 ? queryParts.join(' AND ') : undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Parses order filter parameters from URLSearchParams
|
|
66
|
+
* @param searchParams - The URL search parameters
|
|
67
|
+
* @returns Parsed filter parameters
|
|
68
|
+
* @example
|
|
69
|
+
* const url = new URL('https://example.com/orders?name=1001&confirmation_number=ABC123');
|
|
70
|
+
* parseOrderFilters(url.searchParams) // returns \{ name: '1001', confirmationNumber: 'ABC123' \}
|
|
71
|
+
*/
|
|
72
|
+
export function parseOrderFilters(
|
|
73
|
+
searchParams: URLSearchParams,
|
|
74
|
+
): OrderFilterParams {
|
|
75
|
+
const filters: OrderFilterParams = {};
|
|
76
|
+
|
|
77
|
+
const name = searchParams.get(ORDER_FILTER_FIELDS.NAME);
|
|
78
|
+
if (name) {
|
|
79
|
+
filters.name = name;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const confirmationNumber = searchParams.get(
|
|
83
|
+
ORDER_FILTER_FIELDS.CONFIRMATION_NUMBER,
|
|
84
|
+
);
|
|
85
|
+
if (confirmationNumber) {
|
|
86
|
+
filters.confirmationNumber = confirmationNumber;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return filters;
|
|
90
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {Analytics, getShopAnalytics, useNonce} from '@shopify/hydrogen';
|
|
2
|
-
import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
|
|
3
2
|
import {
|
|
4
3
|
Outlet,
|
|
5
4
|
useRouteError,
|
|
@@ -11,6 +10,7 @@ import {
|
|
|
11
10
|
ScrollRestoration,
|
|
12
11
|
useRouteLoaderData,
|
|
13
12
|
} from 'react-router';
|
|
13
|
+
import type {Route} from './+types/root';
|
|
14
14
|
import favicon from '~/assets/favicon.svg';
|
|
15
15
|
import {FOOTER_QUERY, HEADER_QUERY} from '~/lib/fragments';
|
|
16
16
|
import resetStyles from '~/styles/reset.css?url';
|
|
@@ -65,7 +65,7 @@ export function links() {
|
|
|
65
65
|
];
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export async function loader(args:
|
|
68
|
+
export async function loader(args: Route.LoaderArgs) {
|
|
69
69
|
// Start fetching non-critical data without blocking time to first byte
|
|
70
70
|
const deferredData = loadDeferredData(args);
|
|
71
71
|
|
|
@@ -97,7 +97,7 @@ export async function loader(args: LoaderFunctionArgs) {
|
|
|
97
97
|
* Load data necessary for rendering content above the fold. This is the critical data
|
|
98
98
|
* needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
|
|
99
99
|
*/
|
|
100
|
-
async function loadCriticalData({context}:
|
|
100
|
+
async function loadCriticalData({context}: Route.LoaderArgs) {
|
|
101
101
|
const {storefront} = context;
|
|
102
102
|
|
|
103
103
|
const [header] = await Promise.all([
|
|
@@ -118,7 +118,7 @@ async function loadCriticalData({context}: LoaderFunctionArgs) {
|
|
|
118
118
|
* fetched after the initial page load. If it's unavailable, the page should still 200.
|
|
119
119
|
* Make sure to not throw any errors here, as it will cause the page to 500.
|
|
120
120
|
*/
|
|
121
|
-
function loadDeferredData({context}:
|
|
121
|
+
function loadDeferredData({context}: Route.LoaderArgs) {
|
|
122
122
|
const {storefront, customerAccount, cart} = context;
|
|
123
123
|
|
|
124
124
|
// defer the footer query (below the fold)
|
|
@@ -129,7 +129,7 @@ function loadDeferredData({context}: LoaderFunctionArgs) {
|
|
|
129
129
|
footerMenuHandle: 'footer', // Adjust to your footer menu handle
|
|
130
130
|
},
|
|
131
131
|
})
|
|
132
|
-
.catch((error) => {
|
|
132
|
+
.catch((error: Error) => {
|
|
133
133
|
// Log query errors, but don't throw them so the page can still render
|
|
134
134
|
console.error(error);
|
|
135
135
|
return null;
|
|
@@ -143,7 +143,6 @@ function loadDeferredData({context}: LoaderFunctionArgs) {
|
|
|
143
143
|
|
|
144
144
|
export function Layout({children}: {children?: React.ReactNode}) {
|
|
145
145
|
const nonce = useNonce();
|
|
146
|
-
const data = useRouteLoaderData<RootLoader>('root');
|
|
147
146
|
|
|
148
147
|
return (
|
|
149
148
|
<html lang="en">
|
|
@@ -156,17 +155,7 @@ export function Layout({children}: {children?: React.ReactNode}) {
|
|
|
156
155
|
<Links />
|
|
157
156
|
</head>
|
|
158
157
|
<body>
|
|
159
|
-
{
|
|
160
|
-
<Analytics.Provider
|
|
161
|
-
cart={data.cart}
|
|
162
|
-
shop={data.shop}
|
|
163
|
-
consent={data.consent}
|
|
164
|
-
>
|
|
165
|
-
<PageLayout {...data}>{children}</PageLayout>
|
|
166
|
-
</Analytics.Provider>
|
|
167
|
-
) : (
|
|
168
|
-
children
|
|
169
|
-
)}
|
|
158
|
+
{children}
|
|
170
159
|
<ScrollRestoration nonce={nonce} />
|
|
171
160
|
<Scripts nonce={nonce} />
|
|
172
161
|
</body>
|
|
@@ -175,7 +164,23 @@ export function Layout({children}: {children?: React.ReactNode}) {
|
|
|
175
164
|
}
|
|
176
165
|
|
|
177
166
|
export default function App() {
|
|
178
|
-
|
|
167
|
+
const data = useRouteLoaderData<RootLoader>('root');
|
|
168
|
+
|
|
169
|
+
if (!data) {
|
|
170
|
+
return <Outlet />;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<Analytics.Provider
|
|
175
|
+
cart={data.cart}
|
|
176
|
+
shop={data.shop}
|
|
177
|
+
consent={data.consent}
|
|
178
|
+
>
|
|
179
|
+
<PageLayout {...data}>
|
|
180
|
+
<Outlet />
|
|
181
|
+
</PageLayout>
|
|
182
|
+
</Analytics.Provider>
|
|
183
|
+
);
|
|
179
184
|
}
|
|
180
185
|
|
|
181
186
|
export function ErrorBoundary() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {Route} from './+types/$';
|
|
2
2
|
|
|
3
|
-
export async function loader({request}:
|
|
3
|
+
export async function loader({request}: Route.LoaderArgs) {
|
|
4
4
|
throw new Response(`${new URL(request.url).pathname} not found`, {
|
|
5
5
|
status: 404,
|
|
6
6
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type {Route} from './+types/[robots.txt]';
|
|
2
2
|
import {parseGid} from '@shopify/hydrogen';
|
|
3
3
|
|
|
4
|
-
export async function loader({request, context}:
|
|
4
|
+
export async function loader({request, context}: Route.LoaderArgs) {
|
|
5
5
|
const url = new URL(request.url);
|
|
6
6
|
|
|
7
7
|
const {shop} = await context.storefront.query(ROBOTS_QUERY);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {Route} from './+types/[sitemap.xml]';
|
|
2
2
|
import {getSitemapIndex} from '@shopify/hydrogen';
|
|
3
3
|
|
|
4
4
|
export async function loader({
|
|
5
5
|
request,
|
|
6
6
|
context: {storefront},
|
|
7
|
-
}:
|
|
7
|
+
}: Route.LoaderArgs) {
|
|
8
8
|
const response = await getSitemapIndex({
|
|
9
9
|
storefront,
|
|
10
10
|
request,
|
|
@@ -14,4 +14,3 @@ export async function loader({
|
|
|
14
14
|
|
|
15
15
|
return response;
|
|
16
16
|
}
|
|
17
|
-
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
Await,
|
|
3
|
+
useLoaderData,
|
|
4
|
+
Link,
|
|
5
|
+
} from 'react-router';
|
|
6
|
+
import type {Route} from './+types/_index';
|
|
3
7
|
import {Suspense} from 'react';
|
|
4
|
-
import {Image
|
|
8
|
+
import {Image} from '@shopify/hydrogen';
|
|
5
9
|
import type {
|
|
6
10
|
FeaturedCollectionFragment,
|
|
7
11
|
RecommendedProductsQuery,
|
|
8
12
|
} from 'storefrontapi.generated';
|
|
9
13
|
import {ProductItem} from '~/components/ProductItem';
|
|
10
14
|
|
|
11
|
-
export const meta: MetaFunction = () => {
|
|
15
|
+
export const meta: Route.MetaFunction = () => {
|
|
12
16
|
return [{title: 'Hydrogen | Home'}];
|
|
13
17
|
};
|
|
14
18
|
|
|
15
|
-
export async function loader(args:
|
|
19
|
+
export async function loader(args: Route.LoaderArgs) {
|
|
16
20
|
// Start fetching non-critical data without blocking time to first byte
|
|
17
21
|
const deferredData = loadDeferredData(args);
|
|
18
22
|
|
|
@@ -26,7 +30,7 @@ export async function loader(args: LoaderFunctionArgs) {
|
|
|
26
30
|
* Load data necessary for rendering content above the fold. This is the critical data
|
|
27
31
|
* needed to render the page. If it's unavailable, the whole page should 400 or 500 error.
|
|
28
32
|
*/
|
|
29
|
-
async function loadCriticalData({context}:
|
|
33
|
+
async function loadCriticalData({context}: Route.LoaderArgs) {
|
|
30
34
|
const [{collections}] = await Promise.all([
|
|
31
35
|
context.storefront.query(FEATURED_COLLECTION_QUERY),
|
|
32
36
|
// Add other queries here, so that they are loaded in parallel
|
|
@@ -42,10 +46,10 @@ async function loadCriticalData({context}: LoaderFunctionArgs) {
|
|
|
42
46
|
* fetched after the initial page load. If it's unavailable, the page should still 200.
|
|
43
47
|
* Make sure to not throw any errors here, as it will cause the page to 500.
|
|
44
48
|
*/
|
|
45
|
-
function loadDeferredData({context}:
|
|
49
|
+
function loadDeferredData({context}: Route.LoaderArgs) {
|
|
46
50
|
const recommendedProducts = context.storefront
|
|
47
51
|
.query(RECOMMENDED_PRODUCTS_QUERY)
|
|
48
|
-
.catch((error) => {
|
|
52
|
+
.catch((error: Error) => {
|
|
49
53
|
// Log query errors, but don't throw them so the page can still render
|
|
50
54
|
console.error(error);
|
|
51
55
|
return null;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {redirect
|
|
1
|
+
import {redirect} from 'react-router';
|
|
2
|
+
import type {Route} from './+types/account.$';
|
|
2
3
|
|
|
3
4
|
// fallback wild card for all unauthenticated routes in account section
|
|
4
|
-
export async function loader({context}:
|
|
5
|
-
|
|
5
|
+
export async function loader({context}: Route.LoaderArgs) {
|
|
6
|
+
context.customerAccount.handleAuthStatus();
|
|
6
7
|
|
|
7
8
|
return redirect('/account');
|
|
8
9
|
}
|
|
@@ -5,17 +5,13 @@ import type {
|
|
|
5
5
|
} from 'customer-accountapi.generated';
|
|
6
6
|
import {
|
|
7
7
|
data,
|
|
8
|
-
type ActionFunctionArgs,
|
|
9
|
-
type LoaderFunctionArgs,
|
|
10
|
-
} from '@shopify/remix-oxygen';
|
|
11
|
-
import {
|
|
12
8
|
Form,
|
|
13
9
|
useActionData,
|
|
14
10
|
useNavigation,
|
|
15
11
|
useOutletContext,
|
|
16
|
-
type MetaFunction,
|
|
17
12
|
type Fetcher,
|
|
18
13
|
} from 'react-router';
|
|
14
|
+
import type {Route} from './+types/account.addresses';
|
|
19
15
|
import {
|
|
20
16
|
UPDATE_ADDRESS_MUTATION,
|
|
21
17
|
DELETE_ADDRESS_MUTATION,
|
|
@@ -31,17 +27,17 @@ export type ActionResponse = {
|
|
|
31
27
|
updatedAddress?: AddressFragment;
|
|
32
28
|
};
|
|
33
29
|
|
|
34
|
-
export const meta: MetaFunction = () => {
|
|
30
|
+
export const meta: Route.MetaFunction = () => {
|
|
35
31
|
return [{title: 'Addresses'}];
|
|
36
32
|
};
|
|
37
33
|
|
|
38
|
-
export async function loader({context}:
|
|
39
|
-
|
|
34
|
+
export async function loader({context}: Route.LoaderArgs) {
|
|
35
|
+
context.customerAccount.handleAuthStatus();
|
|
40
36
|
|
|
41
37
|
return {};
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
export async function action({request, context}:
|
|
40
|
+
export async function action({request, context}: Route.ActionArgs) {
|
|
45
41
|
const {customerAccount} = context;
|
|
46
42
|
|
|
47
43
|
try {
|
|
@@ -96,7 +92,11 @@ export async function action({request, context}: ActionFunctionArgs) {
|
|
|
96
92
|
const {data, errors} = await customerAccount.mutate(
|
|
97
93
|
CREATE_ADDRESS_MUTATION,
|
|
98
94
|
{
|
|
99
|
-
variables: {
|
|
95
|
+
variables: {
|
|
96
|
+
address,
|
|
97
|
+
defaultAddress,
|
|
98
|
+
language: customerAccount.i18n.language,
|
|
99
|
+
},
|
|
100
100
|
},
|
|
101
101
|
);
|
|
102
102
|
|
|
@@ -145,6 +145,7 @@ export async function action({request, context}: ActionFunctionArgs) {
|
|
|
145
145
|
address,
|
|
146
146
|
addressId: decodeURIComponent(addressId),
|
|
147
147
|
defaultAddress,
|
|
148
|
+
language: customerAccount.i18n.language,
|
|
148
149
|
},
|
|
149
150
|
},
|
|
150
151
|
);
|
|
@@ -190,7 +191,10 @@ export async function action({request, context}: ActionFunctionArgs) {
|
|
|
190
191
|
const {data, errors} = await customerAccount.mutate(
|
|
191
192
|
DELETE_ADDRESS_MUTATION,
|
|
192
193
|
{
|
|
193
|
-
variables: {
|
|
194
|
+
variables: {
|
|
195
|
+
addressId: decodeURIComponent(addressId),
|
|
196
|
+
language: customerAccount.i18n.language,
|
|
197
|
+
},
|
|
194
198
|
},
|
|
195
199
|
);
|
|
196
200
|
|