@decocms/apps 0.23.3 → 0.24.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/LICENSE +21 -0
- package/commerce/components/Image.tsx +129 -143
- package/commerce/components/JsonLd.tsx +192 -201
- package/commerce/components/Picture.tsx +65 -75
- package/commerce/sdk/analytics.ts +15 -15
- package/commerce/sdk/formatPrice.ts +13 -16
- package/commerce/sdk/url.ts +7 -7
- package/commerce/sdk/useOffer.ts +46 -57
- package/commerce/sdk/useVariantPossibilities.ts +25 -25
- package/commerce/types/commerce.ts +868 -875
- package/commerce/utils/canonical.ts +5 -8
- package/commerce/utils/constants.ts +5 -6
- package/commerce/utils/filters.ts +4 -4
- package/commerce/utils/productToAnalyticsItem.ts +52 -56
- package/commerce/utils/stateByZip.ts +42 -42
- package/package.json +23 -4
- package/shopify/actions/cart/addItems.ts +24 -25
- package/shopify/actions/cart/updateCoupons.ts +19 -20
- package/shopify/actions/cart/updateItems.ts +19 -20
- package/shopify/actions/user/signIn.ts +25 -30
- package/shopify/actions/user/signUp.ts +19 -24
- package/shopify/client.ts +24 -24
- package/shopify/index.ts +20 -18
- package/shopify/init.ts +18 -21
- package/shopify/loaders/ProductDetailsPage.ts +16 -20
- package/shopify/loaders/ProductList.ts +66 -69
- package/shopify/loaders/ProductListingPage.ts +150 -158
- package/shopify/loaders/RelatedProducts.ts +24 -27
- package/shopify/loaders/cart.ts +53 -52
- package/shopify/loaders/shop.ts +22 -27
- package/shopify/loaders/user.ts +27 -32
- package/shopify/utils/admin/admin.ts +33 -34
- package/shopify/utils/admin/queries.ts +2 -2
- package/shopify/utils/cart.ts +18 -14
- package/shopify/utils/cookies.ts +62 -65
- package/shopify/utils/enums.ts +424 -424
- package/shopify/utils/graphql.ts +44 -55
- package/shopify/utils/storefront/queries.ts +24 -29
- package/shopify/utils/storefront/storefront.graphql.gen.ts +55 -55
- package/shopify/utils/transform.ts +370 -376
- package/shopify/utils/types.ts +118 -118
- package/shopify/utils/user.ts +11 -11
- package/shopify/utils/utils.ts +135 -140
- package/vtex/actions/address.ts +86 -86
- package/vtex/actions/auth.ts +14 -27
- package/vtex/actions/checkout.ts +36 -49
- package/vtex/actions/masterData.ts +10 -27
- package/vtex/actions/misc.ts +101 -111
- package/vtex/actions/newsletter.ts +48 -52
- package/vtex/actions/orders.ts +13 -16
- package/vtex/actions/profile.ts +55 -55
- package/vtex/actions/session.ts +36 -35
- package/vtex/actions/trigger.ts +25 -25
- package/vtex/actions/wishlist.ts +51 -53
- package/vtex/client.ts +14 -42
- package/vtex/hooks/index.ts +4 -4
- package/vtex/hooks/useAutocomplete.ts +42 -48
- package/vtex/hooks/useCart.ts +153 -165
- package/vtex/hooks/useUser.ts +40 -40
- package/vtex/hooks/useWishlist.ts +70 -70
- package/vtex/inline-loaders/productDetailsPage.ts +1 -3
- package/vtex/inline-loaders/productList.ts +121 -127
- package/vtex/inline-loaders/productListingPage.ts +10 -34
- package/vtex/inline-loaders/relatedProducts.ts +1 -3
- package/vtex/inline-loaders/suggestions.ts +36 -39
- package/vtex/inline-loaders/workflowProducts.ts +45 -49
- package/vtex/invoke.ts +159 -194
- package/vtex/loaders/address.ts +49 -54
- package/vtex/loaders/brands.ts +19 -26
- package/vtex/loaders/cart.ts +24 -21
- package/vtex/loaders/catalog.ts +51 -53
- package/vtex/loaders/collections.ts +25 -27
- package/vtex/loaders/legacy.ts +487 -534
- package/vtex/loaders/logistics.ts +33 -37
- package/vtex/loaders/navbar.ts +5 -8
- package/vtex/loaders/orders.ts +28 -39
- package/vtex/loaders/pageType.ts +41 -35
- package/vtex/loaders/payment.ts +27 -37
- package/vtex/loaders/profile.ts +38 -38
- package/vtex/loaders/promotion.ts +5 -8
- package/vtex/loaders/search.ts +56 -59
- package/vtex/loaders/session.ts +22 -30
- package/vtex/loaders/user.ts +39 -41
- package/vtex/loaders/wishlist.ts +35 -35
- package/vtex/loaders/wishlistProducts.ts +3 -15
- package/vtex/loaders/workflow.ts +220 -227
- package/vtex/middleware.ts +116 -119
- package/vtex/types.ts +201 -201
- package/vtex/utils/batch.ts +13 -16
- package/vtex/utils/cookies.ts +76 -80
- package/vtex/utils/enrichment.ts +62 -42
- package/vtex/utils/fetchCache.ts +1 -4
- package/vtex/utils/index.ts +6 -6
- package/vtex/utils/intelligentSearch.ts +48 -57
- package/vtex/utils/legacy.ts +108 -124
- package/vtex/utils/pickAndOmit.ts +15 -20
- package/vtex/utils/proxy.ts +136 -146
- package/vtex/utils/resourceRange.ts +3 -3
- package/vtex/utils/segment.ts +100 -111
- package/vtex/utils/similars.ts +1 -2
- package/vtex/utils/sitemap.ts +91 -91
- package/vtex/utils/slugCache.ts +2 -6
- package/vtex/utils/slugify.ts +9 -9
- package/vtex/utils/transform.ts +1012 -1105
- package/vtex/utils/types.ts +1381 -1381
- package/vtex/utils/vtexId.ts +44 -47
- package/.github/workflows/release.yml +0 -34
- package/.releaserc.json +0 -28
- package/knip.json +0 -19
- package/tsconfig.json +0 -11
package/shopify/client.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createGraphqlClient, type GraphQLClient } from "./utils/graphql";
|
|
2
2
|
|
|
3
3
|
export interface ShopifyConfig {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
storeName: string;
|
|
5
|
+
storefrontAccessToken: string;
|
|
6
|
+
publicUrl?: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
let _client: GraphQLClient | null = null;
|
|
@@ -22,37 +22,37 @@ let _fetch: typeof fetch | undefined;
|
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
export function setShopifyFetch(fetchFn: typeof fetch) {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
_fetch = fetchFn;
|
|
26
|
+
if (_config) configureShopify(_config);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export function configureShopify(config: ShopifyConfig) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
_config = config;
|
|
31
|
+
_client = createGraphqlClient(
|
|
32
|
+
`https://${config.storeName}.myshopify.com/api/2025-04/graphql.json`,
|
|
33
|
+
{
|
|
34
|
+
"X-Shopify-Storefront-Access-Token": config.storefrontAccessToken,
|
|
35
|
+
},
|
|
36
|
+
_fetch,
|
|
37
|
+
);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export function getShopifyClient(): GraphQLClient {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
if (!_client || !_config) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
"Shopify not configured. Call configureShopify() first or check deco-shopify.json block.",
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return _client;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export function getShopifyConfig(): ShopifyConfig {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
if (!_config) {
|
|
51
|
+
throw new Error("Shopify not configured.");
|
|
52
|
+
}
|
|
53
|
+
return _config;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export function getBaseUrl(): string {
|
|
57
|
-
|
|
57
|
+
return _config?.publicUrl || "";
|
|
58
58
|
}
|
package/shopify/index.ts
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
// Client & Config
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
export { default as addItems } from "./actions/cart/addItems";
|
|
4
|
+
export { default as updateCoupons } from "./actions/cart/updateCoupons";
|
|
5
|
+
export { default as updateItems } from "./actions/cart/updateItems";
|
|
6
|
+
export { default as signIn } from "./actions/user/signIn";
|
|
7
|
+
export { default as signUp } from "./actions/user/signUp";
|
|
3
8
|
export type { ShopifyConfig } from "./client";
|
|
9
|
+
export {
|
|
10
|
+
configureShopify,
|
|
11
|
+
getBaseUrl,
|
|
12
|
+
getShopifyClient,
|
|
13
|
+
getShopifyConfig,
|
|
14
|
+
setShopifyFetch,
|
|
15
|
+
} from "./client";
|
|
4
16
|
export { initShopify, initShopifyFromBlocks } from "./init";
|
|
5
|
-
|
|
17
|
+
export type { CartLine, ShopifyCart } from "./loaders/cart";
|
|
18
|
+
// Cart
|
|
19
|
+
export { createCart, getCart } from "./loaders/cart";
|
|
20
|
+
export { default as productDetailsPageLoader } from "./loaders/ProductDetailsPage";
|
|
6
21
|
// Product Loaders
|
|
7
22
|
export { default as productListLoader } from "./loaders/ProductList";
|
|
8
|
-
export { default as productDetailsPageLoader } from "./loaders/ProductDetailsPage";
|
|
9
23
|
export { default as productListingPageLoader } from "./loaders/ProductListingPage";
|
|
10
24
|
export { default as relatedProductsLoader } from "./loaders/RelatedProducts";
|
|
11
|
-
|
|
12
|
-
// Cart
|
|
13
|
-
export { getCart, createCart } from "./loaders/cart";
|
|
14
|
-
export type { ShopifyCart, CartLine } from "./loaders/cart";
|
|
15
|
-
export { default as addItems } from "./actions/cart/addItems";
|
|
16
|
-
export { default as updateItems } from "./actions/cart/updateItems";
|
|
17
|
-
export { default as updateCoupons } from "./actions/cart/updateCoupons";
|
|
18
|
-
|
|
25
|
+
export type { Shop } from "./loaders/shop";
|
|
19
26
|
// Shop
|
|
20
27
|
export { default as shopLoader } from "./loaders/shop";
|
|
21
|
-
export type {
|
|
22
|
-
|
|
28
|
+
export type { ShopifyUser } from "./loaders/user";
|
|
23
29
|
// User
|
|
24
30
|
export { default as userLoader } from "./loaders/user";
|
|
25
|
-
export
|
|
26
|
-
export { default as signIn } from "./actions/user/signIn";
|
|
27
|
-
export { default as signUp } from "./actions/user/signUp";
|
|
28
|
-
|
|
31
|
+
export { getCartCookie, setCartCookie } from "./utils/cart";
|
|
29
32
|
// Cookie utils
|
|
30
33
|
export { getCookies, setCookie } from "./utils/cookies";
|
|
31
|
-
export { getCartCookie, setCartCookie } from "./utils/cart";
|
|
32
34
|
export { getUserCookie, setUserCookie } from "./utils/user";
|
package/shopify/init.ts
CHANGED
|
@@ -6,20 +6,17 @@ let initialized = false;
|
|
|
6
6
|
* Initialize Shopify from raw block data.
|
|
7
7
|
* The site is responsible for reading the blocks and passing the config here.
|
|
8
8
|
*/
|
|
9
|
-
export function initShopify(config: {
|
|
10
|
-
|
|
11
|
-
storefrontAccessToken: string;
|
|
12
|
-
}) {
|
|
13
|
-
if (initialized) return;
|
|
9
|
+
export function initShopify(config: { storeName: string; storefrontAccessToken: string }) {
|
|
10
|
+
if (initialized) return;
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
if (!config.storeName || !config.storefrontAccessToken) {
|
|
13
|
+
console.warn("[Shopify] Missing storeName or storefrontAccessToken.");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
console.log(`[Shopify] Initializing: ${config.storeName}.myshopify.com`);
|
|
18
|
+
configureShopify(config);
|
|
19
|
+
initialized = true;
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
/**
|
|
@@ -27,14 +24,14 @@ export function initShopify(config: {
|
|
|
27
24
|
* Looks for the "deco-shopify" block and extracts credentials.
|
|
28
25
|
*/
|
|
29
26
|
export function initShopifyFromBlocks(blocks: Record<string, any>) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
const shopifyBlock = blocks["deco-shopify"];
|
|
28
|
+
if (!shopifyBlock) {
|
|
29
|
+
console.warn("[Shopify] No deco-shopify block found.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
initShopify({
|
|
34
|
+
storeName: shopifyBlock.storeName,
|
|
35
|
+
storefrontAccessToken: shopifyBlock.storefrontAccessToken,
|
|
36
|
+
});
|
|
40
37
|
}
|
|
@@ -1,35 +1,31 @@
|
|
|
1
1
|
import type { ProductDetailsPage } from "../../commerce/types/commerce";
|
|
2
2
|
import { getShopifyClient } from "../client";
|
|
3
3
|
import { GetProduct } from "../utils/storefront/queries";
|
|
4
|
-
import {
|
|
4
|
+
import { type ProductShopify, toProductPage } from "../utils/transform";
|
|
5
5
|
import type { Metafield } from "../utils/types";
|
|
6
6
|
|
|
7
7
|
export interface Props {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
slug: string;
|
|
9
|
+
metafields?: Metafield[];
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export default async function productDetailsPageLoader(
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
props: Props,
|
|
14
|
+
url?: URL,
|
|
15
15
|
): Promise<ProductDetailsPage | null> {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const client = getShopifyClient();
|
|
17
|
+
const { slug, metafields = [] } = props;
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const splitted = slug?.split("-") ?? [];
|
|
20
|
+
const maybeSkuId = Number(splitted[splitted.length - 1]);
|
|
21
|
+
const handle = splitted.slice(0, maybeSkuId ? -1 : undefined).join("-");
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const data = await client.query<{ product?: ProductShopify }>(GetProduct, {
|
|
24
|
+
handle,
|
|
25
|
+
identifiers: metafields,
|
|
26
|
+
});
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
if (!data?.product) return null;
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
data.product,
|
|
32
|
-
url ?? new URL("https://localhost"),
|
|
33
|
-
maybeSkuId || undefined,
|
|
34
|
-
);
|
|
30
|
+
return toProductPage(data.product, url ?? new URL("https://localhost"), maybeSkuId || undefined);
|
|
35
31
|
}
|
|
@@ -1,101 +1,98 @@
|
|
|
1
1
|
import type { Product } from "../../commerce/types/commerce";
|
|
2
2
|
import { getShopifyClient } from "../client";
|
|
3
3
|
import { ProductsByCollection, SearchProducts } from "../utils/storefront/queries";
|
|
4
|
-
import {
|
|
4
|
+
import { type ProductShopify, toProduct } from "../utils/transform";
|
|
5
5
|
import type { Metafield } from "../utils/types";
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
type CollectionSortKeys,
|
|
8
|
+
type SearchSortKeys,
|
|
9
|
+
searchSortShopify,
|
|
10
|
+
sortShopify,
|
|
11
11
|
} from "../utils/utils";
|
|
12
12
|
|
|
13
13
|
export interface QueryProps {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
query: string;
|
|
15
|
+
count: number;
|
|
16
|
+
sort?: SearchSortKeys;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface CollectionProps {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
collection: string;
|
|
21
|
+
count: number;
|
|
22
|
+
sort?: CollectionSortKeys;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export interface FilterProps {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
tags?: string[];
|
|
27
|
+
productTypes?: string[];
|
|
28
|
+
productVendors?: string[];
|
|
29
|
+
priceMin?: number;
|
|
30
|
+
priceMax?: number;
|
|
31
|
+
variantOptions?: { name: string; value: string }[];
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export type Props = {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
props: QueryProps | CollectionProps;
|
|
36
|
+
filters?: FilterProps;
|
|
37
|
+
metafields?: Metafield[];
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
const isQueryList = (p: any): p is QueryProps =>
|
|
41
|
-
|
|
41
|
+
typeof p.query === "string" && typeof p.count === "number";
|
|
42
42
|
|
|
43
43
|
export default async function productListLoader(
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
expandedProps: Props,
|
|
45
|
+
url?: URL,
|
|
46
46
|
): Promise<Product[] | null> {
|
|
47
|
-
|
|
47
|
+
const client = getShopifyClient();
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
(expandedProps as unknown as Props["props"]);
|
|
49
|
+
const props = expandedProps.props ?? (expandedProps as unknown as Props["props"]);
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
const count = props.count ?? 12;
|
|
52
|
+
const metafields = expandedProps.metafields || [];
|
|
53
|
+
const sort = props.sort ?? "";
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
const filters: any[] = [];
|
|
56
|
+
expandedProps.filters?.tags?.forEach((tag) => filters.push({ tag }));
|
|
57
|
+
expandedProps.filters?.productTypes?.forEach((productType) => filters.push({ productType }));
|
|
58
|
+
expandedProps.filters?.productVendors?.forEach((productVendor) =>
|
|
59
|
+
filters.push({ productVendor }),
|
|
60
|
+
);
|
|
61
|
+
if (expandedProps.filters?.priceMin != null)
|
|
62
|
+
filters.push({ price: { min: expandedProps.filters.priceMin } });
|
|
63
|
+
if (expandedProps.filters?.priceMax != null)
|
|
64
|
+
filters.push({ price: { max: expandedProps.filters.priceMax } });
|
|
65
|
+
expandedProps.filters?.variantOptions?.forEach((variantOption) =>
|
|
66
|
+
filters.push({ variantOption }),
|
|
67
|
+
);
|
|
63
68
|
|
|
64
|
-
|
|
69
|
+
let shopifyProducts: { nodes: ProductShopify[] } | undefined;
|
|
65
70
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
identifiers: metafields,
|
|
88
|
-
...sortShopify[sort],
|
|
89
|
-
},
|
|
90
|
-
);
|
|
91
|
-
shopifyProducts = data.collection?.products;
|
|
92
|
-
}
|
|
71
|
+
if (isQueryList(props)) {
|
|
72
|
+
const data = await client.query<{ search: { nodes: ProductShopify[] } }>(SearchProducts, {
|
|
73
|
+
first: count,
|
|
74
|
+
query: props.query,
|
|
75
|
+
productFilters: filters,
|
|
76
|
+
identifiers: metafields,
|
|
77
|
+
...searchSortShopify[sort],
|
|
78
|
+
});
|
|
79
|
+
shopifyProducts = data.search;
|
|
80
|
+
} else {
|
|
81
|
+
const data = await client.query<{
|
|
82
|
+
collection?: { products: { nodes: ProductShopify[] } };
|
|
83
|
+
}>(ProductsByCollection, {
|
|
84
|
+
first: count,
|
|
85
|
+
handle: (props as CollectionProps).collection,
|
|
86
|
+
filters,
|
|
87
|
+
identifiers: metafields,
|
|
88
|
+
...sortShopify[sort],
|
|
89
|
+
});
|
|
90
|
+
shopifyProducts = data.collection?.products;
|
|
91
|
+
}
|
|
93
92
|
|
|
94
|
-
|
|
93
|
+
const baseUrl = url ?? new URL("https://localhost");
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
toProduct(p, p.variants.nodes[0], baseUrl)
|
|
98
|
-
);
|
|
95
|
+
const products = shopifyProducts?.nodes.map((p) => toProduct(p, p.variants.nodes[0], baseUrl));
|
|
99
96
|
|
|
100
|
-
|
|
97
|
+
return products ?? [];
|
|
101
98
|
}
|