mirror of
https://github.com/vercel/commerce.git
synced 2025-07-03 19:51:22 +00:00
Add Spree as allowed Framework
This commit is contained in:
parent
a3ef27f5e7
commit
c90fa7abf2
@ -14,6 +14,7 @@ const PROVIDERS = [
|
|||||||
'swell',
|
'swell',
|
||||||
'vendure',
|
'vendure',
|
||||||
'local',
|
'local',
|
||||||
|
'spree',
|
||||||
]
|
]
|
||||||
|
|
||||||
function getProviderName() {
|
function getProviderName() {
|
||||||
|
@ -2,7 +2,12 @@
|
|||||||
|
|
||||||
COMMERCE_PROVIDER=spree
|
COMMERCE_PROVIDER=spree
|
||||||
|
|
||||||
SPREE_API_HOST = 'http://localhost:3000'
|
{# public (available in the web browser) #}
|
||||||
|
NEXT_PUBLIC_SPREE_API_HOST=http://localhost:3000
|
||||||
|
|
||||||
# TODO:
|
{# private #}
|
||||||
# COMMERCE_IMAGE_HOST
|
NEXT_PUBLIC_SPREE_DEFAULT_LOCALE=en-us
|
||||||
|
NEXT_PUBLIC_SPREE_CART_COOKIE_NAME=spree_cart
|
||||||
|
|
||||||
|
{# # TODO: #}
|
||||||
|
{# # COMMERCE_IMAGE_HOST #}
|
||||||
|
1
framework/spree/api/endpoints/cart/index.ts
Normal file
1
framework/spree/api/endpoints/cart/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
1
framework/spree/api/endpoints/catalog/index.ts
Normal file
1
framework/spree/api/endpoints/catalog/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
1
framework/spree/api/endpoints/catalog/products.ts
Normal file
1
framework/spree/api/endpoints/catalog/products.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
1
framework/spree/api/endpoints/checkout/index.ts
Normal file
1
framework/spree/api/endpoints/checkout/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
1
framework/spree/api/endpoints/customer/index.ts
Normal file
1
framework/spree/api/endpoints/customer/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
1
framework/spree/api/endpoints/login/index.ts
Normal file
1
framework/spree/api/endpoints/login/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
1
framework/spree/api/endpoints/logout/index.ts
Normal file
1
framework/spree/api/endpoints/logout/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
1
framework/spree/api/endpoints/signup/index.ts
Normal file
1
framework/spree/api/endpoints/signup/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
1
framework/spree/api/endpoints/wishlist/index.tsx
Normal file
1
framework/spree/api/endpoints/wishlist/index.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default function noopApi(...args: any[]): void {}
|
39
framework/spree/api/index.ts
Normal file
39
framework/spree/api/index.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import type { APIProvider, CommerceAPI, CommerceAPIConfig } from '@commerce/api'
|
||||||
|
import { getCommerceApi as commerceApi } from '@commerce/api'
|
||||||
|
import createApiFetch from './utils/create-api-fetch'
|
||||||
|
|
||||||
|
import getAllPages from './operations/get-all-pages'
|
||||||
|
import getPage from './operations/get-page'
|
||||||
|
import getSiteInfo from './operations/get-site-info'
|
||||||
|
import getCustomerWishlist from './operations/get-customer-wishlist'
|
||||||
|
import getAllProductPaths from './operations/get-all-product-paths'
|
||||||
|
import getAllProducts from './operations/get-all-products'
|
||||||
|
import getProduct from './operations/get-product'
|
||||||
|
|
||||||
|
export interface SpreeApiConfig extends CommerceAPIConfig {}
|
||||||
|
|
||||||
|
const config: SpreeApiConfig = {
|
||||||
|
commerceUrl: '',
|
||||||
|
apiToken: '',
|
||||||
|
cartCookie: '',
|
||||||
|
customerCookie: '',
|
||||||
|
cartCookieMaxAge: 2592000,
|
||||||
|
fetch: createApiFetch(() => getCommerceApi().getConfig()),
|
||||||
|
}
|
||||||
|
|
||||||
|
const operations = {
|
||||||
|
getAllPages,
|
||||||
|
getPage,
|
||||||
|
getSiteInfo,
|
||||||
|
getCustomerWishlist,
|
||||||
|
getAllProductPaths,
|
||||||
|
getAllProducts,
|
||||||
|
getProduct,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const provider: APIProvider = { config, operations }
|
||||||
|
|
||||||
|
export type SpreeApiProvider = APIProvider
|
||||||
|
|
||||||
|
export const getCommerceApi = (customProvider: APIProvider = provider) =>
|
||||||
|
commerceApi(customProvider)
|
37
framework/spree/api/operations/get-all-pages.ts
Normal file
37
framework/spree/api/operations/get-all-pages.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
export type Page = { url: string }
|
||||||
|
import { OperationContext, OperationOptions } from '@commerce/api/operations'
|
||||||
|
import { GetAllPagesOperation } from '@commerce/types/page'
|
||||||
|
import type { SpreeApiConfig, SpreeApiProvider } from '../index'
|
||||||
|
|
||||||
|
export default function getAllPagesOperation({
|
||||||
|
commerce,
|
||||||
|
}: OperationContext<SpreeApiProvider>) {
|
||||||
|
async function getAllPages<T extends GetAllPagesOperation>(options?: {
|
||||||
|
config?: Partial<SpreeApiConfig>
|
||||||
|
preview?: boolean
|
||||||
|
}): Promise<T['data']>
|
||||||
|
|
||||||
|
async function getAllPages<T extends GetAllPagesOperation>(
|
||||||
|
opts: {
|
||||||
|
config?: Partial<SpreeApiConfig>
|
||||||
|
preview?: boolean
|
||||||
|
} & OperationOptions
|
||||||
|
): Promise<T['data']>
|
||||||
|
|
||||||
|
async function getAllPages<T extends GetAllPagesOperation>({
|
||||||
|
config,
|
||||||
|
preview,
|
||||||
|
query,
|
||||||
|
}: {
|
||||||
|
url?: string
|
||||||
|
config?: Partial<SpreeApiConfig>
|
||||||
|
preview?: boolean
|
||||||
|
query?: string
|
||||||
|
} = {}): Promise<T['data']> {
|
||||||
|
return {
|
||||||
|
pages: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAllPages
|
||||||
|
}
|
17
framework/spree/api/operations/get-all-product-paths.ts
Normal file
17
framework/spree/api/operations/get-all-product-paths.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// import data from '../../data.json'
|
||||||
|
|
||||||
|
export type GetAllProductPathsResult = {
|
||||||
|
products: Array<{ path: string }>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function getAllProductPathsOperation() {
|
||||||
|
function getAllProductPaths(): Promise<GetAllProductPathsResult> {
|
||||||
|
return Promise.resolve({
|
||||||
|
// products: data.products.map(({ path }) => ({ path })),
|
||||||
|
// TODO: Return Storefront [{ path: '/long-sleeve-shirt' }, ...] from Spree products. Paths using product IDs are fine too.
|
||||||
|
products: [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAllProductPaths
|
||||||
|
}
|
79
framework/spree/api/operations/get-all-products.ts
Normal file
79
framework/spree/api/operations/get-all-products.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { Product } from '@commerce/types/product'
|
||||||
|
import { GetAllProductsOperation } from '@commerce/types/product'
|
||||||
|
import type { OperationContext } from '@commerce/api/operations'
|
||||||
|
import type { LocalConfig, Provider, SpreeApiProvider } from '../index'
|
||||||
|
import type { IProducts } from '@spree/storefront-api-v2-sdk/types/interfaces/Product'
|
||||||
|
// import data from '../../../local/data.json'
|
||||||
|
|
||||||
|
export default function getAllProductsOperation({
|
||||||
|
commerce,
|
||||||
|
}: OperationContext<SpreeApiProvider>) {
|
||||||
|
async function getAllProducts<T extends GetAllProductsOperation>({
|
||||||
|
query = 'products.list',
|
||||||
|
variables = { first: 10 },
|
||||||
|
config: userConfig,
|
||||||
|
}: {
|
||||||
|
query?: string
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: Partial<LocalConfig>
|
||||||
|
} = {}): Promise<{ products: Product[] | any[] }> {
|
||||||
|
const config = commerce.getConfig(userConfig)
|
||||||
|
const { fetch: apiFetch /*, locale*/ } = config
|
||||||
|
const first = variables.first // How many products to fetch.
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'sdfuasdufahsdf variables = ',
|
||||||
|
variables,
|
||||||
|
'query = ',
|
||||||
|
query,
|
||||||
|
'config = ',
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log('sdfasdg')
|
||||||
|
|
||||||
|
const { data } = await apiFetch<IProducts>(
|
||||||
|
query,
|
||||||
|
{ variables }
|
||||||
|
// {
|
||||||
|
// ...(locale && {}),
|
||||||
|
// }
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log('asuidfhasdf', data)
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// products: data.products.edges.map(({ node }) =>
|
||||||
|
// normalizeProduct(node as ShopifyProduct)
|
||||||
|
// ),
|
||||||
|
// }
|
||||||
|
|
||||||
|
const normalizedProducts: Product[] = data.data.map((spreeProduct) => {
|
||||||
|
return {
|
||||||
|
id: spreeProduct.id,
|
||||||
|
name: spreeProduct.attributes.name,
|
||||||
|
description: spreeProduct.attributes.description,
|
||||||
|
images: [],
|
||||||
|
variants: [],
|
||||||
|
options: [],
|
||||||
|
price: {
|
||||||
|
value: 10,
|
||||||
|
currencyCode: 'USD',
|
||||||
|
retailPrice: 8,
|
||||||
|
salePrice: 7,
|
||||||
|
listPrice: 6,
|
||||||
|
extendedSalePrice: 2,
|
||||||
|
extendedListPrice: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
// products: data.products,
|
||||||
|
// TODO: Return Spree products.
|
||||||
|
products: normalizedProducts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getAllProducts
|
||||||
|
}
|
6
framework/spree/api/operations/get-customer-wishlist.ts
Normal file
6
framework/spree/api/operations/get-customer-wishlist.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default function getCustomerWishlistOperation() {
|
||||||
|
function getCustomerWishlist(): any {
|
||||||
|
return { wishlist: {} }
|
||||||
|
}
|
||||||
|
return getCustomerWishlist
|
||||||
|
}
|
13
framework/spree/api/operations/get-page.ts
Normal file
13
framework/spree/api/operations/get-page.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export type Page = any
|
||||||
|
export type GetPageResult = { page?: Page }
|
||||||
|
|
||||||
|
export type PageVariables = {
|
||||||
|
id: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function getPageOperation() {
|
||||||
|
function getPage(): Promise<GetPageResult> {
|
||||||
|
return Promise.resolve({})
|
||||||
|
}
|
||||||
|
return getPage
|
||||||
|
}
|
28
framework/spree/api/operations/get-product.ts
Normal file
28
framework/spree/api/operations/get-product.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import type { LocalConfig } from '../index'
|
||||||
|
import { Product } from '@commerce/types/product'
|
||||||
|
import { GetProductOperation } from '@commerce/types/product'
|
||||||
|
import data from '../../../local/data.json'
|
||||||
|
import type { OperationContext } from '@commerce/api/operations'
|
||||||
|
|
||||||
|
export default function getProductOperation({
|
||||||
|
commerce,
|
||||||
|
}: OperationContext<any>) {
|
||||||
|
async function getProduct<T extends GetProductOperation>({
|
||||||
|
query = '',
|
||||||
|
variables,
|
||||||
|
config,
|
||||||
|
}: {
|
||||||
|
query?: string
|
||||||
|
variables?: T['variables']
|
||||||
|
config?: Partial<LocalConfig>
|
||||||
|
preview?: boolean
|
||||||
|
} = {}): Promise<Product | {} | any> {
|
||||||
|
return {
|
||||||
|
product: data.products.find(({ slug }) => slug === variables!.slug),
|
||||||
|
// TODO: Return Spree product.
|
||||||
|
// product: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getProduct
|
||||||
|
}
|
44
framework/spree/api/operations/get-site-info.ts
Normal file
44
framework/spree/api/operations/get-site-info.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { OperationContext } from '@commerce/api/operations'
|
||||||
|
import { Category } from '@commerce/types/site'
|
||||||
|
import { LocalConfig } from '../index'
|
||||||
|
|
||||||
|
export type GetSiteInfoResult<
|
||||||
|
T extends { categories: any[]; brands: any[] } = {
|
||||||
|
categories: Category[]
|
||||||
|
brands: any[]
|
||||||
|
}
|
||||||
|
> = T
|
||||||
|
|
||||||
|
export default function getSiteInfoOperation({}: OperationContext<any>) {
|
||||||
|
// TODO: Get Spree categories for display in React components.
|
||||||
|
function getSiteInfo({
|
||||||
|
query,
|
||||||
|
variables,
|
||||||
|
config: cfg,
|
||||||
|
}: {
|
||||||
|
query?: string
|
||||||
|
variables?: any
|
||||||
|
config?: Partial<LocalConfig>
|
||||||
|
preview?: boolean
|
||||||
|
} = {}): Promise<GetSiteInfoResult> {
|
||||||
|
return Promise.resolve({
|
||||||
|
categories: [
|
||||||
|
{
|
||||||
|
id: 'new-arrivals',
|
||||||
|
name: 'New Arrivals',
|
||||||
|
slug: 'new-arrivals',
|
||||||
|
path: '/new-arrivals',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'featured',
|
||||||
|
name: 'Featured',
|
||||||
|
slug: 'featured',
|
||||||
|
path: '/featured',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
brands: [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSiteInfo
|
||||||
|
}
|
6
framework/spree/api/operations/index.ts
Normal file
6
framework/spree/api/operations/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export { default as getPage } from './get-page'
|
||||||
|
export { default as getSiteInfo } from './get-site-info'
|
||||||
|
export { default as getAllPages } from './get-all-pages'
|
||||||
|
export { default as getProduct } from './get-product'
|
||||||
|
export { default as getAllProducts } from './get-all-products'
|
||||||
|
export { default as getAllProductPaths } from './get-all-product-paths'
|
154
framework/spree/api/utils/create-api-fetch.ts
Normal file
154
framework/spree/api/utils/create-api-fetch.ts
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// import { FetcherError } from '@commerce/utils/errors'
|
||||||
|
// import type { GraphQLFetcher } from '@commerce/api'
|
||||||
|
// import type { BigcommerceConfig } from '../index'
|
||||||
|
|
||||||
|
import { GraphQLFetcher, GraphQLFetcherResult } from '@commerce/api'
|
||||||
|
import { SpreeApiConfig } from '..'
|
||||||
|
import { errors, makeClient } from '@spree/storefront-api-v2-sdk'
|
||||||
|
import { requireConfigValue } from 'framework/spree/isomorphicConfig'
|
||||||
|
import convertSpreeErrorToGraphQlError from 'framework/spree/utils/convertSpreeErrorToGraphQlError'
|
||||||
|
import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse'
|
||||||
|
import type {
|
||||||
|
JsonApiDocument,
|
||||||
|
JsonApiListResponse,
|
||||||
|
} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi'
|
||||||
|
// import fetch from './fetch'
|
||||||
|
|
||||||
|
// const fetchGraphqlApi: (getConfig: () => BigcommerceConfig) => GraphQLFetcher =
|
||||||
|
// (getConfig) =>
|
||||||
|
// async (query: string, { variables, preview } = {}, fetchOptions) => {
|
||||||
|
// // log.warn(query)
|
||||||
|
// const config = getConfig()
|
||||||
|
// const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
|
||||||
|
// ...fetchOptions,
|
||||||
|
// method: 'POST',
|
||||||
|
// headers: {
|
||||||
|
// Authorization: `Bearer ${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 Bigcommerce API' }],
|
||||||
|
// status: res.status,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return { data: json.data, res }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export default fetchGraphqlApi
|
||||||
|
|
||||||
|
const createApiFetch: (
|
||||||
|
getConfig: () => SpreeApiConfig
|
||||||
|
) => GraphQLFetcher<
|
||||||
|
GraphQLFetcherResult<JsonApiDocument | JsonApiListResponse>
|
||||||
|
> = (getConfig) => {
|
||||||
|
const client = makeClient({ host: requireConfigValue('spreeApiHost') })
|
||||||
|
|
||||||
|
// FIXME: Allow Spree SDK to use fetch instead of axios.
|
||||||
|
return async (query, queryData = {}, fetchOptions = {}) => {
|
||||||
|
const url = query
|
||||||
|
console.log('ydsfgasgdfagsdf', url)
|
||||||
|
const { variables } = queryData
|
||||||
|
let prev = null // FIXME:
|
||||||
|
const clientEndpointMethod = url
|
||||||
|
.split('.')
|
||||||
|
.reduce((clientNode: any, pathPart) => {
|
||||||
|
prev = clientNode
|
||||||
|
//FIXME: use actual type instead of any.
|
||||||
|
// TODO: Fix clientNode type
|
||||||
|
return clientNode[pathPart]
|
||||||
|
}, client)
|
||||||
|
.bind(prev)
|
||||||
|
|
||||||
|
console.log('aisdfuiuashdf', clientEndpointMethod)
|
||||||
|
|
||||||
|
const storeResponse: ResultResponse<JsonApiDocument | JsonApiListResponse> =
|
||||||
|
await clientEndpointMethod() // FIXME: Not the best to use variables here as it's type is any.
|
||||||
|
// await clientEndpointMethod(...variables.args) // FIXME: Not the best to use variables here as it's type is any.
|
||||||
|
|
||||||
|
console.log('87868767868', storeResponse)
|
||||||
|
|
||||||
|
if (storeResponse.success()) {
|
||||||
|
return {
|
||||||
|
data: storeResponse.success(),
|
||||||
|
res: storeResponse as any, //FIXME: MUST BE FETCH RESPONSE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const storeResponseError = storeResponse.fail()
|
||||||
|
|
||||||
|
if (storeResponseError instanceof errors.SpreeError) {
|
||||||
|
throw convertSpreeErrorToGraphQlError(storeResponseError)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw storeResponseError
|
||||||
|
// throw getError(
|
||||||
|
// [
|
||||||
|
// {
|
||||||
|
// message: `${err} \n Most likely related to an unexpected output. e.g the store might be protected with password or not available.`,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// 500
|
||||||
|
// )
|
||||||
|
// console.log('jsdkfhjasdf', getConfig())
|
||||||
|
// // await
|
||||||
|
// return {
|
||||||
|
// data: [],
|
||||||
|
// res: ,
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createApiFetch
|
||||||
|
|
||||||
|
// LOCAL
|
||||||
|
|
||||||
|
// fetch<Data = any, Variables = any>(
|
||||||
|
// query: string,
|
||||||
|
// queryData?: CommerceAPIFetchOptions<Variables>,
|
||||||
|
// fetchOptions?: RequestInit
|
||||||
|
// ): Promise<GraphQLFetcherResult<Data>>
|
||||||
|
|
||||||
|
// import { FetcherError } from '@commerce/utils/errors'
|
||||||
|
// import type { GraphQLFetcher } from '@commerce/api'
|
||||||
|
// import type { LocalConfig } from '../index'
|
||||||
|
// import fetch from './fetch'
|
||||||
|
|
||||||
|
// const fetchGraphqlApi: (getConfig: () => LocalConfig) => GraphQLFetcher =
|
||||||
|
// (getConfig) =>
|
||||||
|
// async (query: string, { variables, preview } = {}, fetchOptions) => {
|
||||||
|
// const config = getConfig()
|
||||||
|
// const res = await fetch(config.commerceUrl, {
|
||||||
|
// ...fetchOptions,
|
||||||
|
// method: 'POST',
|
||||||
|
// headers: {
|
||||||
|
// ...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 for API' }],
|
||||||
|
// status: res.status,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return { data: json.data, res }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export default fetchGraphqlApi
|
3
framework/spree/api/utils/fetch.ts
Normal file
3
framework/spree/api/utils/fetch.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import zeitFetch from '@vercel/fetch'
|
||||||
|
|
||||||
|
export default zeitFetch()
|
3
framework/spree/auth/index.ts
Normal file
3
framework/spree/auth/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export { default as useLogin } from './use-login'
|
||||||
|
export { default as useLogout } from './use-logout'
|
||||||
|
export { default as useSignup } from './use-signup'
|
16
framework/spree/auth/use-login.tsx
Normal file
16
framework/spree/auth/use-login.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { MutationHook } from '@commerce/utils/types'
|
||||||
|
import useLogin, { UseLogin } from '@commerce/auth/use-login'
|
||||||
|
|
||||||
|
export default useLogin as UseLogin<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
useHook: () => () => {
|
||||||
|
return async function () {}
|
||||||
|
},
|
||||||
|
}
|
17
framework/spree/auth/use-logout.tsx
Normal file
17
framework/spree/auth/use-logout.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { MutationHook } from '@commerce/utils/types'
|
||||||
|
import useLogout, { UseLogout } from '@commerce/auth/use-logout'
|
||||||
|
|
||||||
|
export default useLogout as UseLogout<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
useHook:
|
||||||
|
({ fetch }) =>
|
||||||
|
() =>
|
||||||
|
async () => {},
|
||||||
|
}
|
19
framework/spree/auth/use-signup.tsx
Normal file
19
framework/spree/auth/use-signup.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
import useCustomer from '../customer/use-customer'
|
||||||
|
import { MutationHook } from '@commerce/utils/types'
|
||||||
|
import useSignup, { UseSignup } from '@commerce/auth/use-signup'
|
||||||
|
|
||||||
|
export default useSignup as UseSignup<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
useHook:
|
||||||
|
({ fetch }) =>
|
||||||
|
() =>
|
||||||
|
() => {},
|
||||||
|
}
|
4
framework/spree/cart/index.ts
Normal file
4
framework/spree/cart/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
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 useUpdateItem } from './use-update-item'
|
17
framework/spree/cart/use-add-item.tsx
Normal file
17
framework/spree/cart/use-add-item.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
|
||||||
|
import { MutationHook } from '@commerce/utils/types'
|
||||||
|
|
||||||
|
export default useAddItem as UseAddItem<typeof handler>
|
||||||
|
export const handler: MutationHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher({ input, options, fetch }) {},
|
||||||
|
useHook:
|
||||||
|
({ fetch }) =>
|
||||||
|
() => {
|
||||||
|
return async function addItem() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
42
framework/spree/cart/use-cart.tsx
Normal file
42
framework/spree/cart/use-cart.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import { SWRHook } from '@commerce/utils/types'
|
||||||
|
import useCart, { UseCart } from '@commerce/cart/use-cart'
|
||||||
|
|
||||||
|
export default useCart as UseCart<typeof handler>
|
||||||
|
|
||||||
|
export const handler: SWRHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher() {
|
||||||
|
return {
|
||||||
|
id: '',
|
||||||
|
createdAt: '',
|
||||||
|
currency: { code: '' },
|
||||||
|
taxesIncluded: '',
|
||||||
|
lineItems: [],
|
||||||
|
lineItemsSubtotalPrice: '',
|
||||||
|
subtotalPrice: 0,
|
||||||
|
totalPrice: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
useHook:
|
||||||
|
({ useData }) =>
|
||||||
|
(input) => {
|
||||||
|
return useMemo(
|
||||||
|
() =>
|
||||||
|
Object.create(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
isEmpty: {
|
||||||
|
get() {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
18
framework/spree/cart/use-remove-item.tsx
Normal file
18
framework/spree/cart/use-remove-item.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { MutationHook } from '@commerce/utils/types'
|
||||||
|
import useRemoveItem, { UseRemoveItem } from '@commerce/cart/use-remove-item'
|
||||||
|
|
||||||
|
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher({ input, options, fetch }) {},
|
||||||
|
useHook:
|
||||||
|
({ fetch }) =>
|
||||||
|
() => {
|
||||||
|
return async function removeItem(input) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
18
framework/spree/cart/use-update-item.tsx
Normal file
18
framework/spree/cart/use-update-item.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { MutationHook } from '@commerce/utils/types'
|
||||||
|
import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item'
|
||||||
|
|
||||||
|
export default useUpdateItem as UseUpdateItem<any>
|
||||||
|
|
||||||
|
export const handler: MutationHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher({ input, options, fetch }) {},
|
||||||
|
useHook:
|
||||||
|
({ fetch }) =>
|
||||||
|
() => {
|
||||||
|
return async function addItem() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
@ -1,88 +0,0 @@
|
|||||||
import type { Fetcher } from '@commerce/utils/types'
|
|
||||||
import convertSpreeErrorToGraphQlError from './utils/convertSpreeErrorToGraphQlError'
|
|
||||||
import { makeClient } from '@spree/storefront-api-v2-sdk'
|
|
||||||
import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse'
|
|
||||||
import type {
|
|
||||||
JsonApiDocument,
|
|
||||||
JsonApiListResponse,
|
|
||||||
} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi'
|
|
||||||
import { errors } from '@spree/storefront-api-v2-sdk'
|
|
||||||
// import { API_TOKEN, API_URL } from './const'
|
|
||||||
// import { handleFetchResponse } from './utils'
|
|
||||||
|
|
||||||
const createFetcher = (fetcherOptions: any): Fetcher => {
|
|
||||||
const { host } = fetcherOptions
|
|
||||||
const client = makeClient({ host })
|
|
||||||
|
|
||||||
//TODO: Add types to fetcherOptions
|
|
||||||
return async (requestOptions) => {
|
|
||||||
console.log('FETCHER')
|
|
||||||
// url?: string
|
|
||||||
// query?: string
|
|
||||||
// method?: string
|
|
||||||
// variables?: any
|
|
||||||
// body?: Body
|
|
||||||
const { url, method, variables, query } = requestOptions
|
|
||||||
const { locale, ...vars } = variables ?? {}
|
|
||||||
|
|
||||||
if (!url) {
|
|
||||||
// TODO: Create a custom type for this error.
|
|
||||||
throw new Error('Url not provider for fetcher.')
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`Fetching products using options: ${JSON.stringify(requestOptions)}.`
|
|
||||||
)
|
|
||||||
|
|
||||||
// const storeResponse = await fetch(url, {
|
|
||||||
// method,
|
|
||||||
// body: JSON.stringify({ query, variables: vars }),
|
|
||||||
// headers: {
|
|
||||||
// 'X-Shopify-Storefront-Access-Token': API_TOKEN,
|
|
||||||
// 'Content-Type': 'application/json', TODO: Probably not needed. Check!
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const storeResponse.json()
|
|
||||||
|
|
||||||
// if (storeResponse.ok) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: Not best to use url for finding the method, but should be good enough for now.
|
|
||||||
|
|
||||||
const clientEndpointMethod = url
|
|
||||||
.split('.')
|
|
||||||
.reduce((clientNode: any, pathPart) => {
|
|
||||||
// TODO: Fix clientNode type
|
|
||||||
return clientNode[pathPart]
|
|
||||||
}, client)
|
|
||||||
|
|
||||||
const storeResponse: ResultResponse<JsonApiDocument | JsonApiListResponse> =
|
|
||||||
await clientEndpointMethod(...variables.args) // TODO: Not the best to use variables here as it's type is any.
|
|
||||||
|
|
||||||
if (storeResponse.success()) {
|
|
||||||
return storeResponse.success()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (storeResponse.fail() instanceof errors.SpreeError) {
|
|
||||||
throw convertSpreeErrorToGraphQlError(storeResponse.fail())
|
|
||||||
}
|
|
||||||
|
|
||||||
throw storeResponse.fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// import { Fetcher } from '@commerce/utils/types'
|
|
||||||
|
|
||||||
// export const fetcher: Fetcher = async () => {
|
|
||||||
// console.log('FETCHER')
|
|
||||||
// const res = await fetch('./data.json')
|
|
||||||
// if (res.ok) {
|
|
||||||
// const { data } = await res.json()
|
|
||||||
// return data
|
|
||||||
// }
|
|
||||||
// throw res
|
|
||||||
// }
|
|
||||||
|
|
||||||
export default createFetcher
|
|
@ -1,43 +0,0 @@
|
|||||||
import type { Provider } from '@commerce'
|
|
||||||
import { Config } from '.'
|
|
||||||
import fetcher from './fetcher'
|
|
||||||
|
|
||||||
// import { handler as useCart } from './cart/use-cart'
|
|
||||||
// import { handler as useAddItem } from './cart/use-add-item'
|
|
||||||
// import { handler as useUpdateItem } from './cart/use-update-item'
|
|
||||||
// import { handler as useRemoveItem } from './cart/use-remove-item'
|
|
||||||
|
|
||||||
// import { handler as useCustomer } from './customer/use-customer'
|
|
||||||
// import { handler as useSearch } from './product/use-search'
|
|
||||||
|
|
||||||
// import { handler as useLogin } from './auth/use-login'
|
|
||||||
// import { handler as useLogout } from './auth/use-logout'
|
|
||||||
// import { handler as useSignup } from './auth/use-signup'
|
|
||||||
|
|
||||||
// export const saleorProvider = {
|
|
||||||
// locale: 'en-us',
|
|
||||||
// cartCookie: '',
|
|
||||||
// cartCookieToken: '',
|
|
||||||
// fetcher,
|
|
||||||
// cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
|
|
||||||
// customer: { useCustomer },
|
|
||||||
// products: { useSearch },
|
|
||||||
// auth: { useLogin, useLogout, useSignup },
|
|
||||||
// }
|
|
||||||
|
|
||||||
export const createProvider = (options: { config: Config }): Provider => {
|
|
||||||
const { config } = options
|
|
||||||
|
|
||||||
return {
|
|
||||||
locale: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string.
|
|
||||||
cartCookie: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string.
|
|
||||||
fetcher: createFetcher({ host: config.store.host }),
|
|
||||||
// FIXME: Add dummy hooks for below based on framework/local EXCEPT use-product
|
|
||||||
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
|
|
||||||
customer: { useCustomer },
|
|
||||||
products: { useSearch },
|
|
||||||
auth: { useLogin, useLogout, useSignup },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type { Provider }
|
|
1
framework/spree/customer/index.ts
Normal file
1
framework/spree/customer/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as useCustomer } from './use-customer'
|
15
framework/spree/customer/use-customer.tsx
Normal file
15
framework/spree/customer/use-customer.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { SWRHook } from '@commerce/utils/types'
|
||||||
|
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
|
||||||
|
|
||||||
|
export default useCustomer as UseCustomer<typeof handler>
|
||||||
|
export const handler: SWRHook<any> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
async fetcher({ input, options, fetch }) {},
|
||||||
|
useHook: () => () => {
|
||||||
|
return async function addItem() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
1
framework/spree/errors/MisconfigurationError.ts
Normal file
1
framework/spree/errors/MisconfigurationError.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default class MisconfigurationError extends Error {}
|
1
framework/spree/errors/MissingConfigurationValueError.ts
Normal file
1
framework/spree/errors/MissingConfigurationValueError.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default class MissingConfigurationValueError extends Error {}
|
86
framework/spree/fetcher.ts
Normal file
86
framework/spree/fetcher.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import type { Fetcher } from '@commerce/utils/types'
|
||||||
|
import convertSpreeErrorToGraphQlError from './utils/convertSpreeErrorToGraphQlError'
|
||||||
|
import { makeClient } from '@spree/storefront-api-v2-sdk'
|
||||||
|
import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse'
|
||||||
|
import type {
|
||||||
|
JsonApiDocument,
|
||||||
|
JsonApiListResponse,
|
||||||
|
} from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi'
|
||||||
|
import { errors } from '@spree/storefront-api-v2-sdk'
|
||||||
|
import { requireConfigValue } from './isomorphicConfig'
|
||||||
|
// import { handleFetchResponse } from './utils'
|
||||||
|
|
||||||
|
const client = makeClient({ host: requireConfigValue('spreeApiHost') })
|
||||||
|
|
||||||
|
const fetcher: Fetcher = async (requestOptions) => {
|
||||||
|
console.log('Fetcher called')
|
||||||
|
// url?: string
|
||||||
|
// query?: string
|
||||||
|
// method?: string
|
||||||
|
// variables?: any
|
||||||
|
// body?: Body
|
||||||
|
const { url, method, variables, query } = requestOptions
|
||||||
|
const { locale, ...vars } = variables ?? {}
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
// TODO: Create a custom type for this error.
|
||||||
|
throw new Error('Url not provider for fetcher.')
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Fetching products using options: ${JSON.stringify(requestOptions)}.`
|
||||||
|
)
|
||||||
|
|
||||||
|
// const storeResponse = await fetch(url, {
|
||||||
|
// method,
|
||||||
|
// body: JSON.stringify({ query, variables: vars }),
|
||||||
|
// headers: {
|
||||||
|
// 'X-Shopify-Storefront-Access-Token': API_TOKEN,
|
||||||
|
// 'Content-Type': 'application/json', TODO: Probably not needed. Check!
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
|
||||||
|
// const storeResponse.json()
|
||||||
|
|
||||||
|
// if (storeResponse.ok) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: Not best to use url for finding the method, but should be good enough for now.
|
||||||
|
|
||||||
|
const clientEndpointMethod = url
|
||||||
|
.split('.')
|
||||||
|
.reduce((clientNode: any, pathPart) => {
|
||||||
|
// TODO: Fix clientNode type
|
||||||
|
return clientNode[pathPart]
|
||||||
|
}, client)
|
||||||
|
|
||||||
|
const storeResponse: ResultResponse<JsonApiDocument | JsonApiListResponse> =
|
||||||
|
await clientEndpointMethod(...variables.args) // TODO: Not the best to use variables here as it's type is any.
|
||||||
|
|
||||||
|
if (storeResponse.success()) {
|
||||||
|
return storeResponse.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
const storeResponseError = storeResponse.fail()
|
||||||
|
|
||||||
|
if (storeResponseError instanceof errors.SpreeError) {
|
||||||
|
throw convertSpreeErrorToGraphQlError(storeResponseError)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw storeResponseError
|
||||||
|
}
|
||||||
|
|
||||||
|
// import { Fetcher } from '@commerce/utils/types'
|
||||||
|
|
||||||
|
// export const fetcher: Fetcher = async () => {
|
||||||
|
// console.log('FETCHER')
|
||||||
|
// const res = await fetch('./data.json')
|
||||||
|
// if (res.ok) {
|
||||||
|
// const { data } = await res.json()
|
||||||
|
// return data
|
||||||
|
// }
|
||||||
|
// throw res
|
||||||
|
// }
|
||||||
|
|
||||||
|
export default fetcher
|
@ -7,37 +7,29 @@ import {
|
|||||||
useCommerce as useCoreCommerce,
|
useCommerce as useCoreCommerce,
|
||||||
} from '@commerce'
|
} from '@commerce'
|
||||||
|
|
||||||
// import { provider, Provider } from './provider'
|
import { provider } from './provider'
|
||||||
import { createProvider, Provider } from './createProvider'
|
import type { Provider } from './provider'
|
||||||
|
import { requireConfigValue } from './isomorphicConfig'
|
||||||
// export { provider }
|
|
||||||
|
|
||||||
// TODO: Below is probably not needed. Expect default values to be set by NextJS Commerce and be ok for now.
|
|
||||||
// export const saleorConfig: CommerceConfig = {
|
|
||||||
// locale: 'en-us',
|
|
||||||
// cartCookie: Const.CHECKOUT_ID_COOKIE,
|
|
||||||
// }
|
|
||||||
|
|
||||||
export type Config = {
|
|
||||||
store: {
|
|
||||||
host: string
|
|
||||||
}
|
|
||||||
} & CommerceConfig // This is the type that holds any custom values specifically for the Spree Framework.
|
|
||||||
|
|
||||||
export type SpreeProps = {
|
export type SpreeProps = {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
provider: Provider
|
provider: Provider
|
||||||
config: Config
|
config: SpreeConfig
|
||||||
} & Config
|
} & SpreeConfig
|
||||||
|
|
||||||
export function CommerceProvider({ children, ...config }: SpreeProps) {
|
export const spreeCommerceConfigDefaults: CommerceConfig = {
|
||||||
console.log('CommerceProvider called')
|
locale: requireConfigValue('defaultLocale'),
|
||||||
|
cartCookie: requireConfigValue('cartCookieName'),
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Make sure this doesn't get called all the time. If it does, useMemo.
|
export type SpreeConfig = CommerceConfig
|
||||||
const provider = createProvider({ config })
|
|
||||||
|
|
||||||
|
export function CommerceProvider({ children, ...restProps }: SpreeProps) {
|
||||||
return (
|
return (
|
||||||
<CoreCommerceProvider provider={provider} config={config}>
|
<CoreCommerceProvider
|
||||||
|
provider={provider}
|
||||||
|
config={{ ...spreeCommerceConfigDefaults, ...restProps }}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</CoreCommerceProvider>
|
</CoreCommerceProvider>
|
||||||
)
|
)
|
||||||
|
21
framework/spree/isomorphicConfig.ts
Normal file
21
framework/spree/isomorphicConfig.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import forceIsomorphicConfigValues from './utils/forceIsomorphicConfigValues'
|
||||||
|
import requireConfig from './utils/requireConfig'
|
||||||
|
|
||||||
|
const isomorphicConfig = {
|
||||||
|
spreeApiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST,
|
||||||
|
defaultLocale: process.env.NEXT_PUBLIC_SPREE_DEFAULT_LOCALE,
|
||||||
|
cartCookieName: process.env.NEXT_PUBLIC_SPREE_CART_COOKIE_NAME,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default forceIsomorphicConfigValues(
|
||||||
|
isomorphicConfig,
|
||||||
|
['defaultLocale', 'cartCookieName'],
|
||||||
|
['spreeApiHost']
|
||||||
|
)
|
||||||
|
|
||||||
|
type IsomorphicConfig = typeof isomorphicConfig
|
||||||
|
|
||||||
|
const requireConfigValue = (key: keyof IsomorphicConfig) =>
|
||||||
|
requireConfig<IsomorphicConfig>(isomorphicConfig, key)
|
||||||
|
|
||||||
|
export { requireConfigValue }
|
@ -2,12 +2,7 @@ const commerce = require('./commerce.config.json')
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
commerce,
|
commerce,
|
||||||
store: {
|
|
||||||
host: process.env.SPREE_API_HOST,
|
|
||||||
},
|
|
||||||
// images: {
|
// images: {
|
||||||
// domains: [process.env.COMMERCE_IMAGE_HOST],
|
// domains: [process.env.COMMERCE_IMAGE_HOST],
|
||||||
// },
|
// },
|
||||||
// locale: 'en-us',
|
|
||||||
// cartCookie: Const.CHECKOUT_ID_COOKIE,
|
|
||||||
}
|
}
|
||||||
|
2
framework/spree/product/index.ts
Normal file
2
framework/spree/product/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { default as usePrice } from './use-price'
|
||||||
|
export { default as useSearch } from './use-search'
|
2
framework/spree/product/use-price.tsx
Normal file
2
framework/spree/product/use-price.tsx
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from '@commerce/product/use-price'
|
||||||
|
export { default } from '@commerce/product/use-price'
|
28
framework/spree/provider.ts
Normal file
28
framework/spree/provider.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import type { Provider } from '@commerce'
|
||||||
|
import fetcher from './fetcher'
|
||||||
|
|
||||||
|
// TODO: Using dummy hooks to fetch static content. Based on the local framework.
|
||||||
|
import { handler as useCart } from './cart/use-cart'
|
||||||
|
import { handler as useAddItem } from './cart/use-add-item'
|
||||||
|
import { handler as useUpdateItem } from './cart/use-update-item'
|
||||||
|
import { handler as useRemoveItem } from './cart/use-remove-item'
|
||||||
|
import { handler as useCustomer } from './customer/use-customer'
|
||||||
|
import { handler as useSearch } from './product/use-search'
|
||||||
|
import { handler as useLogin } from './auth/use-login'
|
||||||
|
import { handler as useLogout } from './auth/use-logout'
|
||||||
|
import { handler as useSignup } from './auth/use-signup'
|
||||||
|
|
||||||
|
const provider = {
|
||||||
|
locale: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string.
|
||||||
|
cartCookie: '', // Not an optional key in TypeScript, but already set in config. So, just make it an empty string.
|
||||||
|
fetcher,
|
||||||
|
// FIXME: Add dummy hooks for below based on framework/local EXCEPT use-product
|
||||||
|
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
|
||||||
|
customer: { useCustomer },
|
||||||
|
products: { useSearch },
|
||||||
|
auth: { useLogin, useLogout, useSignup },
|
||||||
|
}
|
||||||
|
|
||||||
|
export { provider }
|
||||||
|
|
||||||
|
export type { Provider }
|
5
framework/spree/types/index.ts
Normal file
5
framework/spree/types/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export type UnknownObjectValues = Record<string, unknown>
|
||||||
|
|
||||||
|
export type NonUndefined<T> = T extends undefined ? never : T
|
||||||
|
|
||||||
|
export type ValueOf<T> = T[keyof T]
|
43
framework/spree/utils/forceIsomorphicConfigValues.ts
Normal file
43
framework/spree/utils/forceIsomorphicConfigValues.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import type { NonUndefined, UnknownObjectValues } from '../types'
|
||||||
|
import MisconfigurationError from '../errors/MisconfigurationError'
|
||||||
|
import isServer from './isServer'
|
||||||
|
|
||||||
|
const generateMisconfigurationErrorMessage = (
|
||||||
|
keys: Array<string | number | symbol>
|
||||||
|
) => `${keys.join(', ')} must have values before running the Framework.`
|
||||||
|
|
||||||
|
const forceIsomorphicConfigValues = <
|
||||||
|
X extends keyof T,
|
||||||
|
T extends UnknownObjectValues,
|
||||||
|
H extends Record<X, NonUndefined<T[X]>>
|
||||||
|
>(
|
||||||
|
config: T,
|
||||||
|
requiredServerKeys: string[],
|
||||||
|
requiredPublicKeys: X[]
|
||||||
|
) => {
|
||||||
|
if (isServer) {
|
||||||
|
const missingServerConfigValues = requiredServerKeys.filter(
|
||||||
|
(requiredServerKey) => typeof config[requiredServerKey] === 'undefined'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (missingServerConfigValues.length > 0) {
|
||||||
|
throw new MisconfigurationError(
|
||||||
|
generateMisconfigurationErrorMessage(missingServerConfigValues)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const missingPublicConfigValues = requiredPublicKeys.filter(
|
||||||
|
(requiredPublicKey) => typeof config[requiredPublicKey] === 'undefined'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (missingPublicConfigValues.length > 0) {
|
||||||
|
throw new MisconfigurationError(
|
||||||
|
generateMisconfigurationErrorMessage(missingPublicConfigValues)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config as T & H
|
||||||
|
}
|
||||||
|
|
||||||
|
export default forceIsomorphicConfigValues
|
1
framework/spree/utils/isServer.ts
Normal file
1
framework/spree/utils/isServer.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default typeof window === 'undefined'
|
16
framework/spree/utils/requireConfig.ts
Normal file
16
framework/spree/utils/requireConfig.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import MissingConfigurationValueError from '../errors/MissingConfigurationValueError'
|
||||||
|
import type { NonUndefined, ValueOf } from '../types'
|
||||||
|
|
||||||
|
const requireConfig = <T>(isomorphicConfig: T, key: keyof T) => {
|
||||||
|
const valueUnderKey = isomorphicConfig[key]
|
||||||
|
|
||||||
|
if (typeof valueUnderKey === 'undefined') {
|
||||||
|
throw new MissingConfigurationValueError(
|
||||||
|
`Value for configuration key ${key} was undefined.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return valueUnderKey as NonUndefined<ValueOf<T>>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default requireConfig
|
13
framework/spree/wishlist/use-add-item.tsx
Normal file
13
framework/spree/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/spree/wishlist/use-remove-item.tsx
Normal file
17
framework/spree/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
|
43
framework/spree/wishlist/use-wishlist.tsx
Normal file
43
framework/spree/wishlist/use-wishlist.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { HookFetcher } from '@commerce/utils/types'
|
||||||
|
import type { Product } from '@commerce/types/product'
|
||||||
|
|
||||||
|
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>
|
||||||
|
swrOptions?: any
|
||||||
|
) {
|
||||||
|
const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => {
|
||||||
|
return { data: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
useWishlist.extend = extendHook
|
||||||
|
|
||||||
|
return useWishlist
|
||||||
|
}
|
||||||
|
|
||||||
|
export default extendHook(fetcher)
|
@ -2,9 +2,9 @@
|
|||||||
"name": "nextjs-commerce",
|
"name": "nextjs-commerce",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "NODE_OPTIONS='--inspect' next dev",
|
"dev": "NODE_OPTIONS='--inspect' next dev -p 4000",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start -p 4000",
|
||||||
"analyze": "BUNDLE_ANALYZE=both yarn build",
|
"analyze": "BUNDLE_ANALYZE=both yarn build",
|
||||||
"prettier-fix": "prettier --write .",
|
"prettier-fix": "prettier --write .",
|
||||||
"find:unused": "npx next-unused",
|
"find:unused": "npx next-unused",
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
"@components/*": ["components/*"],
|
"@components/*": ["components/*"],
|
||||||
"@commerce": ["framework/commerce"],
|
"@commerce": ["framework/commerce"],
|
||||||
"@commerce/*": ["framework/commerce/*"],
|
"@commerce/*": ["framework/commerce/*"],
|
||||||
"@framework": ["framework/local"],
|
"@framework": ["framework/spree"],
|
||||||
"@framework/*": ["framework/local/*"]
|
"@framework/*": ["framework/spree/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],
|
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user