@rebuy/rebuy-hydrogen 1.0.3 → 2.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/README.md CHANGED
@@ -1,407 +1,24 @@
1
1
  # Rebuy + Hydrogen Overview
2
2
 
3
- Rebuy + Hydrogen package is a web development framework used for building Shopify custom storefronts. It includes providers, components, and tooling you need to get started so you can spend your time creating intelligent shopping experiences.
3
+ Rebuy + Hydrogen package is a web development framework used for building Shopify custom storefronts. It includes providers, components, and tooling you need to get started so you can spend your time creating intelligent shopping experiences.
4
4
 
5
5
  ## How Rebuy + Hydrogen Works
6
6
 
7
- Rebuy + Hydrogen is a lightweight framework for creating personalized shopping experiences that are lightening fast. The framework is composed of:
8
- 1. Providers - these components build contextual objects that are used during network calls.
9
- 2. Containers - these components get personalized data from Rebuy and pass the resulting information to children components via props.
10
- 3. Components - these components can be used to render various merchandising UI.
11
- 4. Events - these components automatically track user behaviors.
7
+ Rebuy + Hydrogen is a lightweight framework for creating personalized shopping experiences that are lightening fast. The framework is composed of:
8
+
9
+ 1. **Providers** - these components build contextual objects that are used during network calls.
10
+ 2. **Containers** - these components get personalized data from Rebuy and pass the resulting information to children components via props.
11
+ 3. **Components** - these components can be used to render various merchandising UI.
12
+ 4. **Events** - these components automatically track user behaviors.
12
13
 
13
14
  ### Data Sources
14
15
 
15
- Rebuy + Hydrogen is powered by Rebuy's Data Sources, which allows for AI-powered product recommendations, custom defined rulesets, or mixture of human and computer derived output. Data Sources are created in Rebuy's App Admin, and the data source URL is used in your Hydrogen project. Separating the logic from the codebase allows non-technical team members to manage the merchandising rules without the need for costly code changes or redeployments.
16
+ Rebuy + Hydrogen is powered by Rebuy's Data Sources, which allows for AI-powered product recommendations, custom defined rulesets, or mixture of human and computer derived output. Data Sources are created in Rebuy's App Admin, and the data source URL is used in your Hydrogen project. Separating the logic from the codebase allows non-technical team members to manage the merchandising rules without the need for costly code changes or redeployments.
16
17
 
17
18
  ### User Interfaces
18
19
 
19
- Rebuy + Hydrogen uses a React container pattern to separate the data fetching logic and state managment from the presentional components. As such, container components do not render user interface elements. They simply pass their own properties, as well as data received from Rebuy, to their children components for consumption. This allows you to build your own user interface with your existing components, or use Rebuy's out of the box components to get up and running quickly.
20
-
21
-
22
- # Getting Started
23
-
24
- In this tutorial, you'll extend your Hydrogen app with the Rebuy + Hydrogen framework. After completing this tutorial you'll have accomplished the following:
25
-
26
- - Installed the Rebuy + Hydrogen framework
27
- - Configured your environment
28
- - Generated AI-powered product recommendations
29
-
30
- ## Requirements
31
-
32
- In order to complete this tutorial, you will need the following:
33
- - [Shopify](https://www.shopify.com/) account
34
- - [Rebuy](https://www.rebuyengine.com/) installed in your Shopify store
35
- - [Hydrogen App](https://shopify.dev/custom-storefronts/hydrogen/getting-started/create#step-1-create-a-new-hydrogen-storefront)
36
-
37
- ### Install Dependencies
38
- Additionally, you will need the following dependencies:
39
- - [npm](https://www.npmjs.com/)
40
- - [Node.js](https://nodejs.org/en/) (version 16.5.0 or higher)
41
-
42
-
43
- # Step 1: Create a new Hydrogen app
44
-
45
- You can create a Hydrogen app locally using `yarn`, `npm`, or `npx`.
46
-
47
- If you want to [integrate with an existing React framework](https://shopify.dev/custom-storefronts/hydrogen/framework/integrate-react-frameworks), like [Next.js](https://nextjs.org/) or [Gatsby](https://www.gatsbyjs.com/), then you can add the [@shopify/hydrogen](https://www.npmjs.com/package/@shopify/hydrogen) `npm` package to your project.
48
-
49
- #### 1. Navigate to your working directoy
50
- Navigate to the directory where you want to create your project and run the following command:
51
- ```bash
52
- cd <directory>
53
- ```
54
-
55
- #### 2. Create your project
56
- Create a new Hydrogen project with a default template linked to a demo-store with the following command:
57
- ```bash
58
- npm init @shopify/hydrogen -- --template demo-store
59
- ```
60
-
61
- #### 3. Name your project
62
- Follow the prompts to name your new Hydrogen application.
63
-
64
-
65
- # Step 2: Link your Shopify Storefront
66
- Connect your Hydrogen app to your Shopify storefront by updating the properties in the `hydrogen.config.js` file. You will need to [generate a Storefront API access token](https://help.shopify.com/en/manual/apps/custom-apps?shpxid=5ef5d325-B992-4F4F-3A6D-0FACECA2B482#install-the-app-and-get-the-api-access-tokens) for your Hydrogen app to communicate with your Shopify store.
67
-
68
- #### 1. Open your `hydrogen.config.js` file
69
- Open `hydrogen.config.js` file located in the root of your project directory.
70
-
71
- #### 2. Update your config file
72
- Edit your `hydrogen.config.js` file with the following changes and save the file:
73
- - Update `storeDomain` to specify your store's domain name.
74
- - Update `storefrontToken` to specify your Storefront API access token.
75
- - Update `storefrontApiVersion` to specify the Storefront API version that you want to use.
76
-
77
- ```javascript
78
- export default defineConfig({
79
- shopify: {
80
- defaultCountryCode: 'US',
81
- defaultLanguageCode: 'EN',
82
- storeDomain: 'YOUR_STORE_NAME.myshopify.com',
83
- storefrontToken: 'YOUR_STOREFRONT_ACCESS_TOKEN',
84
- storefrontApiVersion: '2022-07',
85
- },
86
- });
87
- ```
88
-
89
-
90
- # Step 3: Start a development server
91
- Create a new local development server to start testing your changes.
92
-
93
- #### 1. Navigate to your project directoy
94
- Navigate to the root of your project directory:
95
- ```bash
96
- cd <directory>
97
- ```
98
-
99
- #### 2. Start the development server
100
- Start up a new local development server on `localhost`:
101
- ```bash
102
- npm run dev
103
- ```
104
-
105
- The development environment will open automatically at [http://localhost:3000](http://localhost:3000).
106
-
107
-
108
- # Step 4: Install Rebuy + Hydrogen Package
109
- Once you have successfully created your new Hydrogen app and linked it to your Shopify store, it's now time to install the Rebuy + Hydrogen package. If you currently have your development server running, you can terminate the server with the following command `Ctrl` + `C`.
110
-
111
- #### 1. Navigate to your project directoy
112
- Navigate to the root of your project directory:
113
- ```bash
114
- cd <directory>
115
- ```
116
-
117
- #### 2. Install the Rebuy + Hydrogen package
118
- Install the Rebuy + Hydrogen package from [npm](https://www.npmjs.com/) with the following command:
119
- ```bash
120
- npm install @rebuy/rebuy-hydrogen
121
- ```
122
-
123
- #### 3. Update your `.env` with your Rebuy public API key
124
- You can find your [Rebuy public API key](https://rebuyengine.com/account/keys) in Rebuy admin > Account > API Keys. Once located, update your `.env` with the following:
125
- ```
126
- PUBLIC_REBUY_API_KEY=yourRebuyAPIKey
127
- ```
128
- *If your project does not have an `.env` file, you can create a new one and save it in the project root directory.*
129
-
130
-
131
- # Step 5: Build Personalized Shopping Experiences
132
- Once you have included Rebuy + Hydrogen in your project, it's time to start building dynamic, personalized shopping experiences! In this tutorial, we will accomplish the following:
133
-
134
- - Customize the product details page
135
- - Display a list of AI-powered product recommendations
136
- - Display a list of recently viewed products
137
-
138
- #### 1. Create a Recommended Products Component
139
- Create a new file in `src/components` called `ProductRecommendations.jsx`, and add the following to the file:
140
-
141
- ```javascript
142
- import {
143
- ProductOptionsProvider,
144
- AddToCartButton,
145
- } from '@shopify/hydrogen';
146
- import ProductCard from './ProductCard';
147
- import {
148
- BUTTON_PRIMARY_CLASSES,
149
- } from './Button.client';
150
-
151
- function AddToCartMarkup({product}) {
152
- const selectedVariant = product.variants.nodes[0];
153
- const isOutOfStock = !selectedVariant.availableForSale;
154
-
155
- return (
156
- <div className="space-y-2 mb-8">
157
- <AddToCartButton
158
- className={BUTTON_PRIMARY_CLASSES}
159
- disabled={isOutOfStock}
160
- product={product}
161
- variantId={selectedVariant.id}
162
- attributes={[
163
- {key: '_source', value: 'Rebuy'},
164
- {key: '_attribution', value: 'Product Recommendations'
165
- }]}
166
- >
167
- {isOutOfStock ? 'Out of stock' : 'Add to bag'}
168
- </AddToCartButton>
169
- </div>
170
- );
171
- }
172
-
173
- export default function ProductRecommendations(props) {
174
- const {product, products, metadata} = props;
175
-
176
- console.log('ProductRecommendations component properties:', props);
177
-
178
- return(
179
- <>
180
- <h1 className="font-bold text-4xl md:text-5xl text-gray-900 mb-6 mt-6">If you like {product.title}, you'll also love these</h1>
181
- <ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
182
- {products.map((product) => (
183
- <li key={product.id}>
184
- <ProductOptionsProvider data={product} initialVariantId={product.variants.nodes[0].id}>
185
- <ProductCard product={product} />
186
- <AddToCartMarkup product={product} />
187
- </ProductOptionsProvider>
188
- </li>
189
- ))}
190
- </ul>
191
- </>
192
- )
193
- }
194
- ```
195
-
196
- #### 2. Create a Recently Viewed Products Component
197
- Create a new file in `src/components` called `RecentlyViewedProducts.jsx`, and add the following to the file:
198
-
199
- ```javascript
200
- import {
201
- ProductOptionsProvider,
202
- AddToCartButton,
203
- } from '@shopify/hydrogen';
204
- import ProductCard from './ProductCard';
205
- import {
206
- BUTTON_PRIMARY_CLASSES,
207
- } from './Button.client';
208
-
209
- function AddToCartMarkup({product}) {
210
- const selectedVariant = product.variants.nodes[0];
211
- const isOutOfStock = !selectedVariant.availableForSale;
212
-
213
- return (
214
- <div className="space-y-2 mb-8">
215
- <AddToCartButton
216
- className={BUTTON_PRIMARY_CLASSES}
217
- disabled={isOutOfStock}
218
- product={product}
219
- variantId={selectedVariant.id}
220
- attributes={[
221
- {key: '_source', value: 'Rebuy'},
222
- {key: '_attribution', value: 'Recently Viewed Product'
223
- }]}
224
- >
225
- {isOutOfStock ? 'Out of stock' : 'Add to bag'}
226
- </AddToCartButton>
227
- </div>
228
- );
229
- }
230
-
231
- export default function RecentlyViewedProducts(props) {
232
- const {product, products, metadata} = props;
233
-
234
- console.log('RecentlyViewedProducts component properties:', props);
235
-
236
- return(
237
- <>
238
- <h1 className="font-bold text-4xl md:text-5xl text-gray-900 mb-6 mt-6">Recently Viewed</h1>
239
- <ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
240
- {products.map((product) => (
241
- <li key={product.id}>
242
- <ProductOptionsProvider data={product} initialVariantId={product.variants.nodes[0].id}>
243
- <ProductCard product={product} />
244
- <AddToCartMarkup product={product} />
245
- </ProductOptionsProvider>
246
- </li>
247
- ))}
248
- </ul>
249
- </>
250
- )
251
- }
252
- ```
253
-
254
- #### 3. Install the RebuyContextProvider
255
- Open `/src/App.server.jsx` file and install the `RebuyContextProvider`, so all Rebuy API calls are enriched with contextual information. Open the file and make the following edits:
256
-
257
- ##### Import the provider
258
- ```javascript
259
- import {RebuyContextProvider} from '@rebuy/rebuy-hydrogen/RebuyContextProvider.client';
260
- ```
261
-
262
- ##### Add RebuyContextProvider component
263
- Wrap the `<Route>` component with the `RebuyContextProvider`, as follows:
264
- ```html
265
- <RebuyContextProvider>
266
- <Route path="*" page={<NotFound />} />
267
- </RebuyContextProvider>
268
- ```
269
-
270
- #### 4. Customize the Product Details Page
271
- Let's include our new components to the `ProductDetails` client component, located in `src/components/ProductDetails.client.jsx`. We will be wrapping our new components with a `RebuyWidgetContainer` components, which will give us an interface to request data from Rebuy.
272
-
273
- ##### Import your new UI components
274
- Import the `ProductRecommendations` and `RecentlyViewedProducts` components in to the `ProductDetails.client.jsx` file.
275
- ```javascript
276
- import ProductRecommendations from './ProductRecommendations';
277
- import RecentlyViewedProducts from './RecentlyViewedProducts';
278
- ```
279
-
280
- ##### Import Rebuy components
281
- Import the `RebuyWidgetContainer` and `RebuyProductViewed` components in to the `ProductDetails.client.jsx` file.
282
- ```javascript
283
- import RebuyWidgetContainer from '@rebuy/rebuy-hydrogen/RebuyWidgetContainer.client';
284
- import RebuyProductViewed from '@rebuy/rebuy-hydrogen/RebuyProductViewed.client';
285
- ```
286
-
287
- ##### Add imported components in to the `ProductDetails` functional component
288
- Before the closing `</ProductOptionsProvider>` tag, insert our imported components:
289
- ```html
290
- <RebuyWidgetContainer product={product} dataSource="/api/v1/products/recommended" limit="6">
291
- <ProductRecommendations />
292
- </RebuyWidgetContainer>
293
-
294
- <RebuyWidgetContainer product={product} dataSource="/api/v1/products/viewed" limit="6">
295
- <RecentlyViewedProducts />
296
- </RebuyWidgetContainer>
297
-
298
- <RebuyProductViewed product={product} />
299
- ```
300
-
301
- ##### Review your changes
302
- When you look at the product details page, you should now see two new sections:
303
- 1. AI-powered product recommendations
304
- 2. Recently viewed products
305
-
306
- Additionally, we included the `<RebuyProductViewed product={product} />` component, which will automatically send a `user -> viewed -> product` event to Rebuy. This event is powering the recently viewed component in real-time! As you navigate the site, your browsing history will be tracked and display for easy navigation to previous product pages!
307
-
308
-
309
- # Documentation
310
- Below you will find documentation on each of the components included in the Rebuy + Hydrogen package.
311
-
312
- ## RebuyContextProvider
313
- The `RebuyContextProvider` is a React provider component. It is responsible for collecting environment and state-based information as a `RebuyContext`. It will gather information about the current URL, session UTM parameters, as well as cart information such as the cart token, subtotal, line items, etc. As a provider component, no UI is rendered, it simply creates a React context value that can be requested by any child component.
314
-
315
- The `RebuyContextProvider` is a client component, which means it renders in the browser. It has two dependencies, which are `useUrl` and `useCart`, so the component should be located high up in the component tree but **below** the `ShopifyProvider` and `CartProvider`.
316
-
317
- The recommended location is in `src/App.server.jsx` inside `<Router>` and wrapping **both** `<FileRoutes>` and the `<Route>` components, which makes the `RebuyContext` automatically available on all pages across the site.
318
-
319
- #### Import the RebuyContextProvider
320
- ```javascript
321
- import {RebuyContextProvider} from '@rebuy/rebuy-hydrogen/RebuyContextProvider.client';
322
- ```
323
-
324
- #### Wrap the Route compontent with RebuyContextProvider
325
- ```html
326
- <Router>
327
- <RebuyContextProvider>
328
- <FileRoutes
329
- basePath={countryCode ? `/${countryCode}/` : undefined}
330
- routes={routes}
331
- />
332
- <Route path="*" page={<NotFound />} />
333
- </RebuyContextProvider>
334
- </Router>
335
- ```
336
-
337
- #### Access RebuyContext
338
- Rebuy + Hydrogen components will automatically include and use `RebuyContext`, but if you would like to use this context information in your own components, you certainly can!
339
-
340
- ```javascript
341
- import React from 'react';
342
- import {RebuyContext} from '@rebuy/rebuy-hydrogen/RebuyContextProvider.client';
343
-
344
- // Access context parameters with React's useContext method
345
- const contextParameters = React.useContext(RebuyContext);
346
- ```
347
-
348
- ## RebuyWidgetContainer
349
- The `RebuyWidgetContainer` is a React container component. This component is responsible for fetching data and state management. It does not render any UI components, and will pass along its properties and data received from Rebuy to all of their children components. This abstraction allows you to use your existing application components or Rebuy's out of the box components.
350
-
351
- #### Component properties
352
- The component accepts argments via props to allow for different API responses. All props listed below are optional, if no `dataSource` prop is provided then `'/api/v1/products/recommended'` will be used.
353
-
354
- | Property | Value |
355
- | -------- | ----- |
356
- | dataSource | `{String}` A relative URL path of your Rebuy data source (i.e. '/api/v1/products/recommended) |
357
- | product | `{Object}` A product object with an `id` |
358
- | productId | `{String}` A product ID string in graphQL URL format |
359
- | variant | `{Object}` A variant object with an `id` |
360
- | variantId | `{String}` A variant ID string in graphQL URL format |
361
- | collection | `{Object}` A collection object with an `id` |
362
- | collectionId | `{String}` A collection ID string in graphQL URL format |
363
- | limit | `{Number}` A number of products to return from Rebuy's API |
364
- | options | `{Object}` An object with key, value pairs of Rebuy REST API arguments (i.e. metafields: yes) |
365
-
366
- #### Children components properties
367
- All child components will inherit the container props, in addition to two container generated props `products` and `metadata`. So if you've provided a `product={product}` prop to the container, then each child compontent will have the following properties `product`, `products`, and `metadata`.
368
-
369
- | Property | Value |
370
- | -------- | ----- |
371
- | products | `{Array}` An array of product objects returned from Rebuy's API |
372
- | metadata | `{Object}` An object with metadata about the input data, rules matched, rules not matched, excluded products, and cache info |
373
-
374
- #### Example implementation
375
- In the following example, we are using an example compontent called `ProductRecommendations` which is a UI component used to render data provided to it via props by the `RebuyWidgetContainer` component.
376
-
377
- When the components are rendered, it will display `6` products powered by Rebuy's AI algorithm based on the provided input `product`. So if the input product was ketchup, you would likely see recommended products like mustard, mayo, or other condiments.
378
-
379
- ```javascript
380
- import ProductRecommendations from './ProductRecommendations';
381
- import RebuyWidgetContainer from '@rebuy/rebuy-hydrogen/RebuyWidgetContainer.client';
382
-
383
- <RebuyWidgetContainer product={product} dataSource="/api/v1/products/recommended" limit="6">
384
- <ProductRecommendations />
385
- </RebuyWidgetContainer>
386
- ```
387
-
388
- ## RebuyProductViewed
389
- The `RebuyProductViewed` is a client component, which helps automate customer browsing behavior. This behavior is used to train the AI as well as power recently viewed products. This component would typically live in your `ProductDetails` component, so as a customer is browsing your site we automatically record the product pages they are viewing.
390
-
391
- #### Component properties
392
- The component accepts argments via props to allow for registering different products that have been viewed. One of the following properties are required to fire the event.
393
-
394
- | Property | Value |
395
- | -------- | ----- |
396
- | product | `{Object}` A product object with an `id` |
397
- | productId | `{String}` A product ID string in graphQL URL format |
398
- | productHandle | `{String}` A product handle string |
399
-
400
- #### Example implementation
401
- The following example would be included in the `ProductDetails.client.jsx` component to automatically register customer product view events. The current product displayed is passed to the `RebuyProductViewed` component via props as `product={product}`.
20
+ Rebuy + Hydrogen uses a React container pattern to separate the data fetching logic and state management from the presentational components. As such, container components do not render user interface elements. They simply pass their own properties, as well as data received from Rebuy, to their children components for consumption. This allows you to build your own user interface with your existing components, or use Rebuy's out of the box components to get up and running quickly.
402
21
 
403
- ```javascript
404
- import RebuyProductViewed from '@rebuy/rebuy-hydrogen/RebuyProductViewed.client';
22
+ ### Getting Started
405
23
 
406
- <RebuyProductViewed product={product} />
407
- ```
24
+ To get started, please visit our [Rebuy + Shopify Hydrogen documentation](https://developers.rebuyengine.com/reference/shopify-hydrogen-getting-started).
@@ -1,13 +1,33 @@
1
- import { useUrl, useCart } from '@shopify/hydrogen';
2
-
3
- import React, { createContext, useMemo } from 'react';
4
-
1
+ import { RebuyClient } from '@rebuy/rebuy';
2
+ import { RebuyContext } from '@rebuy/rebuy-hydrogen/RebuyContexts.client';
5
3
  import * as Utilities from '@rebuy/rebuy/utilities';
4
+ import { useCart, useShop, useUrl } from '@shopify/hydrogen';
5
+ import { useEffect, useMemo, useState } from 'react';
6
+
7
+ const REBUY_API_KEY = import.meta.env.PUBLIC_REBUY_API_KEY;
8
+ const API = '/api/v1';
9
+
10
+ const getEncodedAttributes = (attributes) =>
11
+ encodeURIComponent(
12
+ JSON.stringify(
13
+ attributes.reduce(
14
+ (merged, { key, value }) => ({ ...merged, [key]: value }),
15
+ {}
16
+ )
17
+ )
18
+ );
6
19
 
7
- export const RebuyContext = createContext(null);
8
-
9
- export function RebuyContextProvider({ children }) {
20
+ export const RebuyContextProvider = ({ children }) => {
21
+ // Shopify
22
+ const cart = useCart();
23
+ const shop = useShop();
10
24
  const url = useUrl();
25
+
26
+ // Default state
27
+ const [initialized, setInitialized] = useState(false);
28
+ const [rebuyConfig, setRebuyConfig] = useState(null);
29
+ const [config, setConfig] = useState({ shop: null });
30
+ // const [Rebuy, setRebuy] = useState(null);
11
31
  const queryObject = Utilities.queryStringToObject(url.search);
12
32
  const utmObject = Utilities.utmObjectFromString(url);
13
33
 
@@ -17,114 +37,186 @@ export function RebuyContextProvider({ children }) {
17
37
  }
18
38
  }
19
39
 
20
- const cart = useCart();
21
-
22
- const contextParameters = {};
23
-
24
- // Set URL
25
- contextParameters.url = url.href;
40
+ // Initialization
41
+ useEffect(() => {
42
+ const getRebuyConfig = async () => {
43
+ try {
44
+ const request = {
45
+ url: `${API}/user/config`,
46
+ parameters: { shop: shop.storeDomain },
47
+ };
48
+
49
+ const { data: rebuy, ...response } = await new RebuyClient(
50
+ REBUY_API_KEY
51
+ ).getDataFromCDN(request.url, request.parameters);
52
+
53
+ // Missing Rebuy shop data?
54
+ if (!rebuy?.shop) {
55
+ throw new Error(
56
+ 'Rebuy configuration is not properly set up - missing shop',
57
+ { cause: response }
58
+ );
59
+ }
26
60
 
27
- // Set time
28
- if (queryObject.hasOwnProperty('time')) {
29
- contextParameters.time = queryObject.time;
30
- }
61
+ setRebuyConfig(rebuy);
62
+ } catch (err) {
63
+ console.warn('Error fetching Rebuy shop config');
64
+ console.error(err, err.cause);
65
+ }
66
+ };
31
67
 
32
- // Cart object
33
- const cartContext = {};
68
+ const onBeforeReady = async () => {
69
+ // Initializing...
70
+ if (!rebuyConfig?.shop) {
71
+ return await getRebuyConfig();
72
+ }
73
+ };
74
+
75
+ onBeforeReady();
76
+ }, [rebuyConfig, shop]);
77
+
78
+ useEffect(() => {
79
+ const applyConfig = async () => {
80
+ // Still fetching Rebuy config OR config already applied. Abort!
81
+ if (!rebuyConfig || config.shop) return;
82
+
83
+ // Bring it all together
84
+ const appConfig = { ...config, ...rebuyConfig };
85
+
86
+ setConfig(appConfig);
87
+ };
88
+
89
+ applyConfig();
90
+ }, [config, rebuyConfig]);
91
+
92
+ useEffect(() => {
93
+ const onReady = () => {
94
+ // Still initializing... Abort!
95
+ if (!config.shop) return;
96
+
97
+ // Initialized!
98
+ setInitialized(true);
99
+ // const { api_key, cache_key } = config.shop;
100
+ // setRebuy(new RebuyClient(api_key, { cache_key }));
101
+ };
102
+
103
+ onReady();
104
+ }, [config]);
105
+
106
+ const contextParameters = useMemo(() => {
107
+ // Still initializing... Abort!
108
+ if (!config.shop) return null;
109
+
110
+ const { cache_key } = config.shop;
111
+ const contextParameters = {
112
+ url: url.href,
113
+ cache_key,
114
+ };
115
+ // Cart object
116
+ const cartContext = {};
117
+
118
+ // Set time
119
+ if (Object.prototype.hasOwnProperty.call(queryObject, 'time')) {
120
+ contextParameters.time = queryObject.time;
121
+ }
34
122
 
35
- // Set Cart: token
36
- if (cart.id) {
37
- cartContext.token = Utilities.getIdFromGraphUrl(cart.id, 'Cart');
38
- contextParameters.cart_token = Utilities.getIdFromGraphUrl(cart.id, 'Cart');
39
- }
123
+ // Set Cart: token
124
+ if (cart.id) {
125
+ cartContext.token = Utilities.getIdFromGraphUrl(cart.id, 'Cart');
126
+ contextParameters.cart_token = Utilities.getIdFromGraphUrl(
127
+ cart.id,
128
+ 'Cart'
129
+ );
130
+ }
40
131
 
41
- // Set Cart: subtotal
42
- if (
43
- cart.estimatedCost &&
44
- cart.estimatedCost.subtotalAmount &&
45
- cart.estimatedCost.subtotalAmount &&
46
- cart.estimatedCost.subtotalAmount.amount
47
- ) {
48
- cartContext.subtotal = Utilities.amountToCents(cart.estimatedCost.subtotalAmount.amount);
49
- contextParameters.cart_subtotal = Utilities.amountToCents(cart.estimatedCost.subtotalAmount.amount);
50
- }
132
+ // Set Cart: subtotal
133
+ if (cart.cost?.subtotalAmount?.amount) {
134
+ const { amount } = cart.cost.subtotalAmount;
51
135
 
52
- // Set Cart: line count
53
- if (typeof cart.lines != 'undefined') {
54
- cartContext.line_count = cart.lines.length;
55
- contextParameters.cart_count = cart.lines.length;
56
- contextParameters.cart_line_count = cart.lines.length;
57
- }
136
+ cartContext.subtotal = Utilities.amountToCents(amount);
137
+ contextParameters.cart_subtotal = Utilities.amountToCents(amount);
138
+ }
58
139
 
59
- // Set Cart: item count
60
- if (typeof cart.totalQuantity != 'undefined') {
61
- cartContext.item_count = cart.totalQuantity;
62
- contextParameters.cart_item_count = cart.totalQuantity;
63
- }
140
+ // Set Cart: line count
141
+ if (typeof cart.lines != 'undefined') {
142
+ const totalLines = cart.lines.length;
64
143
 
65
- // Set Cart: line items
66
- if (typeof cart.lines != 'undefined') {
67
- cartContext.items = [];
68
- for (let i = 0; i < cart.lines.length; i++) {
69
- const cartItem = cart.lines[i];
144
+ cartContext.line_count = totalLines;
145
+ contextParameters.cart_count = totalLines;
146
+ contextParameters.cart_line_count = totalLines;
147
+ }
70
148
 
71
- const item = {};
149
+ // Set Cart: item count
150
+ if (typeof cart.totalQuantity != 'undefined') {
151
+ const { totalQuantity } = cart;
72
152
 
73
- if (cartItem.product?.id) {
74
- item.product_id = Utilities.getIdFromGraphUrl(cartItem.product.id, 'Product');
75
- }
153
+ cartContext.item_count = totalQuantity;
154
+ contextParameters.cart_item_count = totalQuantity;
155
+ }
76
156
 
77
- if (cartItem.merchandise?.id) {
78
- item.variant_id = Utilities.getIdFromGraphUrl(cartItem.merchandise.id, 'ProductVariant');
79
- }
157
+ // Set Cart: line items
158
+ if (typeof cart.lines != 'undefined') {
159
+ cartContext.items = [];
80
160
 
81
- item.quantity = cartItem.quantity;
161
+ for (const cartItem of cart.lines) {
162
+ const item = {
163
+ quantity: cartItem.quantity,
164
+ };
82
165
 
83
- if (cartItem.attributes) {
84
- const attributes = {};
166
+ if (cartItem.product?.id) {
167
+ item.product_id = Utilities.getIdFromGraphUrl(
168
+ cartItem.product.id,
169
+ 'Product'
170
+ );
171
+ }
85
172
 
86
- for (let i = 0; i < cartItem.attributes.length; i++) {
87
- const key = cartItem.attributes[i].key;
88
- const value = cartItem.attributes[i].value;
89
- attributes[key] = value;
173
+ if (cartItem.merchandise?.id) {
174
+ item.variant_id = Utilities.getIdFromGraphUrl(
175
+ cartItem.merchandise.id,
176
+ 'ProductVariant'
177
+ );
90
178
  }
91
179
 
92
- item.properties = JSON.stringify(attributes);
93
- }
180
+ if (cartItem.attributes) {
181
+ item.properties = getEncodedAttributes(cartItem.attributes);
182
+ }
94
183
 
95
- // TBD: item.selling_plan
184
+ // TBD: item.selling_plan
96
185
 
97
- cartContext.items.push(item);
186
+ cartContext.items.push(item);
187
+ }
98
188
  }
99
- }
100
189
 
101
- // Set Cart: attributes
102
- if (cart.attributes) {
103
- const attributes = {};
104
-
105
- for (let i = 0; i < cart.attributes.length; i++) {
106
- const key = cart.attributes[i].key;
107
- const value = cart.attributes[i].value;
108
- attributes[key] = value;
190
+ // Set Cart: attributes
191
+ if (cart.attributes) {
192
+ cartContext.attributes = getEncodedAttributes(cart.attributes);
109
193
  }
110
194
 
111
- cartContext.attributes = JSON.stringify(attributes);
112
- }
195
+ // Set Cart: notes
196
+ if (cart.note) {
197
+ cartContext.note = cart.note;
198
+ }
113
199
 
114
- // Set Cart: notes
115
- if (cart.note) {
116
- cartContext.note = cart.note;
117
- }
200
+ // Set cart
201
+ contextParameters.cart = cartContext;
118
202
 
119
- // Set cart
120
- contextParameters.cart = cartContext;
203
+ return contextParameters;
204
+ }, [cart, config, queryObject, url]);
121
205
 
122
- const value = useMemo(
123
- () => ({
124
- ...contextParameters,
125
- }),
126
- [contextParameters]
206
+ // Static reference (JSON) + memoization
207
+ // ^ prevent re-rendering children when context params are unchanged
208
+ const contextParametersJSON = JSON.stringify(contextParameters);
209
+ const contextValue = useMemo(
210
+ () => ({ contextParameters: JSON.parse(contextParametersJSON) }),
211
+ [contextParametersJSON]
127
212
  );
128
213
 
129
- return React.createElement(RebuyContext.Provider, { value }, children);
130
- }
214
+ // Still initializing...
215
+ if (!initialized) return null;
216
+
217
+ return (
218
+ <RebuyContext.Provider value={contextValue}>
219
+ {children}
220
+ </RebuyContext.Provider>
221
+ );
222
+ };
@@ -0,0 +1,3 @@
1
+ import { createContext } from 'react';
2
+
3
+ export const RebuyContext = createContext(null);
@@ -0,0 +1,63 @@
1
+ import { AddToCartButton, ProductOptionsProvider } from '@shopify/hydrogen';
2
+ import { Section } from '~/components';
3
+ import { ProductCard } from '~/components/cards';
4
+ import { Button } from '~/components/elements';
5
+
6
+ const AddToCartMarkup = ({ product }) => {
7
+ const selectedVariant = product.variants.nodes[0];
8
+ const isOutOfStock = !selectedVariant.availableForSale;
9
+
10
+ return (
11
+ <div className="space-y-2 mb-8">
12
+ <AddToCartButton
13
+ disabled={isOutOfStock}
14
+ product={product}
15
+ variantId={selectedVariant?.id}
16
+ quantity={1}
17
+ accessibleAddingToCartLabel="Adding item to your cart"
18
+ type="button"
19
+ attributes={[
20
+ { key: '_source', value: 'Rebuy' },
21
+ { key: '_attribution', value: 'Product Recommendations' },
22
+ ]}
23
+ >
24
+ <Button
25
+ width="full"
26
+ variant={isOutOfStock ? 'secondary' : 'primary'}
27
+ as="span"
28
+ >
29
+ {isOutOfStock ? 'Out of stock' : 'Add to bag'}
30
+ </Button>
31
+ </AddToCartButton>
32
+ </div>
33
+ );
34
+ };
35
+
36
+ export const RebuyProductRecommendations = ({ product, products }) => {
37
+ const title = `Like ${product.title}? You may also like:`;
38
+
39
+ return (
40
+ products.length > 0 && (
41
+ <Section heading={title} padding="y">
42
+ <ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 px-6 md:px-8 lg:px-12">
43
+ {products.map((product) => (
44
+ <li key={product.id}>
45
+ <ProductOptionsProvider
46
+ data={product}
47
+ initialVariantId={product.variants.nodes[0].id}
48
+ >
49
+ <ProductCard
50
+ product={product}
51
+ className={'w-100'}
52
+ />
53
+ <AddToCartMarkup product={product} />
54
+ </ProductOptionsProvider>
55
+ </li>
56
+ ))}
57
+ </ul>
58
+ </Section>
59
+ )
60
+ );
61
+ };
62
+
63
+ export default RebuyProductRecommendations;
@@ -1,42 +1,62 @@
1
- import React, { useState, useEffect } from 'react';
2
-
1
+ import { RebuyClient } from '@rebuy/rebuy';
3
2
  import * as Utilities from '@rebuy/rebuy/utilities';
4
- import RebuyClient from '@rebuy/rebuy/client';
3
+ import { useEffect, useMemo, useState } from 'react';
5
4
 
6
5
  const REBUY_API_KEY = import.meta.env.PUBLIC_REBUY_API_KEY;
7
6
 
8
- export default function RebuyProductViewed(props) {
9
- const { product, productId, productHandle, children } = props;
7
+ export const RebuyProductViewed = ({ children, ...props }) => {
8
+ const { product, productId, productHandle } = props;
10
9
 
10
+ // eslint-disable-next-line no-unused-vars
11
11
  const [event, setEvent] = useState(null);
12
+ const [Rebuy, setRebuy] = useState(null);
13
+ const [initialized, setInitialized] = useState(false);
14
+ const shopifyProductId = product?.id ?? productId ?? null;
15
+
16
+ const request = useMemo(() => {
17
+ const request = {
18
+ parameters: {},
19
+ };
20
+
21
+ if (shopifyProductId) {
22
+ request.parameters.shopify_product_id = Utilities.getIdFromGraphUrl(
23
+ shopifyProductId,
24
+ 'Product'
25
+ );
26
+ } else if (productHandle) {
27
+ request.parameters.shopify_product_handle = productHandle;
28
+ }
29
+
30
+ return request;
31
+ }, [productHandle, shopifyProductId]);
12
32
 
13
- const Rebuy = new RebuyClient(REBUY_API_KEY);
33
+ useEffect(() => {
34
+ const recordView = async () => {
35
+ const response = await Rebuy.trackProductViewed(request.parameters);
14
36
 
15
- const request = {
16
- parameters: {},
17
- };
37
+ setEvent(response.data);
38
+ };
18
39
 
19
- if (product) {
20
- request.parameters.shopify_product_id = Utilities.getIdFromGraphUrl(product.id, 'Product');
21
- } else if (productId) {
22
- request.parameters.shopify_product_id = Utilities.getIdFromGraphUrl(productId, 'Product');
23
- } else if (productHandle) {
24
- request.parameters.shopify_product_handle = productHandle;
25
- }
40
+ if (!initialized) {
41
+ const client = new RebuyClient(REBUY_API_KEY);
26
42
 
27
- if (Object.keys(request.parameters).length == 0) {
28
- console.log('No parameters!');
29
- return;
30
- }
43
+ setRebuy(client);
44
+ setInitialized(true);
31
45
 
32
- useEffect(() => {
46
+ // Abort
47
+ return;
48
+ }
49
+
50
+ // Initialized
33
51
  recordView();
34
- }, []);
52
+ }, [Rebuy, request, initialized]);
53
+
54
+ if (Object.keys(request.parameters).length === 0) {
55
+ console.warn('No parameters!');
56
+ return;
57
+ }
35
58
 
36
- const recordView = async () => {
37
- const response = await Rebuy.trackProductViewed(request.parameters);
38
- setEvent(response.data);
39
- };
59
+ return children;
60
+ };
40
61
 
41
- return <>{children}</>;
42
- }
62
+ export default RebuyProductViewed;
@@ -0,0 +1,65 @@
1
+ import { AddToCartButton, ProductOptionsProvider } from '@shopify/hydrogen';
2
+ import { Section } from '~/components';
3
+ import { ProductCard } from '~/components/cards';
4
+ import { Button } from '~/components/elements';
5
+
6
+ const AddToCartMarkup = ({ product }) => {
7
+ const selectedVariant = product.variants.nodes[0];
8
+ const isOutOfStock = !selectedVariant.availableForSale;
9
+
10
+ return (
11
+ <div className="space-y-2 mb-8">
12
+ <AddToCartButton
13
+ disabled={isOutOfStock}
14
+ product={product}
15
+ variantId={selectedVariant?.id}
16
+ quantity={1}
17
+ accessibleAddingToCartLabel="Adding item to your cart"
18
+ type="button"
19
+ attributes={[
20
+ { key: '_source', value: 'Rebuy' },
21
+ { key: '_attribution', value: 'Recently Viewed Product' },
22
+ ]}
23
+ >
24
+ <Button
25
+ width="full"
26
+ variant={isOutOfStock ? 'secondary' : 'primary'}
27
+ as="span"
28
+ >
29
+ {isOutOfStock ? 'Out of stock' : 'Add to bag'}
30
+ </Button>
31
+ </AddToCartButton>
32
+ </div>
33
+ );
34
+ };
35
+
36
+ export const RebuyRecentlyViewedProducts = ({ product, products }) => {
37
+ const title = 'Recently Viewed';
38
+ // Filter current product from recently viewed
39
+ const filtered = products.filter((recent) => recent.id !== product.id);
40
+
41
+ return (
42
+ filtered.length > 0 && (
43
+ <Section heading={title} padding="y">
44
+ <ul className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16 px-6 md:px-8 lg:px-12">
45
+ {filtered.map((product) => (
46
+ <li key={product.id}>
47
+ <ProductOptionsProvider
48
+ data={product}
49
+ initialVariantId={product.variants.nodes[0].id}
50
+ >
51
+ <ProductCard
52
+ product={product}
53
+ className={'w-100'}
54
+ />
55
+ <AddToCartMarkup product={product} />
56
+ </ProductOptionsProvider>
57
+ </li>
58
+ ))}
59
+ </ul>
60
+ </Section>
61
+ )
62
+ );
63
+ };
64
+
65
+ export default RebuyRecentlyViewedProducts;
@@ -1,97 +1,136 @@
1
- import React, { useState, useEffect, useContext } from 'react';
2
- import { RebuyContext } from './RebuyContextProvider.client';
3
- import { flattenConnection } from '@shopify/hydrogen';
4
-
1
+ import { RebuyClient } from '@rebuy/rebuy';
2
+ import { RebuyContext } from '@rebuy/rebuy-hydrogen/RebuyContexts.client';
5
3
  import * as Utilities from '@rebuy/rebuy/utilities';
6
- import RebuyClient from '@rebuy/rebuy/client';
4
+ import { flattenConnection, useCart } from '@shopify/hydrogen';
5
+ import React, { useContext, useEffect, useMemo, useState } from 'react';
7
6
 
8
7
  const REBUY_API_KEY = import.meta.env.PUBLIC_REBUY_API_KEY;
9
8
 
10
- export default function RebuyWidgetContainer(props) {
11
- const { product, productId, variant, variantId, collection, collectionId, dataSource, limit, options, children } =
12
- props;
9
+ export const RebuyWidgetContainer = ({ children, ...props }) => {
10
+ const {
11
+ collection,
12
+ collectionId,
13
+ dataSource,
14
+ limit,
15
+ options,
16
+ product,
17
+ productId,
18
+ variant,
19
+ variantId,
20
+ } = props;
13
21
 
14
- const contextParameters = useContext(RebuyContext);
22
+ const cart = useCart();
23
+ const { contextParameters } = useContext(RebuyContext);
15
24
  const [products, setProducts] = useState([]);
16
25
  const [metadata, setMetadata] = useState([]);
26
+ const [Rebuy, setRebuy] = useState(null);
27
+ const [initialized, setInitialized] = useState(false);
28
+ const isCartReady = ['uninitialized', 'idle'].includes(cart.status); // uninitialized, creating, fetching, updating, idle
29
+ const shopifyProductId = product?.id ?? productId ?? null;
30
+ const shopifyVariantId = variant?.id ?? variantId ?? null;
31
+ const shopifyCollectionId = collection?.id ?? collectionId ?? null;
32
+
33
+ // Initialize Rebuy API client
34
+ useEffect(() => {
35
+ if (!Rebuy) {
36
+ const client = new RebuyClient(REBUY_API_KEY);
37
+ client.setContextParameters(contextParameters);
17
38
 
18
- const Rebuy = new RebuyClient(REBUY_API_KEY);
19
-
20
- Rebuy.setContextParameters(contextParameters);
21
-
22
- const request = {
23
- url: '/api/v1/products/recommended',
24
- parameters: {},
25
- };
26
-
27
- if (dataSource) {
28
- request.url = dataSource;
29
- }
30
-
31
- if (product) {
32
- request.parameters.shopify_product_ids = Utilities.getIdFromGraphUrl(product.id, 'Product');
33
- } else if (productId) {
34
- request.parameters.shopify_product_ids = Utilities.getIdFromGraphUrl(productId, 'Product');
35
- }
36
-
37
- if (variant) {
38
- request.parameters.shopify_variant_ids = Utilities.getIdFromGraphUrl(variant.id, 'ProductVariant');
39
- } else if (variantId) {
40
- request.parameters.shopify_variant_ids = Utilities.getIdFromGraphUrl(variantId, 'ProductVariant');
41
- }
42
-
43
- if (collection) {
44
- request.parameters.shopify_collection_ids = Utilities.getIdFromGraphUrl(collection.id, 'Collection');
45
- } else if (collectionId) {
46
- request.parameters.shopify_collection_ids = Utilities.getIdFromGraphUrl(collectionId, 'Collection');
47
- }
48
-
49
- if (limit) {
50
- request.parameters.limit = limit;
51
- }
39
+ if (options) {
40
+ client.setDefaultParameters(options);
41
+ }
52
42
 
53
- if (options) {
54
- Rebuy.setDefaultParameters(options);
55
- }
43
+ setRebuy(client);
44
+ setInitialized(true);
45
+ }
46
+ }, [Rebuy, contextParameters, options]);
56
47
 
48
+ // Update context params
57
49
  useEffect(() => {
58
- fetchData();
50
+ if (!Rebuy) return;
51
+
52
+ Rebuy.setContextParameters(contextParameters);
53
+ }, [Rebuy, contextParameters]);
54
+
55
+ // Memoize request object on prop changes (e.g. product)
56
+ const request = useMemo(() => {
57
+ const request = {
58
+ url: dataSource || '/api/v1/products/recommended',
59
+ parameters: {},
60
+ };
61
+
62
+ if (shopifyProductId) {
63
+ request.parameters.shopify_product_ids =
64
+ Utilities.getIdFromGraphUrl(shopifyProductId, 'Product');
65
+ }
66
+
67
+ if (shopifyVariantId) {
68
+ request.parameters.shopify_variant_ids =
69
+ Utilities.getIdFromGraphUrl(shopifyVariantId, 'ProductVariant');
70
+ }
71
+
72
+ if (shopifyCollectionId) {
73
+ request.parameters.shopify_collection_ids =
74
+ Utilities.getIdFromGraphUrl(shopifyCollectionId, 'Collection');
75
+ }
76
+
77
+ if (limit) {
78
+ request.parameters.limit = limit;
79
+ }
80
+
81
+ return request;
59
82
  }, [
60
- product,
61
- productId,
62
- variant,
63
- variantId,
64
- collection,
65
- collectionId,
66
83
  dataSource,
67
84
  limit,
68
- options,
85
+ shopifyCollectionId,
86
+ shopifyProductId,
87
+ shopifyVariantId,
69
88
  ]);
70
89
 
71
- const fetchData = async () => {
72
- const response = await Rebuy.getStorefrontData(request.url, request.parameters);
90
+ // Update product recommendations on cart or request change
91
+ useEffect(() => {
92
+ let ignore = false;
93
+
94
+ // Initializing...
95
+ if (!initialized || !Rebuy || !isCartReady) return;
96
+
97
+ const fetchData = async () => {
98
+ const { data, metadata } = await Rebuy.getStorefrontData(
99
+ request.url,
100
+ request.parameters
101
+ );
102
+
103
+ data.forEach((product) => {
104
+ product.variants = {
105
+ nodes: flattenConnection(product.variants),
106
+ };
107
+ });
108
+
109
+ // Set state only if mounted
110
+ if (!ignore) {
111
+ setProducts(data);
112
+ setMetadata(metadata);
113
+ }
114
+ };
115
+
116
+ fetchData();
73
117
 
74
- const { data, metadata } = response;
75
- data.forEach((product) => {
76
- product.variants = {
77
- nodes: flattenConnection(product.variants),
78
- };
79
- });
118
+ // Unmounted?
119
+ return () => {
120
+ ignore = true;
121
+ };
122
+ }, [Rebuy, initialized, request, isCartReady]);
80
123
 
81
- setProducts(data);
82
- setMetadata(metadata);
83
- };
124
+ const childrenWithProps = (props) =>
125
+ React.Children.map(children, (child) =>
126
+ React.isValidElement(child)
127
+ ? React.cloneElement(child, props)
128
+ : child
129
+ );
84
130
 
85
- const childrenWithProps = (data) => {
86
- return React.Children.map(children, (child) => {
87
- if (React.isValidElement(child)) {
88
- return React.cloneElement(child, data);
89
- }
90
- return child;
91
- });
92
- };
131
+ const childProps = { ...props, products, metadata };
93
132
 
94
- const data = Object.assign({}, props, { products, metadata });
133
+ return childrenWithProps(childProps);
134
+ };
95
135
 
96
- return <>{childrenWithProps(data)}</>;
97
- }
136
+ export default RebuyWidgetContainer;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy-hydrogen",
3
3
  "description": "This is the default library for Rebuy + Shopify Hydrogen",
4
- "version": "1.0.3",
4
+ "version": "2.0.0",
5
5
  "license": "MIT",
6
6
  "author": "Rebuy, Inc.",
7
7
  "type": "module",
@@ -10,10 +10,12 @@
10
10
  },
11
11
  "files": [
12
12
  "README.md",
13
+ "**/*.js",
13
14
  "**/*.jsx"
14
15
  ],
15
16
  "scripts": {
16
- "lint": "eslint . --ext .js,.jsx",
17
+ "format": "npm run lint -- --fix",
18
+ "lint": "eslint . --ext .js,.jsx,.json",
17
19
  "prepare": "husky install",
18
20
  "test": "echo \"Error: no test specified\" && exit 1"
19
21
  },
@@ -34,26 +36,39 @@
34
36
  },
35
37
  "homepage": "https://bitbucket.org/rebuyengine/npm-rebuy-hydrogen#readme",
36
38
  "devDependencies": {
37
- "@commitlint/cli": "^17.0.2",
38
- "@commitlint/config-conventional": "^17.0.2",
39
+ "@commitlint/cli": "^17.2.0",
40
+ "@commitlint/config-conventional": "^17.2.0",
39
41
  "@semantic-release/changelog": "^6.0.1",
40
42
  "@semantic-release/commit-analyzer": "^9.0.2",
41
43
  "@semantic-release/git": "^10.0.1",
42
44
  "@semantic-release/npm": "^9.0.1",
43
45
  "@semantic-release/release-notes-generator": "^10.0.3",
44
- "@shopify/hydrogen": "^1.0.1",
46
+ "@shopify/hydrogen": "^1.6.1",
45
47
  "conventional-changelog-eslint": "^3.0.9",
46
48
  "cz-conventional-changelog": "^3.3.0",
47
- "cz-customizable": "^6.3.0",
48
- "eslint": "^8.17.0",
49
- "eslint-plugin-react": "^7.30.0",
49
+ "cz-customizable": "^6.9.2",
50
+ "eslint": "^8.26.0",
51
+ "eslint-config-prettier": "^8.5.0",
52
+ "eslint-plugin-import": "^2.26.0",
53
+ "eslint-plugin-json": "^3.1.0",
54
+ "eslint-plugin-prettier": "^4.2.1",
55
+ "eslint-plugin-react": "^7.31.10",
56
+ "eslint-plugin-react-hooks": "^4.6.0",
50
57
  "husky": "^8.0.1",
51
- "semantic-release": "^19.0.3"
58
+ "lint-staged": "^13.0.3",
59
+ "prettier": "^2.7.1",
60
+ "semantic-release": "^19.0.5"
52
61
  },
53
62
  "dependencies": {
54
- "@rebuy/rebuy": "^1.0.1"
63
+ "@rebuy/rebuy": "^1.3.9"
55
64
  },
56
65
  "peerDependencies": {
57
- "@shopify/hydrogen": "^1.0.1"
66
+ "@shopify/hydrogen": "^1.6.1"
67
+ },
68
+ "lint-staged": {
69
+ "**/*.{js,jsx,json}": [
70
+ "npm run format",
71
+ "prettier --write"
72
+ ]
58
73
  }
59
74
  }