mirror of
https://github.com/vercel/commerce.git
synced 2025-03-14 22:42:33 +00:00
Adding shopify progress
This commit is contained in:
commit
ddd5497ac5
@ -2,4 +2,6 @@ BIGCOMMERCE_STOREFRONT_API_URL=
|
||||
BIGCOMMERCE_STOREFRONT_API_TOKEN=
|
||||
BIGCOMMERCE_STORE_API_URL=
|
||||
BIGCOMMERCE_STORE_API_TOKEN=
|
||||
BIGCOMMERCE_STORE_API_CLIENT_ID=
|
||||
BIGCOMMERCE_STORE_API_CLIENT_ID=
|
||||
SHOPIFY_STORE_DOMAIN=
|
||||
SHOPIFY_STOREFRONT_ACCESS_TOKEN=
|
@ -1,4 +0,0 @@
|
||||
## Changelog
|
||||
|
||||
- Select Variants Working
|
||||
- Click on cart item title, closes the sidebar
|
@ -4,11 +4,11 @@ import cn from 'classnames'
|
||||
import type { LineItem } from '@framework/types'
|
||||
import useCart from '@framework/cart/use-cart'
|
||||
import useCustomer from '@framework/customer/use-customer'
|
||||
import { Avatar } from '@components/common'
|
||||
import { Heart, Bag } from '@components/icons'
|
||||
import { useUI } from '@components/ui/context'
|
||||
import DropdownMenu from './DropdownMenu'
|
||||
import s from './UserNav.module.css'
|
||||
import { Avatar } from '@components/common'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
|
@ -4,9 +4,8 @@ import { NextSeo } from 'next-seo'
|
||||
import { FC, useState } from 'react'
|
||||
import s from './ProductView.module.css'
|
||||
|
||||
import { useUI } from '@components/ui'
|
||||
import { Swatch, ProductSlider } from '@components/product'
|
||||
import { Button, Container, Text } from '@components/ui'
|
||||
import { Button, Container, Text, useUI } from '@components/ui'
|
||||
|
||||
import type { Product } from '@commerce/types'
|
||||
import usePrice from '@framework/product/use-price'
|
||||
@ -100,7 +99,6 @@ const ProductView: FC<Props> = ({ product }) => {
|
||||
</ProductSlider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={s.sidebar}>
|
||||
<section>
|
||||
{product.options?.map((opt) => (
|
||||
|
@ -8,6 +8,7 @@ export interface State {
|
||||
displayToast: boolean
|
||||
modalView: string
|
||||
toastText: string
|
||||
userAvatar: string
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
@ -17,6 +18,7 @@ const initialState = {
|
||||
modalView: 'LOGIN_VIEW',
|
||||
displayToast: false,
|
||||
toastText: '',
|
||||
userAvatar: '',
|
||||
}
|
||||
|
||||
type Action =
|
||||
|
@ -1,11 +1,10 @@
|
||||
import React, { FC, useState } from 'react'
|
||||
import cn from 'classnames'
|
||||
import { Heart } from '@components/icons'
|
||||
|
||||
import { useUI } from '@components/ui'
|
||||
import type { Product, ProductVariant } from '@commerce/types'
|
||||
import useCustomer from '@framework/customer/use-customer'
|
||||
import useAddItem from '@framework/wishlist/use-add-item'
|
||||
import useCustomer from '@framework/customer/use-customer'
|
||||
import useRemoveItem from '@framework/wishlist/use-remove-item'
|
||||
import useWishlist from '@framework/wishlist/use-wishlist'
|
||||
|
||||
|
260
framework/shopify/README.md
Normal file
260
framework/shopify/README.md
Normal file
@ -0,0 +1,260 @@
|
||||
## Table of Contents
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
- [Modifications](#modifications)
|
||||
- [Adding item to Cart](#adding-item-to-cart)
|
||||
- [Proceed to Checkout](#proceed-to-checkout)
|
||||
- [General Usage](#general-usage)
|
||||
- [CommerceProvider](#commerceprovider)
|
||||
- [useCommerce](#usecommerce)
|
||||
- [Hooks](#hooks)
|
||||
- [usePrice](#useprice)
|
||||
- [useAddItem](#useadditem)
|
||||
- [useRemoveItem](#useremoveitem)
|
||||
- [useUpdateItem](#useupdateitem)
|
||||
- [APIs](#apis)
|
||||
- [getProduct](#getproduct)
|
||||
- [getAllProducts](#getallproducts)
|
||||
- [getAllCollections](#getallcollections)
|
||||
- [getAllPages](#getallpages)
|
||||
|
||||
# Shopify Storefront Data Hooks
|
||||
|
||||
Collection of hooks and data fetching functions to integrate Shopify in a React application. Designed to work with [Next.js Commerce](https://demo.vercel.store/).
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Install dependencies:
|
||||
|
||||
```
|
||||
yarn install shopify-buy
|
||||
yarn install -D @types/shopify-buy
|
||||
```
|
||||
|
||||
3. Environment variables need to be set:
|
||||
|
||||
```
|
||||
SHOPIFY_STORE_DOMAIN=
|
||||
SHOPIFY_STOREFRONT_ACCESS_TOKEN=
|
||||
NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=
|
||||
NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN=
|
||||
```
|
||||
|
||||
4. Point the framework to `shopify` by updating `tsconfig.json`:
|
||||
|
||||
```
|
||||
"@framework/*": ["framework/shopify/*"],
|
||||
"@framework": ["framework/shopify"]
|
||||
```
|
||||
|
||||
### Modifications
|
||||
|
||||
These modifications are temporarily until contributions are made to remove them.
|
||||
|
||||
#### Adding item to Cart
|
||||
|
||||
```js
|
||||
// components/product/ProductView/ProductView.tsx
|
||||
const ProductView: FC<Props> = ({ product }) => {
|
||||
const addToCart = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
await addItem({
|
||||
productId: product.id,
|
||||
variantId: variant ? variant.id : product.variants[0].id,
|
||||
})
|
||||
openSidebar()
|
||||
setLoading(false)
|
||||
} catch (err) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Proceed to Checkout
|
||||
|
||||
```js
|
||||
// components/cart/CartSidebarView/CartSidebarView.tsx
|
||||
import { useCommerce } from '@framework'
|
||||
|
||||
const CartSidebarView: FC = () => {
|
||||
const { checkout } = useCommerce()
|
||||
return (
|
||||
<Button href={checkout.webUrl} Component="a" width="100%">
|
||||
Proceed to Checkout
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## General Usage
|
||||
|
||||
### CommerceProvider
|
||||
|
||||
Provider component that creates the commerce context for children.
|
||||
|
||||
```js
|
||||
import { CommerceProvider } from '@framework'
|
||||
|
||||
const App = ({ children }) => {
|
||||
return <CommerceProvider locale={locale}>{children}</CommerceProvider>
|
||||
}
|
||||
|
||||
export default App
|
||||
```
|
||||
|
||||
### useCommerce
|
||||
|
||||
Returns the configs that are defined in the nearest `CommerceProvider`. Also provides access to Shopify's `checkout` and `shop`.
|
||||
|
||||
```js
|
||||
import { useCommerce } from 'nextjs-commerce-shopify'
|
||||
|
||||
const { checkout, shop } = useCommerce()
|
||||
```
|
||||
|
||||
- `checkout`: The information required to checkout items and pay ([Documentation](https://shopify.dev/docs/storefront-api/reference/checkouts/checkout)).
|
||||
- `shop`: Represents a collection of the general settings and information about the shop ([Documentation](https://shopify.dev/docs/storefront-api/reference/online-store/shop/index)).
|
||||
|
||||
## Hooks
|
||||
|
||||
### usePrice
|
||||
|
||||
Display the product variant price according to currency and locale.
|
||||
|
||||
```js
|
||||
import usePrice from '@framework/product/use-price'
|
||||
|
||||
const { price } = usePrice({
|
||||
amount,
|
||||
})
|
||||
```
|
||||
|
||||
Takes in either `amount` or `variant`:
|
||||
|
||||
- `amount`: A price value for a particular item if the amount is known.
|
||||
- `variant`: A shopify product variant. Price will be extracted from the variant.
|
||||
|
||||
### useAddItem
|
||||
|
||||
```js
|
||||
import { useAddItem } from '@framework/cart'
|
||||
|
||||
const AddToCartButton = ({ variantId, quantity }) => {
|
||||
const addItem = useAddItem()
|
||||
|
||||
const addToCart = async () => {
|
||||
await addItem({
|
||||
variantId,
|
||||
})
|
||||
}
|
||||
|
||||
return <button onClick={addToCart}>Add To Cart</button>
|
||||
}
|
||||
```
|
||||
|
||||
### useRemoveItem
|
||||
|
||||
```js
|
||||
import { useRemoveItem } from '@framework/cart'
|
||||
|
||||
const RemoveButton = ({ item }) => {
|
||||
const removeItem = useRemoveItem()
|
||||
|
||||
const handleRemove = async () => {
|
||||
await removeItem({ id: item.id })
|
||||
}
|
||||
|
||||
return <button onClick={handleRemove}>Remove</button>
|
||||
}
|
||||
```
|
||||
|
||||
### useUpdateItem
|
||||
|
||||
```js
|
||||
import { useUpdateItem } from '@framework/cart'
|
||||
|
||||
const CartItem = ({ item }) => {
|
||||
const [quantity, setQuantity] = useState(item.quantity)
|
||||
const updateItem = useUpdateItem(item)
|
||||
|
||||
const updateQuantity = async (e) => {
|
||||
const val = e.target.value
|
||||
await updateItem({ quantity: val })
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
type="number"
|
||||
max={99}
|
||||
min={0}
|
||||
value={quantity}
|
||||
onChange={updateQuantity}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## APIs
|
||||
|
||||
Collections of APIs to fetch data from a Shopify store.
|
||||
|
||||
The data is fetched using the [Shopify JavaScript Buy SDK](https://github.com/Shopify/js-buy-sdk#readme). Read the [Shopify Storefront API reference](https://shopify.dev/docs/storefront-api/reference) for more information.
|
||||
|
||||
### getProduct
|
||||
|
||||
Get a single product by its `handle`.
|
||||
|
||||
```js
|
||||
import getProduct from '@framework/product/get-product'
|
||||
import { getConfig } from '@framework/api'
|
||||
|
||||
const config = getConfig()
|
||||
|
||||
const product = await getProduct({
|
||||
variables: { slug },
|
||||
config,
|
||||
})
|
||||
```
|
||||
|
||||
### getAllProducts
|
||||
|
||||
```js
|
||||
import getAllProducts from '@framework/product/get-all-products'
|
||||
import { getConfig } from '@framework/api'
|
||||
|
||||
const config = getConfig()
|
||||
|
||||
const { products } = await getAllProducts({
|
||||
variables: { first: 12 },
|
||||
config,
|
||||
})
|
||||
```
|
||||
|
||||
### getAllCollections
|
||||
|
||||
```js
|
||||
import getAllCollections from '@framework/product/get-all-collections'
|
||||
import { getConfig } from '@framework/api'
|
||||
|
||||
const config = getConfig()
|
||||
|
||||
const collections = await getAllCollections({
|
||||
config,
|
||||
})
|
||||
```
|
||||
|
||||
### getAllPages
|
||||
|
||||
```js
|
||||
import getAllPages from '@framework/common/get-all-pages'
|
||||
import { getConfig } from '@framework/api'
|
||||
|
||||
const config = getConfig()
|
||||
|
||||
const pages = await getAllPages({
|
||||
variables: { first: 12 },
|
||||
config,
|
||||
})
|
||||
```
|
1
framework/shopify/api/cart/index.ts
Normal file
1
framework/shopify/api/cart/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/shopify/api/catalog/index.ts
Normal file
1
framework/shopify/api/catalog/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/shopify/api/catalog/products.ts
Normal file
1
framework/shopify/api/catalog/products.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/shopify/api/checkout/index.ts
Normal file
1
framework/shopify/api/checkout/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/shopify/api/customers/index.ts
Normal file
1
framework/shopify/api/customers/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/shopify/api/customers/login.ts
Normal file
1
framework/shopify/api/customers/login.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/shopify/api/customers/logout.ts
Normal file
1
framework/shopify/api/customers/logout.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/shopify/api/customers/signup.ts
Normal file
1
framework/shopify/api/customers/signup.ts
Normal file
@ -0,0 +1 @@
|
||||
export default function () {}
|
60
framework/shopify/api/index.ts
Normal file
60
framework/shopify/api/index.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import type { CommerceAPIConfig } from '@commerce/api'
|
||||
import fetchGraphqlApi from './utils/fetch-graphql-api'
|
||||
|
||||
export interface ShopifyConfig extends CommerceAPIConfig {}
|
||||
|
||||
// No I don't like this - will fix it later
|
||||
const API_URL =
|
||||
process.env.SHOPIFY_STORE_DOMAIN ||
|
||||
process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
|
||||
const API_TOKEN =
|
||||
process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN ||
|
||||
process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN
|
||||
|
||||
if (!API_URL) {
|
||||
throw new Error(
|
||||
`The environment variable SHOPIFY_STORE_DOMAIN is missing and it's required to access your store`
|
||||
)
|
||||
}
|
||||
|
||||
if (!API_TOKEN) {
|
||||
throw new Error(
|
||||
`The environment variable SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store`
|
||||
)
|
||||
}
|
||||
|
||||
export class Config {
|
||||
private config: ShopifyConfig
|
||||
|
||||
constructor(config: ShopifyConfig) {
|
||||
this.config = config
|
||||
}
|
||||
|
||||
getConfig(userConfig: Partial<ShopifyConfig> = {}) {
|
||||
return Object.entries(userConfig).reduce<ShopifyConfig>(
|
||||
(cfg, [key, value]) => Object.assign(cfg, { [key]: value }),
|
||||
{ ...this.config }
|
||||
)
|
||||
}
|
||||
|
||||
setConfig(newConfig: Partial<ShopifyConfig>) {
|
||||
Object.assign(this.config, newConfig)
|
||||
}
|
||||
}
|
||||
|
||||
const config = new Config({
|
||||
commerceUrl: API_URL,
|
||||
apiToken: API_TOKEN,
|
||||
// TODO
|
||||
// @ts-ignore
|
||||
fetch: fetchGraphqlApi,
|
||||
customerCookie: 'SHOP_TOKEN',
|
||||
})
|
||||
|
||||
export function getConfig(userConfig?: Partial<ShopifyConfig>) {
|
||||
return config.getConfig(userConfig)
|
||||
}
|
||||
|
||||
export function setConfig(newConfig: Partial<ShopifyConfig>) {
|
||||
return config.setConfig(newConfig)
|
||||
}
|
21
framework/shopify/api/operations/get-all-collections.ts
Normal file
21
framework/shopify/api/operations/get-all-collections.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import Client from 'shopify-buy'
|
||||
import { ShopifyConfig } from '../index'
|
||||
|
||||
type Options = {
|
||||
config: ShopifyConfig
|
||||
}
|
||||
|
||||
const getAllCollections = async (options: Options) => {
|
||||
const { config } = options
|
||||
|
||||
const client = Client.buildClient({
|
||||
storefrontAccessToken: config.apiToken,
|
||||
domain: config.commerceUrl,
|
||||
})
|
||||
|
||||
const res = await client.collection.fetchAllWithProducts()
|
||||
|
||||
return JSON.parse(JSON.stringify(res))
|
||||
}
|
||||
|
||||
export default getAllCollections
|
27
framework/shopify/api/operations/get-page.ts
Normal file
27
framework/shopify/api/operations/get-page.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { ShopifyConfig, getConfig } from '..'
|
||||
import type { Page } from '../../types'
|
||||
|
||||
export type { Page }
|
||||
|
||||
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
|
||||
|
||||
export type PageVariables = {
|
||||
id: string
|
||||
}
|
||||
|
||||
async function getPage({
|
||||
url,
|
||||
variables,
|
||||
config,
|
||||
preview,
|
||||
}: {
|
||||
url?: string
|
||||
variables: PageVariables
|
||||
config?: ShopifyConfig
|
||||
preview?: boolean
|
||||
}): Promise<GetPageResult> {
|
||||
config = getConfig(config)
|
||||
return {}
|
||||
}
|
||||
|
||||
export default getPage
|
51
framework/shopify/api/utils/fetch-graphql-api.ts
Normal file
51
framework/shopify/api/utils/fetch-graphql-api.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { CommerceAPIFetchOptions } from '@commerce/api'
|
||||
import { FetcherError } from '@commerce/utils/errors'
|
||||
import { getConfig } from '../index'
|
||||
|
||||
export interface GraphQLFetcherResult<Data = any> {
|
||||
data: Data
|
||||
res: Response
|
||||
}
|
||||
export type GraphQLFetcher<
|
||||
Data extends GraphQLFetcherResult = GraphQLFetcherResult,
|
||||
Variables = any
|
||||
> = (
|
||||
query: string,
|
||||
queryData?: CommerceAPIFetchOptions<Variables>,
|
||||
fetchOptions?: RequestInit
|
||||
) => Promise<Data>
|
||||
|
||||
const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
query: string,
|
||||
{ variables } = {},
|
||||
fetchOptions
|
||||
) => {
|
||||
const config = getConfig()
|
||||
const url = `https://${config.commerceUrl}/api/2020-10/graphql.json`
|
||||
|
||||
const res = await fetch(url, {
|
||||
...fetchOptions,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Shopify-Storefront-Access-Token': config.apiToken,
|
||||
...fetchOptions?.headers,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
variables,
|
||||
}),
|
||||
})
|
||||
|
||||
const json = await res.json()
|
||||
if (json.errors) {
|
||||
throw new FetcherError({
|
||||
errors: json.errors ?? [{ message: 'Failed to fetch Shopify API' }],
|
||||
status: res.status,
|
||||
})
|
||||
}
|
||||
|
||||
return { data: json.data, res }
|
||||
}
|
||||
|
||||
export default fetchGraphqlApi
|
2
framework/shopify/api/wishlist/index.tsx
Normal file
2
framework/shopify/api/wishlist/index.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
export type WishlistItem = { product: any; id: number }
|
||||
export default function () {}
|
13
framework/shopify/auth/use-login.tsx
Normal file
13
framework/shopify/auth/use-login.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export function emptyHook() {
|
||||
const useEmptyHook = async (options = {}) => {
|
||||
return useCallback(async function () {
|
||||
return Promise.resolve()
|
||||
}, [])
|
||||
}
|
||||
|
||||
return useEmptyHook
|
||||
}
|
||||
|
||||
export default emptyHook
|
13
framework/shopify/auth/use-logout.tsx
Normal file
13
framework/shopify/auth/use-logout.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export function emptyHook() {
|
||||
const useEmptyHook = async (options = {}) => {
|
||||
return useCallback(async function () {
|
||||
return Promise.resolve()
|
||||
}, [])
|
||||
}
|
||||
|
||||
return useEmptyHook
|
||||
}
|
||||
|
||||
export default emptyHook
|
13
framework/shopify/auth/use-signup.tsx
Normal file
13
framework/shopify/auth/use-signup.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export function emptyHook() {
|
||||
const useEmptyHook = async (options = {}) => {
|
||||
return useCallback(async function () {
|
||||
return Promise.resolve()
|
||||
}, [])
|
||||
}
|
||||
|
||||
return useEmptyHook
|
||||
}
|
||||
|
||||
export default emptyHook
|
5
framework/shopify/cart/index.ts
Normal file
5
framework/shopify/cart/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export { default as useCart } from './use-cart'
|
||||
export { default as useAddItem } from './use-add-item'
|
||||
export { default as useRemoveItem } from './use-remove-item'
|
||||
// export { default as useWishlistActions } from './use-cart-actions'
|
||||
// export { default as useUpdateItem } from './use-cart-actions'
|
30
framework/shopify/cart/use-add-item.tsx
Normal file
30
framework/shopify/cart/use-add-item.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { useCallback } from 'react'
|
||||
import { LineItemToAdd } from 'shopify-buy'
|
||||
import { useCommerce } from '../index'
|
||||
|
||||
type Options = {
|
||||
productId: number
|
||||
variantId: string | number
|
||||
}
|
||||
|
||||
const useAddItem = () => {
|
||||
const { checkout, client, updateCheckout } = useCommerce()
|
||||
|
||||
return useCallback(
|
||||
async function addItem(options: Options) {
|
||||
const lineItems: LineItemToAdd[] = [
|
||||
{
|
||||
variantId: `${options.variantId}`,
|
||||
quantity: 1,
|
||||
},
|
||||
]
|
||||
|
||||
const cart = await client?.checkout.addLineItems(checkout.id, lineItems)
|
||||
updateCheckout(cart)
|
||||
return cart
|
||||
},
|
||||
[checkout, client]
|
||||
)
|
||||
}
|
||||
|
||||
export default useAddItem
|
42
framework/shopify/cart/use-cart.tsx
Normal file
42
framework/shopify/cart/use-cart.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { useCommerce } from '../index'
|
||||
|
||||
export function emptyHook() {
|
||||
const { checkout } = useCommerce()
|
||||
const { lineItems, totalPriceV2 } = checkout || {}
|
||||
|
||||
return {
|
||||
data: {
|
||||
subTotal: totalPriceV2?.amount || 0,
|
||||
total: totalPriceV2?.amount || 0,
|
||||
currency: {
|
||||
code: '',
|
||||
},
|
||||
line_items:
|
||||
lineItems?.map((item) => {
|
||||
return [
|
||||
{
|
||||
id: item.id,
|
||||
name: item.title,
|
||||
quantity: item.quantity,
|
||||
},
|
||||
]
|
||||
}) || [],
|
||||
items:
|
||||
lineItems?.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.title,
|
||||
images: [{ url: '/jacket.png' }],
|
||||
url: '/',
|
||||
quantity: item.quantity,
|
||||
productId: item.id,
|
||||
variantId: item.id,
|
||||
}
|
||||
}) || [],
|
||||
},
|
||||
isEmpty: false,
|
||||
isLoading: false,
|
||||
}
|
||||
}
|
||||
|
||||
export default emptyHook
|
17
framework/shopify/cart/use-remove-item.tsx
Normal file
17
framework/shopify/cart/use-remove-item.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useCommerce } from '../index'
|
||||
|
||||
const useRemoveItem = () => {
|
||||
const { checkout, client, updateCheckout } = useCommerce()
|
||||
|
||||
return useCallback(
|
||||
async function removeItem({ id }: { id: string }) {
|
||||
const cart = await client?.checkout.removeLineItems(checkout.id, [id])
|
||||
updateCheckout(cart)
|
||||
return cart
|
||||
},
|
||||
[checkout, client]
|
||||
)
|
||||
}
|
||||
|
||||
export default useRemoveItem
|
24
framework/shopify/cart/use-update-item.tsx
Normal file
24
framework/shopify/cart/use-update-item.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useCommerce } from '../index'
|
||||
|
||||
const useUpdateItem = (item: CartItem) => {
|
||||
const { checkout, client, updateCheckout } = useCommerce()
|
||||
|
||||
return useCallback(
|
||||
async function updateItem({ quantity }: { quantity: number }) {
|
||||
const lineItemsToUpdate = [{ id: item.id, quantity }]
|
||||
|
||||
const cart = await client?.checkout.updateLineItems(
|
||||
checkout.id,
|
||||
lineItemsToUpdate
|
||||
)
|
||||
|
||||
updateCheckout(cart)
|
||||
|
||||
return cart
|
||||
},
|
||||
[checkout, client]
|
||||
)
|
||||
}
|
||||
|
||||
export default useUpdateItem
|
55
framework/shopify/common/get-all-pages.ts
Normal file
55
framework/shopify/common/get-all-pages.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { getConfig, ShopifyConfig } from '../api'
|
||||
import { Page as PageType, PageEdge } from '../types'
|
||||
|
||||
export type Page = PageType
|
||||
|
||||
export const getAllPagesQuery = /* GraphQL */ `
|
||||
query($first: Int!) {
|
||||
pages(first: $first) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
handle
|
||||
body
|
||||
bodySummary
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
type Variables = {
|
||||
first?: number
|
||||
}
|
||||
|
||||
type Options = {
|
||||
variables?: Variables
|
||||
config: ShopifyConfig
|
||||
preview?: boolean
|
||||
}
|
||||
|
||||
type ReturnType = {
|
||||
pages: Page[]
|
||||
}
|
||||
|
||||
const getAllPages = async (options?: Options): Promise<ReturnType> => {
|
||||
let { config, variables = { first: 250 } } = options || {}
|
||||
|
||||
config = getConfig(config)
|
||||
|
||||
const { data } = await config.fetch(getAllPagesQuery, { variables })
|
||||
|
||||
const pages = data.pages.edges.map(({ node }: PageEdge) => {
|
||||
return {
|
||||
...node,
|
||||
name: node.handle,
|
||||
url: `${config!.locale}/${node.handle}`,
|
||||
}
|
||||
})
|
||||
|
||||
return { pages }
|
||||
}
|
||||
|
||||
export default getAllPages
|
30
framework/shopify/common/get-site-info.ts
Normal file
30
framework/shopify/common/get-site-info.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { ShopifyConfig } from '../index'
|
||||
|
||||
type Options = {
|
||||
config: ShopifyConfig
|
||||
preview?: boolean
|
||||
}
|
||||
|
||||
const getSiteInfo = async (options: Options) => {
|
||||
// TODO
|
||||
return {
|
||||
categories: [
|
||||
{
|
||||
path: '',
|
||||
name: '',
|
||||
entityId: 0,
|
||||
},
|
||||
],
|
||||
brands: [
|
||||
{
|
||||
node: {
|
||||
path: '',
|
||||
name: '',
|
||||
entityId: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
export default getSiteInfo
|
32
framework/shopify/customer/use-customer.tsx
Normal file
32
framework/shopify/customer/use-customer.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type { SwrOptions } from '@commerce/utils/use-data'
|
||||
import useCommerceCustomer from '@commerce/use-customer'
|
||||
|
||||
const defaultOpts = {}
|
||||
|
||||
export type Customer = {
|
||||
entityId: number
|
||||
firstName: string
|
||||
lastName: string
|
||||
email: string
|
||||
}
|
||||
export type CustomerData = {}
|
||||
|
||||
export const fetcher: HookFetcher<Customer | null> = async () => {
|
||||
return null
|
||||
}
|
||||
|
||||
export function extendHook(
|
||||
customFetcher: typeof fetcher,
|
||||
swrOptions?: SwrOptions<Customer | null>
|
||||
) {
|
||||
const useCustomer = () => {
|
||||
return { data: { firstName: null, lastName: null, email: null } }
|
||||
}
|
||||
|
||||
useCustomer.extend = extendHook
|
||||
|
||||
return useCustomer
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
109
framework/shopify/index.tsx
Normal file
109
framework/shopify/index.tsx
Normal file
@ -0,0 +1,109 @@
|
||||
import React, {
|
||||
ReactNode,
|
||||
createContext,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
useEffect,
|
||||
} from 'react'
|
||||
import Client from 'shopify-buy'
|
||||
import { Shop, Cart, Client as ClientType } from './types'
|
||||
import {
|
||||
getCheckoutIdFromStorage,
|
||||
setCheckoutIdInStorage,
|
||||
} from './utils/storage'
|
||||
import { getConfig } from '@framework/api'
|
||||
|
||||
const Commerce = createContext<CommerceContextValue | {}>({})
|
||||
|
||||
type CommerceProps = {
|
||||
children?: ReactNode
|
||||
locale: string
|
||||
}
|
||||
|
||||
type CommerceContextValue = {
|
||||
client: ClientType
|
||||
shop: Shop
|
||||
checkout: Cart
|
||||
updateCheckout: (cart: Cart | undefined) => void
|
||||
currencyCode: string
|
||||
locale: string
|
||||
sessionToken: string
|
||||
}
|
||||
|
||||
export function CommerceProvider({
|
||||
children,
|
||||
locale = 'en-US',
|
||||
}: CommerceProps) {
|
||||
const sessionToken = 'nextjs-commerce-shopify-token'
|
||||
|
||||
const config = getConfig()
|
||||
|
||||
const client = Client.buildClient({
|
||||
storefrontAccessToken: config.apiToken,
|
||||
domain: config.commerceUrl,
|
||||
language: locale,
|
||||
}) as ClientType
|
||||
|
||||
const [shop, setShop] = useState<Shop>()
|
||||
const [checkout, setCheckout] = useState<Cart>()
|
||||
|
||||
const fetchShopify = async () => {
|
||||
const shopInfo: Shop = await client.shop.fetchInfo()
|
||||
let checkoutResource: Cart
|
||||
|
||||
const checkoutOptions = {
|
||||
presentmentCurrencyCode:
|
||||
/*config.currencyCode ||*/ shopInfo?.currencyCode,
|
||||
}
|
||||
|
||||
let checkoutId = getCheckoutIdFromStorage(sessionToken)
|
||||
|
||||
// we could have a cart id stored in session storage
|
||||
// user could be refreshing or navigating back and forth
|
||||
if (checkoutId) {
|
||||
checkoutResource = await client.checkout.fetch(checkoutId)
|
||||
|
||||
// could be expired order - we will create a new order
|
||||
if (checkoutResource.completedAt) {
|
||||
checkoutResource = await client.checkout.create(checkoutOptions)
|
||||
}
|
||||
} else {
|
||||
checkoutResource = await client.checkout.create(checkoutOptions)
|
||||
}
|
||||
|
||||
setCheckoutIdInStorage(sessionToken, checkoutResource.id)
|
||||
|
||||
setShop(shopInfo)
|
||||
setCheckout(checkoutResource)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchShopify()
|
||||
}, [])
|
||||
|
||||
const updateCheckout = (newCheckout: Cart) => {
|
||||
setCheckout(newCheckout)
|
||||
}
|
||||
|
||||
// Because the config is an object, if the parent re-renders this provider
|
||||
// will re-render every consumer unless we memoize the config
|
||||
const cfg = useMemo(
|
||||
() => ({
|
||||
client,
|
||||
checkout,
|
||||
shop,
|
||||
updateCheckout: updateCheckout,
|
||||
currencyCode: /*config.currencyCode ||*/ checkout?.currencyCode,
|
||||
locale,
|
||||
sessionToken,
|
||||
}),
|
||||
[client]
|
||||
)
|
||||
|
||||
return <Commerce.Provider value={cfg}>{children}</Commerce.Provider>
|
||||
}
|
||||
|
||||
export function useCommerce<T extends CommerceContextValue>() {
|
||||
return useContext(Commerce) as T
|
||||
}
|
31
framework/shopify/product/get-all-product-paths.ts
Normal file
31
framework/shopify/product/get-all-product-paths.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import Client from 'shopify-buy'
|
||||
import { getConfig } from '../api'
|
||||
import { Product } from '../types'
|
||||
import toCommerceProducts from '../utils/to-commerce-products'
|
||||
|
||||
type ReturnType = {
|
||||
products: any[]
|
||||
}
|
||||
|
||||
const getAllProductPaths = async (): Promise<ReturnType> => {
|
||||
const config = getConfig()
|
||||
|
||||
const client = Client.buildClient({
|
||||
storefrontAccessToken: config.apiToken,
|
||||
domain: config.commerceUrl,
|
||||
})
|
||||
|
||||
const res = (await client.product.fetchAll()) as Product[]
|
||||
|
||||
const products = toCommerceProducts(res)
|
||||
|
||||
return {
|
||||
products: products.map((product) => {
|
||||
return {
|
||||
node: { ...product },
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
export default getAllProductPaths
|
40
framework/shopify/product/get-all-products.ts
Normal file
40
framework/shopify/product/get-all-products.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import Client from 'shopify-buy'
|
||||
import { ShopifyConfig } from '../api'
|
||||
import { Product } from '../types'
|
||||
import toCommerceProducts from '../utils/to-commerce-products'
|
||||
|
||||
export type ProductNode = Product
|
||||
|
||||
type Variables = {
|
||||
first?: number
|
||||
field?: string
|
||||
}
|
||||
|
||||
type Options = {
|
||||
variables: Variables
|
||||
config: ShopifyConfig
|
||||
preview?: boolean
|
||||
}
|
||||
|
||||
type ReturnType = {
|
||||
products: any[]
|
||||
}
|
||||
|
||||
const getAllProducts = async (options: Options): Promise<ReturnType> => {
|
||||
const { config } = options
|
||||
|
||||
const client = Client.buildClient({
|
||||
storefrontAccessToken: config.apiToken,
|
||||
domain: config.commerceUrl,
|
||||
})
|
||||
|
||||
const res = (await client.product.fetchAll()) as Product[]
|
||||
|
||||
const products = toCommerceProducts(res)
|
||||
|
||||
return {
|
||||
products,
|
||||
}
|
||||
}
|
||||
|
||||
export default getAllProducts
|
37
framework/shopify/product/get-product.ts
Normal file
37
framework/shopify/product/get-product.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import Client from 'shopify-buy'
|
||||
import { ShopifyConfig } from '../api'
|
||||
import { Product } from '../types'
|
||||
import toCommerceProducts from '../utils/to-commerce-products'
|
||||
|
||||
export type ProductNode = Product
|
||||
|
||||
type Variables = {
|
||||
slug: string
|
||||
}
|
||||
|
||||
type Options = {
|
||||
variables: Variables
|
||||
config: ShopifyConfig
|
||||
preview?: boolean
|
||||
}
|
||||
|
||||
type ReturnType = {
|
||||
product: any
|
||||
}
|
||||
|
||||
const getProduct = async (options: Options): Promise<ReturnType> => {
|
||||
const { variables, config } = options
|
||||
|
||||
const client = Client.buildClient({
|
||||
storefrontAccessToken: config.apiToken,
|
||||
domain: config.commerceUrl,
|
||||
})
|
||||
|
||||
const res = (await client.product.fetchByHandle(variables.slug)) as Product
|
||||
|
||||
return {
|
||||
product: toCommerceProducts([res])[0],
|
||||
}
|
||||
}
|
||||
|
||||
export default getProduct
|
2
framework/shopify/product/use-price.tsx
Normal file
2
framework/shopify/product/use-price.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
export * from '@commerce/use-price'
|
||||
export { default } from '@commerce/use-price'
|
41
framework/shopify/product/use-search.tsx
Normal file
41
framework/shopify/product/use-search.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type { SwrOptions } from '@commerce/utils/use-data'
|
||||
import useCommerceSearch from '@commerce/products/use-search'
|
||||
import { ProductEdge } from '../types'
|
||||
|
||||
const defaultOpts = {}
|
||||
|
||||
export type SearchProductsInput = {
|
||||
search?: string
|
||||
categoryId?: number
|
||||
brandId?: number
|
||||
sort?: string
|
||||
}
|
||||
|
||||
export type SearchProductsData = {
|
||||
products: ProductEdge[]
|
||||
found: boolean
|
||||
}
|
||||
|
||||
export const fetcher: HookFetcher<SearchProductsData, SearchProductsInput> = (
|
||||
options,
|
||||
{ search, categoryId, brandId, sort },
|
||||
fetch
|
||||
) => {
|
||||
return { found: false, products: [] }
|
||||
}
|
||||
|
||||
export function extendHook(
|
||||
customFetcher: typeof fetcher,
|
||||
swrOptions?: SwrOptions<SearchProductsData, SearchProductsInput>
|
||||
) {
|
||||
const useSearch = (input: SearchProductsInput = {}) => {
|
||||
return {}
|
||||
}
|
||||
|
||||
useSearch.extend = extendHook
|
||||
|
||||
return useSearch
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
130
framework/shopify/types.ts
Normal file
130
framework/shopify/types.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import {
|
||||
Product as BaseProduct,
|
||||
ProductVariant as BaseProductVariant,
|
||||
Cart as BaseCart,
|
||||
CheckoutResource as BaseCheckoutResource,
|
||||
AttributeInput,
|
||||
Client as BaseClient,
|
||||
Shop as BaseShop,
|
||||
Image as BaseImage,
|
||||
} from 'shopify-buy'
|
||||
|
||||
export type SelectedOptions = {
|
||||
id: string
|
||||
name: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export type PresentmentPrice = {
|
||||
price: PriceV2
|
||||
}
|
||||
|
||||
export type ProductVariant = BaseProductVariant & {
|
||||
selectedOptions: Array<SelectedOptions>
|
||||
presentmentPrices: Array<PresentmentPrice>
|
||||
}
|
||||
|
||||
// TODO
|
||||
export type ProductOptions = {
|
||||
node: {
|
||||
__typename: string
|
||||
displayName: string
|
||||
values: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
label: string
|
||||
id: string
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
export type ProductEdge = {
|
||||
node: Product
|
||||
}
|
||||
|
||||
export type Product = BaseProduct & {
|
||||
handle: string
|
||||
name: string
|
||||
path: string
|
||||
entityId: number
|
||||
descriptionHtml: string
|
||||
prices: {
|
||||
price: {
|
||||
value: number
|
||||
currencyCode: string
|
||||
}
|
||||
retailPrice: {
|
||||
value: number
|
||||
currencyCode: string
|
||||
}
|
||||
}
|
||||
images: {
|
||||
edges: [{ node: { urlOriginal: string; altText: string } }]
|
||||
}
|
||||
productOptions: ProductOptions
|
||||
variants: Array<ProductVariant> & {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
productOptions: ProductOptions[]
|
||||
entityId: number
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export type PriceV2 = {
|
||||
amount: number
|
||||
currencyCode: string
|
||||
}
|
||||
|
||||
export type Cart = BaseCart & {
|
||||
webUrl?: string
|
||||
currencyCode?: string
|
||||
lineItemsSubtotalPrice?: PriceV2
|
||||
totalPriceV2?: PriceV2
|
||||
}
|
||||
|
||||
export type Shop = BaseShop & {
|
||||
currencyCode?: string
|
||||
}
|
||||
|
||||
export type Create = {
|
||||
presentmentCurrencyCode?: string
|
||||
}
|
||||
|
||||
export type CheckoutResource = BaseCheckoutResource & {
|
||||
updateLineItems(
|
||||
checkoutId: string | number,
|
||||
lineItems: AttributeInput[]
|
||||
): Promise<Cart>
|
||||
|
||||
create: (input: Create) => Promise<Cart>
|
||||
}
|
||||
|
||||
export type Client = BaseClient & {
|
||||
checkout: CheckoutResource
|
||||
}
|
||||
|
||||
export type Page = {
|
||||
id: string
|
||||
title: string
|
||||
name: string
|
||||
handle: string
|
||||
body: string
|
||||
bodySummary: string
|
||||
url: string
|
||||
sort_order: number
|
||||
}
|
||||
|
||||
export type PageEdge = {
|
||||
node: Page
|
||||
}
|
||||
|
||||
export type Image = BaseImage
|
13
framework/shopify/utils/storage.ts
Normal file
13
framework/shopify/utils/storage.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export const getCheckoutIdFromStorage = (token: string) => {
|
||||
if (window && window.sessionStorage) {
|
||||
return window.sessionStorage.getItem(token)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const setCheckoutIdInStorage = (token: string, id: string | number) => {
|
||||
if (window && window.sessionStorage) {
|
||||
return window.sessionStorage.setItem(token, id + '')
|
||||
}
|
||||
}
|
60
framework/shopify/utils/to-commerce-products.ts
Normal file
60
framework/shopify/utils/to-commerce-products.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { Product, Image } from '../types'
|
||||
|
||||
export default function toCommerceProducts(products: Product[]) {
|
||||
return products.map((product: Product) => {
|
||||
return {
|
||||
id: product.id,
|
||||
entityId: product.id,
|
||||
name: product.title,
|
||||
slug: product.handle,
|
||||
title: product.title,
|
||||
vendor: product.vendor,
|
||||
description: product.descriptionHtml,
|
||||
path: `/${product.handle}`,
|
||||
price: {
|
||||
value: +product.variants[0].price,
|
||||
currencyCode: 'USD', // TODO
|
||||
},
|
||||
images: product.images.map((image: Image) => {
|
||||
return {
|
||||
url: image.src,
|
||||
}
|
||||
}),
|
||||
variants: product.variants.map((variant) => {
|
||||
return {
|
||||
id: variant.id,
|
||||
options: variant.selectedOptions.map((selectedOption) => {
|
||||
return {
|
||||
__typename: 'MultipleChoiceOption',
|
||||
displayName: selectedOption.name,
|
||||
values: [
|
||||
{
|
||||
node: {
|
||||
id: variant.id,
|
||||
label: selectedOption.value,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}),
|
||||
}
|
||||
}),
|
||||
productOptions: product.options.map((option) => {
|
||||
return {
|
||||
__typename: 'MultipleChoiceOption',
|
||||
displayName: option.name,
|
||||
values: option.values.map((value) => {
|
||||
return {
|
||||
node: {
|
||||
entityId: 1,
|
||||
label: value.value,
|
||||
hexColors: [value.value],
|
||||
},
|
||||
}
|
||||
}),
|
||||
}
|
||||
}),
|
||||
options: [],
|
||||
}
|
||||
})
|
||||
}
|
13
framework/shopify/wishlist/use-add-item.tsx
Normal file
13
framework/shopify/wishlist/use-add-item.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export function emptyHook() {
|
||||
const useEmptyHook = async (options = {}) => {
|
||||
return useCallback(async function () {
|
||||
return Promise.resolve()
|
||||
}, [])
|
||||
}
|
||||
|
||||
return useEmptyHook
|
||||
}
|
||||
|
||||
export default emptyHook
|
17
framework/shopify/wishlist/use-remove-item.tsx
Normal file
17
framework/shopify/wishlist/use-remove-item.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
type Options = {
|
||||
includeProducts?: boolean
|
||||
}
|
||||
|
||||
export function emptyHook(options?: Options) {
|
||||
const useEmptyHook = async ({ id }: { id: string | number }) => {
|
||||
return useCallback(async function () {
|
||||
return Promise.resolve()
|
||||
}, [])
|
||||
}
|
||||
|
||||
return useEmptyHook
|
||||
}
|
||||
|
||||
export default emptyHook
|
45
framework/shopify/wishlist/use-wishlist.tsx
Normal file
45
framework/shopify/wishlist/use-wishlist.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
import { HookFetcher } from '@commerce/utils/types'
|
||||
import { SwrOptions } from '@commerce/utils/use-data'
|
||||
import useCommerceWishlist from '@commerce/wishlist/use-wishlist'
|
||||
import { Product } from '../types'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
|
||||
const defaultOpts = {}
|
||||
|
||||
export type Wishlist = {
|
||||
items: [
|
||||
{
|
||||
product_id: number
|
||||
variant_id: number
|
||||
id: number
|
||||
product: Product
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export interface UseWishlistOptions {
|
||||
includeProducts?: boolean
|
||||
}
|
||||
|
||||
export interface UseWishlistInput extends UseWishlistOptions {
|
||||
customerId?: number
|
||||
}
|
||||
|
||||
export const fetcher: HookFetcher<Wishlist | null, UseWishlistInput> = () => {
|
||||
return null
|
||||
}
|
||||
|
||||
export function extendHook(
|
||||
customFetcher: typeof fetcher,
|
||||
swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput>
|
||||
) {
|
||||
const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => {
|
||||
return { data: null }
|
||||
}
|
||||
|
||||
useWishlist.extend = extendHook
|
||||
|
||||
return useWishlist
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
@ -16,14 +16,14 @@
|
||||
"jsx": "preserve",
|
||||
"paths": {
|
||||
"@lib/*": ["lib/*"],
|
||||
"@assets/*": ["assets/*"],
|
||||
"@config/*": ["config/*"],
|
||||
"@components/*": ["components/*"],
|
||||
"@utils/*": ["utils/*"],
|
||||
"@commerce/*": ["framework/commerce/*"],
|
||||
"@config/*": ["config/*"],
|
||||
"@assets/*": ["assets/*"],
|
||||
"@components/*": ["components/*"],
|
||||
"@commerce": ["framework/commerce"],
|
||||
"@framework/*": ["framework/bigcommerce/*"],
|
||||
"@framework": ["framework/bigcommerce"]
|
||||
"@commerce/*": ["framework/commerce/*"],
|
||||
"@framework": ["framework/bigcommerce"],
|
||||
"@framework/*": ["framework/bigcommerce/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],
|
||||
|
Loading…
x
Reference in New Issue
Block a user