mirror of
https://github.com/vercel/commerce.git
synced 2025-05-17 15:06:59 +00:00
Merge branch 'custom-fields' of github.com:vercel/commerce into main
This commit is contained in:
commit
15faacc7de
@ -63,6 +63,7 @@
|
|||||||
"react-dom": "^18"
|
"react-dom": "^18"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@manifoldco/swagger-to-ts": "^2.1.0",
|
||||||
"@taskr/clear": "^1.1.0",
|
"@taskr/clear": "^1.1.0",
|
||||||
"@taskr/esnext": "^1.1.0",
|
"@taskr/esnext": "^1.1.0",
|
||||||
"@taskr/watch": "^1.1.0",
|
"@taskr/watch": "^1.1.0",
|
||||||
@ -74,6 +75,7 @@
|
|||||||
"@types/react": "^18.0.14",
|
"@types/react": "^18.0.14",
|
||||||
"lint-staged": "^12.1.7",
|
"lint-staged": "^12.1.7",
|
||||||
"next": "^12.0.8",
|
"next": "^12.0.8",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
@ -80,6 +80,15 @@ export const productInfoFragment = /* GraphQL */ `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
customFields {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
entityId
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
localeMeta: metafields(namespace: $locale, keys: ["name", "description"])
|
localeMeta: metafields(namespace: $locale, keys: ["name", "description"])
|
||||||
@include(if: $hasLocale) {
|
@include(if: $hasLocale) {
|
||||||
edges {
|
edges {
|
||||||
|
@ -5,10 +5,12 @@ import type {
|
|||||||
import type {
|
import type {
|
||||||
GetCustomerWishlistOperation,
|
GetCustomerWishlistOperation,
|
||||||
Wishlist,
|
Wishlist,
|
||||||
|
WishlistItem,
|
||||||
} from '@vercel/commerce/types/wishlist'
|
} from '@vercel/commerce/types/wishlist'
|
||||||
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
import type { RecursivePartial } from '../utils/types'
|
||||||
import { BigcommerceConfig, Provider } from '..'
|
import { BigcommerceConfig, Provider } from '..'
|
||||||
import getAllProducts, { ProductEdge } from './get-all-products'
|
|
||||||
|
import type { Product } from '@vercel/commerce/types/product'
|
||||||
|
|
||||||
export default function getCustomerWishlistOperation({
|
export default function getCustomerWishlistOperation({
|
||||||
commerce,
|
commerce,
|
||||||
@ -47,6 +49,12 @@ export default function getCustomerWishlistOperation({
|
|||||||
|
|
||||||
const wishlist = data[0]
|
const wishlist = data[0]
|
||||||
|
|
||||||
|
if (!wishlist) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const items: WishlistItem[] = []
|
||||||
|
|
||||||
if (includeProducts && wishlist?.items?.length) {
|
if (includeProducts && wishlist?.items?.length) {
|
||||||
const ids = wishlist.items
|
const ids = wishlist.items
|
||||||
?.map((item) => (item?.productId ? String(item?.productId) : null))
|
?.map((item) => (item?.productId ? String(item?.productId) : null))
|
||||||
@ -57,25 +65,36 @@ export default function getCustomerWishlistOperation({
|
|||||||
variables: { first: 50, ids },
|
variables: { first: 50, ids },
|
||||||
config,
|
config,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Put the products in an object that we can use to get them by id
|
// Put the products in an object that we can use to get them by id
|
||||||
const productsById = graphqlData.products.reduce<{
|
const productsById = graphqlData.products.reduce<{
|
||||||
[k: number]: ProductEdge
|
[k: number]: Product
|
||||||
}>((prods, p) => {
|
}>((prods, p) => {
|
||||||
prods[Number(p.id)] = p as any
|
prods[Number(p.id)] = p as any
|
||||||
return prods
|
return prods
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
// Populate the wishlist items with the graphql products
|
// Populate the wishlist items with the graphql products
|
||||||
wishlist.items.forEach((item) => {
|
wishlist.items.forEach((item) => {
|
||||||
const product = item && productsById[Number(item.productId)]
|
const product = item && productsById[Number(item.productId)]
|
||||||
if (item && product) {
|
if (item && product) {
|
||||||
// @ts-ignore Fix this type when the wishlist type is properly defined
|
items.push({
|
||||||
item.product = product
|
id: String(item.id),
|
||||||
|
productId: String(item.productId),
|
||||||
|
variantId: String(item.variantId),
|
||||||
|
product,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { wishlist: wishlist as RecursiveRequired<typeof wishlist> }
|
return {
|
||||||
|
wishlist: {
|
||||||
|
id: String(wishlist.id),
|
||||||
|
items,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getCustomerWishlist
|
return getCustomerWishlist
|
||||||
|
@ -100,7 +100,7 @@ export default function getAllProductPathsOperation({
|
|||||||
const variables: GetProductQueryVariables = {
|
const variables: GetProductQueryVariables = {
|
||||||
locale,
|
locale,
|
||||||
hasLocale: !!locale,
|
hasLocale: !!locale,
|
||||||
path: slug ? `/${slug}` : vars.path!,
|
path: `/${slug}`,
|
||||||
}
|
}
|
||||||
const { data } = await config.fetch<GetProductQuery>(query, { variables })
|
const { data } = await config.fetch<GetProductQuery>(query, { variables })
|
||||||
const product = data.site?.route?.node
|
const product = data.site?.route?.node
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import type { Page } from '@vercel/commerce/types/page'
|
import type { Page } from '@vercel/commerce/types/page'
|
||||||
import type { Product } from '@vercel/commerce/types/product'
|
import type { Product } from '@vercel/commerce/types/product'
|
||||||
|
import type { CustomField } from '@vercel/commerce/types/common'
|
||||||
import type { Cart, LineItem } from '@vercel/commerce/types/cart'
|
import type { Cart, LineItem } from '@vercel/commerce/types/cart'
|
||||||
import type { Category, Brand } from '@vercel/commerce/types/site'
|
import type { Category, Brand } from '@vercel/commerce/types/site'
|
||||||
import type { BigcommerceCart, BCCategory, BCBrand } from '../types'
|
import type { BigcommerceCart, BCCategory, BCBrand } from '../types'
|
||||||
import type { ProductNode } from '../api/operations/get-all-products'
|
import type { ProductNode } from '../api/operations/get-all-products'
|
||||||
import type { definitions } from '../api/definitions/store-content'
|
import type { definitions } from '../api/definitions/store-content'
|
||||||
|
import type { CustomFieldEdge } from '../../schema'
|
||||||
|
|
||||||
import getSlug from './get-slug'
|
import getSlug from './get-slug'
|
||||||
|
|
||||||
@ -20,10 +22,20 @@ function normalizeProductOption(productOption: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeProduct(productNode: ProductNode): Product {
|
// TODO: change this after schema definition is updated
|
||||||
|
interface ProductNodeWithCustomFields extends ProductNode {
|
||||||
|
customFields: {
|
||||||
|
edges?: CustomFieldEdge[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeProduct(
|
||||||
|
productNode: ProductNodeWithCustomFields
|
||||||
|
): Product {
|
||||||
const {
|
const {
|
||||||
entityId: id,
|
entityId: id,
|
||||||
productOptions,
|
productOptions,
|
||||||
|
customFields,
|
||||||
prices,
|
prices,
|
||||||
path,
|
path,
|
||||||
images,
|
images,
|
||||||
@ -52,6 +64,7 @@ export function normalizeProduct(productNode: ProductNode): Product {
|
|||||||
})
|
})
|
||||||
) || [],
|
) || [],
|
||||||
options: productOptions?.edges?.map(normalizeProductOption) || [],
|
options: productOptions?.edges?.map(normalizeProductOption) || [],
|
||||||
|
customFields: customFields?.edges?.map(normalizeCustomFieldsValue) || [],
|
||||||
slug: path?.replace(/^\/+|\/+$/g, ''),
|
slug: path?.replace(/^\/+|\/+$/g, ''),
|
||||||
price: {
|
price: {
|
||||||
value: prices?.price.value,
|
value: prices?.price.value,
|
||||||
@ -137,3 +150,15 @@ export function normalizeBrand(brand: BCBrand): Brand {
|
|||||||
path: `/${slug}`,
|
path: `/${slug}`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeCustomFieldsValue(field: CustomFieldEdge): CustomField {
|
||||||
|
const {
|
||||||
|
node: { entityId, name, value },
|
||||||
|
} = field
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: String(entityId),
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -34,3 +34,67 @@ export interface Image {
|
|||||||
*/
|
*/
|
||||||
height?: number
|
height?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CustomField {
|
||||||
|
/**
|
||||||
|
* The unique identifier for the custom field.
|
||||||
|
*/
|
||||||
|
id: string
|
||||||
|
/**
|
||||||
|
* The name of the custom field.
|
||||||
|
*/
|
||||||
|
name: string
|
||||||
|
/**
|
||||||
|
* The value of the custom field.
|
||||||
|
*/
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Metafield {
|
||||||
|
/**
|
||||||
|
* The unique identifier for the metafield.
|
||||||
|
*/
|
||||||
|
key: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The namespace for the metafield, which is a container for a set of metadata.
|
||||||
|
* @example `rating`
|
||||||
|
*/
|
||||||
|
namespace: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value of the metafield, usually a string that can be might parsed into JSON.
|
||||||
|
* @example `{"value": 5, "scale_max": 5}`
|
||||||
|
*/
|
||||||
|
value: any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value of the metafield, complete with HTML formatting.
|
||||||
|
*/
|
||||||
|
valueHtml?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the metafield, used to determine how the value should be interpreted.
|
||||||
|
* For example: `string`, `integer`, `boolean`, `json` ...
|
||||||
|
*/
|
||||||
|
type?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the metafield, that can be used as a label.
|
||||||
|
*/
|
||||||
|
name?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Metafields {
|
||||||
|
/**
|
||||||
|
* The namespace for the metafield, which is a container for a set of metadata.
|
||||||
|
* @example `reviews`, `specifications`
|
||||||
|
*/
|
||||||
|
[namespace: string]: {
|
||||||
|
/**
|
||||||
|
* The key of the metafield, which is the name of the metafield.
|
||||||
|
* @example `rating`
|
||||||
|
*/
|
||||||
|
[key: string]: Metafield
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Image } from './common'
|
import { CustomField, Image, Metafields } from './common'
|
||||||
|
|
||||||
export interface ProductPrice {
|
export interface ProductPrice {
|
||||||
/**
|
/**
|
||||||
@ -126,6 +126,33 @@ export interface Product {
|
|||||||
* List of images associated with the product.
|
* List of images associated with the product.
|
||||||
*/
|
*/
|
||||||
images: Image[]
|
images: Image[]
|
||||||
|
/**
|
||||||
|
* List of custom fields / properties associated with the product.
|
||||||
|
* @example
|
||||||
|
* customFields: [{
|
||||||
|
* id: '1',
|
||||||
|
* name: 'Warehouse Location',
|
||||||
|
* value: 'Aisle 3, Shelf 5, Bin 6'
|
||||||
|
* }]
|
||||||
|
*/
|
||||||
|
customFields?: CustomField[]
|
||||||
|
/**
|
||||||
|
* Advanced custom fields that can be added to a product. They are used to store additional information about the product, in a structured format, grouped by namespaces.
|
||||||
|
* @example
|
||||||
|
* {
|
||||||
|
* // Namespace, the container for a set of metadata
|
||||||
|
* reviews: {
|
||||||
|
* // Key of the metafield
|
||||||
|
* rating: {
|
||||||
|
* key: 'rating',
|
||||||
|
* value: 4,
|
||||||
|
* valueHtml: '★★★★☆',
|
||||||
|
* type: 'integer',
|
||||||
|
* name: 'Rating',
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
metafields?: Metafields
|
||||||
/**
|
/**
|
||||||
* List of the product’s variants.
|
* List of the product’s variants.
|
||||||
*/
|
*/
|
||||||
@ -204,7 +231,6 @@ export type ProductsSchema = {
|
|||||||
/**
|
/**
|
||||||
* Product operations
|
* Product operations
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type GetAllProductPathsOperation = {
|
export type GetAllProductPathsOperation = {
|
||||||
data: { products: Pick<Product, 'path'>[] }
|
data: { products: Pick<Product, 'path'>[] }
|
||||||
variables: { first?: number }
|
variables: { first?: number }
|
||||||
@ -219,7 +245,24 @@ export type GetAllProductsOperation = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MetafieldsIdentifiers = Array<{
|
||||||
|
namespace: string
|
||||||
|
key: string
|
||||||
|
}>
|
||||||
|
|
||||||
export type GetProductOperation = {
|
export type GetProductOperation = {
|
||||||
data: { product?: Product }
|
data: { product?: Product }
|
||||||
variables: { path: string; slug?: never } | { path?: never; slug: string }
|
variables: {
|
||||||
|
slug: string
|
||||||
|
/**
|
||||||
|
* Metafields identifiers used to fetch the product metafields, represented as an array of objects with the namespace and key.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* withMetafields: [
|
||||||
|
* {namespace: 'reviews', key: 'rating'},
|
||||||
|
* {namespace: 'reviews', key: 'count'},
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
withMetafields?: MetafieldsIdentifiers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,24 @@
|
|||||||
import type { OperationContext } from '@vercel/commerce/api/operations'
|
import type { OperationContext } from '@vercel/commerce/api/operations'
|
||||||
import { normalizeProduct } from '../../utils'
|
import type { GetProductOperation } from '@vercel/commerce/types/product'
|
||||||
import type { Provider, SaleorConfig } from '..'
|
import type { Provider, SaleorConfig } from '..'
|
||||||
|
|
||||||
|
import { normalizeProduct } from '../../utils'
|
||||||
|
|
||||||
import * as Query from '../../utils/queries'
|
import * as Query from '../../utils/queries'
|
||||||
|
|
||||||
type Variables = {
|
|
||||||
slug: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReturnType = {
|
|
||||||
product: any
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function getProductOperation({
|
export default function getProductOperation({
|
||||||
commerce,
|
commerce,
|
||||||
}: OperationContext<Provider>) {
|
}: OperationContext<Provider>) {
|
||||||
async function getProduct({
|
async function getProduct<T extends GetProductOperation>({
|
||||||
query = Query.ProductOneBySlug,
|
query = Query.ProductOneBySlug,
|
||||||
variables,
|
variables,
|
||||||
config: cfg,
|
config: cfg,
|
||||||
}: {
|
}: {
|
||||||
query?: string
|
query?: string
|
||||||
variables: Variables
|
variables: T['variables']
|
||||||
config?: Partial<SaleorConfig>
|
config?: Partial<SaleorConfig>
|
||||||
preview?: boolean
|
preview?: boolean
|
||||||
}): Promise<ReturnType> {
|
}): Promise<T['data']> {
|
||||||
const { fetch, locale } = commerce.getConfig(cfg)
|
const { fetch, locale } = commerce.getConfig(cfg)
|
||||||
|
|
||||||
const { data } = await fetch(
|
const { data } = await fetch(
|
||||||
@ -37,9 +31,9 @@ export default function getProductOperation({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return data && data.product
|
||||||
product: data && data.product ? normalizeProduct(data.product) : null,
|
? { product: normalizeProduct(data.product) }
|
||||||
}
|
: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getProduct
|
return getProduct
|
||||||
|
@ -55,7 +55,7 @@ export default function getProductOperation({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...(productByHandle && {
|
...(productByHandle && {
|
||||||
product: normalizeProduct(productByHandle as ShopifyProduct),
|
product: normalizeProduct(productByHandle as ShopifyProduct, locale),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,14 +30,7 @@ const fetchGraphqlApi: GraphQLFetcher = async (
|
|||||||
|
|
||||||
return { data, res }
|
return { data, res }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw getError(
|
throw getError([err], 500)
|
||||||
[
|
|
||||||
{
|
|
||||||
message: `${err} \n Most likely related to an unexpected output. E.g: NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN & NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN might be incorect.`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
500
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default fetchGraphqlApi
|
export default fetchGraphqlApi
|
||||||
|
39
packages/shopify/src/types/metafields.ts
Normal file
39
packages/shopify/src/types/metafields.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
type LiteralUnion<T extends string> = T | Omit<T, T>
|
||||||
|
|
||||||
|
export type MetafieldTypes =
|
||||||
|
| 'integer'
|
||||||
|
| 'boolean'
|
||||||
|
| 'color'
|
||||||
|
| 'json'
|
||||||
|
| 'date'
|
||||||
|
| 'file_reference'
|
||||||
|
| 'date_time'
|
||||||
|
| 'dimension'
|
||||||
|
| 'multi_line_text_field'
|
||||||
|
| 'number_decimal'
|
||||||
|
| 'number_integer'
|
||||||
|
| 'page_reference'
|
||||||
|
| 'product_reference'
|
||||||
|
| 'rating'
|
||||||
|
| 'single_line_text_field'
|
||||||
|
| 'url'
|
||||||
|
| 'variant_reference'
|
||||||
|
| 'volume'
|
||||||
|
| 'weight'
|
||||||
|
| 'list.color'
|
||||||
|
| 'list.date'
|
||||||
|
| 'list.date_time'
|
||||||
|
| 'list.dimension'
|
||||||
|
| 'list.file_reference'
|
||||||
|
| 'list.number_integer'
|
||||||
|
| 'list.number_decimal'
|
||||||
|
| 'list.page_reference'
|
||||||
|
| 'list.product_reference'
|
||||||
|
| 'list.rating'
|
||||||
|
| 'list.single_line_text_field'
|
||||||
|
| 'list.url'
|
||||||
|
| 'list.variant_reference'
|
||||||
|
| 'list.volume'
|
||||||
|
| 'list.weight'
|
||||||
|
|
||||||
|
export type MetafieldType = LiteralUnion<MetafieldTypes>
|
@ -1,7 +1,12 @@
|
|||||||
import { FetcherError } from '@vercel/commerce/utils/errors'
|
import { FetcherError } from '@vercel/commerce/utils/errors'
|
||||||
|
|
||||||
export function getError(errors: any[] | null, status: number) {
|
export function getError(errors: any[] | null, status: number) {
|
||||||
errors = errors ?? [{ message: 'Failed to fetch Shopify API' }]
|
errors = errors ?? [
|
||||||
|
{
|
||||||
|
message:
|
||||||
|
'Failed to fetch Shopify API, most likely related to an unexpected output. E.g: NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN & NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN might be incorect',
|
||||||
|
},
|
||||||
|
]
|
||||||
return new FetcherError({ errors, status })
|
return new FetcherError({ errors, status })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
118
packages/shopify/src/utils/metafields.ts
Normal file
118
packages/shopify/src/utils/metafields.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import type { MetafieldType } from '../types/metafields'
|
||||||
|
|
||||||
|
export const parseJson = (value: string): any => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const unitConversion: Record<string, string> = {
|
||||||
|
mm: 'millimeter',
|
||||||
|
cm: 'centimeter',
|
||||||
|
m: 'meter',
|
||||||
|
in: 'inch',
|
||||||
|
ft: 'foot',
|
||||||
|
yd: 'yard',
|
||||||
|
|
||||||
|
ml: 'milliliter',
|
||||||
|
l: 'liter',
|
||||||
|
us_fl_oz: 'fluid-ounce',
|
||||||
|
us_gal: 'gallon',
|
||||||
|
|
||||||
|
kg: 'kilogram',
|
||||||
|
g: 'gram',
|
||||||
|
lb: 'pound',
|
||||||
|
oz: 'ounce',
|
||||||
|
|
||||||
|
MILLIMETERS: 'millimeter',
|
||||||
|
CENTIMETERS: 'centimeter',
|
||||||
|
METERS: 'meter',
|
||||||
|
|
||||||
|
MILLILITERS: 'milliliter',
|
||||||
|
LITERS: 'liter',
|
||||||
|
FLUID_OUNCES: 'fluid-ounce',
|
||||||
|
IMPERIAL_FLUID_OUNCES: 'fluid-ounce',
|
||||||
|
GALLONS: 'gallon',
|
||||||
|
|
||||||
|
KILOGRAMS: 'kilogram',
|
||||||
|
GRAMS: 'gram',
|
||||||
|
OUNCES: 'ounce',
|
||||||
|
POUNDS: 'pound',
|
||||||
|
|
||||||
|
FEET: 'foot',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMeasurment = (input: string, locale: string = 'en-US') => {
|
||||||
|
try {
|
||||||
|
let { unit, value } = JSON.parse(input)
|
||||||
|
return new Intl.NumberFormat(locale, {
|
||||||
|
unit: unitConversion[unit],
|
||||||
|
style: 'unit',
|
||||||
|
}).format(parseFloat(value))
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMetafieldValue = (
|
||||||
|
type: MetafieldType,
|
||||||
|
value: string,
|
||||||
|
locale: string = 'en-US'
|
||||||
|
) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'boolean':
|
||||||
|
return value === 'true' ? '✓' : '✕'
|
||||||
|
case 'number_integer':
|
||||||
|
return parseInt(value).toLocaleString(locale)
|
||||||
|
case 'number_decimal':
|
||||||
|
return parseFloat(value).toLocaleString(locale)
|
||||||
|
case 'date':
|
||||||
|
return Intl.DateTimeFormat(locale, {
|
||||||
|
dateStyle: 'medium',
|
||||||
|
}).format(new Date(value))
|
||||||
|
case 'date_time':
|
||||||
|
return Intl.DateTimeFormat(locale, {
|
||||||
|
dateStyle: 'medium',
|
||||||
|
timeStyle: 'long',
|
||||||
|
}).format(new Date(value))
|
||||||
|
case 'dimension':
|
||||||
|
case 'volume':
|
||||||
|
case 'weight':
|
||||||
|
return getMeasurment(value, locale)
|
||||||
|
case 'rating':
|
||||||
|
const { scale_max: length, value: val } = JSON.parse(value)
|
||||||
|
return Array.from({ length }, (_, i) =>
|
||||||
|
i <= val - 1 ? '★' : '☆'
|
||||||
|
).join('')
|
||||||
|
case 'color':
|
||||||
|
return `<figure style="background-color: ${value}; width: 1rem; height:1rem; display:block; margin-top: 2px; border-radius: 100%;"/>`
|
||||||
|
case 'url':
|
||||||
|
return `<a href="${value}" target="_blank" rel="norreferrer">${value}</a>`
|
||||||
|
case 'multi_line_text_field':
|
||||||
|
return value
|
||||||
|
.split('\n')
|
||||||
|
.map((line) => `${line}<br/>`)
|
||||||
|
.join('')
|
||||||
|
case 'json':
|
||||||
|
case 'single_line_text_field':
|
||||||
|
case 'product_reference':
|
||||||
|
case 'page_reference':
|
||||||
|
case 'variant_reference':
|
||||||
|
case 'file_reference':
|
||||||
|
default:
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toLabel = (string: string) =>
|
||||||
|
string
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[_-]+/g, ' ')
|
||||||
|
.replace(/\s{2,}/g, ' ')
|
||||||
|
.trim()
|
||||||
|
.split(' ')
|
||||||
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
|
.join(' ')
|
@ -1,7 +1,9 @@
|
|||||||
import type { Page } from '@vercel/commerce/types/page'
|
import type { Page } from '@vercel/commerce/types/page'
|
||||||
import type { Product } from '@vercel/commerce/types/product'
|
import type { Product } from '@vercel/commerce/types/product'
|
||||||
|
import type { Metafield } from '@vercel/commerce/types/common'
|
||||||
import type { Cart, LineItem } from '@vercel/commerce/types/cart'
|
import type { Cart, LineItem } from '@vercel/commerce/types/cart'
|
||||||
import type { Category } from '@vercel/commerce/types/site'
|
import type { Category } from '@vercel/commerce/types/site'
|
||||||
|
import type { MetafieldType } from '../types/metafields'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Product as ShopifyProduct,
|
Product as ShopifyProduct,
|
||||||
@ -15,14 +17,12 @@ import type {
|
|||||||
Page as ShopifyPage,
|
Page as ShopifyPage,
|
||||||
PageEdge,
|
PageEdge,
|
||||||
Collection,
|
Collection,
|
||||||
MetafieldConnection,
|
|
||||||
MediaConnection,
|
|
||||||
Model3d,
|
|
||||||
Metafield,
|
|
||||||
Maybe,
|
Maybe,
|
||||||
|
Metafield as ShopifyMetafield,
|
||||||
} from '../../schema'
|
} from '../../schema'
|
||||||
|
|
||||||
import { colorMap } from './colors'
|
import { colorMap } from './colors'
|
||||||
|
import { getMetafieldValue, toLabel, parseJson } from './metafields'
|
||||||
|
|
||||||
const money = ({ amount, currencyCode }: MoneyV2) => {
|
const money = ({ amount, currencyCode }: MoneyV2) => {
|
||||||
return {
|
return {
|
||||||
@ -92,7 +92,6 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
|
|||||||
name,
|
name,
|
||||||
values: [value],
|
values: [value],
|
||||||
})
|
})
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -100,22 +99,8 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeProductMedia = ({ edges }: MediaConnection) => {
|
export function normalizeProduct(
|
||||||
return edges
|
{
|
||||||
.filter(({ node }) => Object.keys(node).length !== 0)
|
|
||||||
.map(({ node }) => {
|
|
||||||
return {
|
|
||||||
sources: (node as Model3d).sources.map(({ format, url }) => {
|
|
||||||
return {
|
|
||||||
format: format,
|
|
||||||
url: url,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeProduct({
|
|
||||||
id,
|
id,
|
||||||
title: name,
|
title: name,
|
||||||
vendor,
|
vendor,
|
||||||
@ -128,7 +113,9 @@ export function normalizeProduct({
|
|||||||
options,
|
options,
|
||||||
metafields,
|
metafields,
|
||||||
...rest
|
...rest
|
||||||
}: ShopifyProduct): Product {
|
}: ShopifyProduct,
|
||||||
|
locale?: string
|
||||||
|
): Product {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
@ -143,12 +130,48 @@ export function normalizeProduct({
|
|||||||
.filter((o) => o.name !== 'Title') // By default Shopify adds a 'Title' name when there's only one option. We don't need it. https://community.shopify.com/c/Shopify-APIs-SDKs/Adding-new-product-variant-is-automatically-adding-quot-Default/td-p/358095
|
.filter((o) => o.name !== 'Title') // By default Shopify adds a 'Title' name when there's only one option. We don't need it. https://community.shopify.com/c/Shopify-APIs-SDKs/Adding-new-product-variant-is-automatically-adding-quot-Default/td-p/358095
|
||||||
.map((o) => normalizeProductOption(o))
|
.map((o) => normalizeProductOption(o))
|
||||||
: [],
|
: [],
|
||||||
|
metafields: normalizeMetafields(metafields, locale),
|
||||||
description: description || '',
|
description: description || '',
|
||||||
...(descriptionHtml && { descriptionHtml }),
|
...(descriptionHtml && { descriptionHtml }),
|
||||||
...rest,
|
...rest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeMetafields(
|
||||||
|
metafields: Maybe<ShopifyMetafield>[],
|
||||||
|
locale?: string
|
||||||
|
) {
|
||||||
|
const output: Record<string, Record<string, Metafield>> = {}
|
||||||
|
|
||||||
|
if (!metafields) return output
|
||||||
|
|
||||||
|
for (const metafield of metafields) {
|
||||||
|
if (!metafield) continue
|
||||||
|
|
||||||
|
const { key, type, namespace, value, ...rest } = metafield
|
||||||
|
|
||||||
|
const newField = {
|
||||||
|
...rest,
|
||||||
|
key,
|
||||||
|
name: toLabel(key),
|
||||||
|
type,
|
||||||
|
namespace,
|
||||||
|
value,
|
||||||
|
valueHtml: getMetafieldValue(type, value, locale),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!output[namespace]) {
|
||||||
|
output[namespace] = {
|
||||||
|
[key]: newField,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output[namespace][key] = newField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeCart(checkout: Checkout): Cart {
|
export function normalizeCart(checkout: Checkout): Cart {
|
||||||
return {
|
return {
|
||||||
id: checkout.id,
|
id: checkout.id,
|
||||||
@ -217,3 +240,19 @@ export const normalizeCategory = ({
|
|||||||
slug: handle,
|
slug: handle,
|
||||||
path: `/${handle}`,
|
path: `/${handle}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const normalizeMetafieldValue = (
|
||||||
|
type: MetafieldType,
|
||||||
|
value: string,
|
||||||
|
locale?: string
|
||||||
|
) => {
|
||||||
|
if (type.startsWith('list.')) {
|
||||||
|
const arr = parseJson(value)
|
||||||
|
return Array.isArray(arr)
|
||||||
|
? arr
|
||||||
|
.map((v) => getMetafieldValue(type.split('.')[1], v, locale))
|
||||||
|
.join(' • ')
|
||||||
|
: value
|
||||||
|
}
|
||||||
|
return getMetafieldValue(type, value, locale)
|
||||||
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
const getProductQuery = /* GraphQL */ `
|
const getProductQuery = /* GraphQL */ `
|
||||||
query getProductBySlug($slug: String!) {
|
query getProductBySlug(
|
||||||
|
$slug: String!
|
||||||
|
$withMetafields: [HasMetafieldsIdentifier!] = []
|
||||||
|
) {
|
||||||
productByHandle(handle: $slug) {
|
productByHandle(handle: $slug) {
|
||||||
id
|
id
|
||||||
handle
|
handle
|
||||||
@ -24,15 +27,7 @@ const getProductQuery = /* GraphQL */ `
|
|||||||
currencyCode
|
currencyCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
metafields(first: 30) {
|
variants(first: 25) {
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
key
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
variants(first: 250) {
|
|
||||||
pageInfo {
|
pageInfo {
|
||||||
hasNextPage
|
hasNextPage
|
||||||
hasPreviousPage
|
hasPreviousPage
|
||||||
@ -59,7 +54,7 @@ const getProductQuery = /* GraphQL */ `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
images(first: 250) {
|
images(first: 25) {
|
||||||
pageInfo {
|
pageInfo {
|
||||||
hasNextPage
|
hasNextPage
|
||||||
hasPreviousPage
|
hasPreviousPage
|
||||||
@ -88,6 +83,12 @@ const getProductQuery = /* GraphQL */ `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
metafields(identifiers: $withMetafields) {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
namespace
|
||||||
|
description
|
||||||
|
type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import type { GetProductOperation } from '@vercel/commerce/types/product'
|
||||||
|
|
||||||
import { normalizeProduct } from '../../utils'
|
import { normalizeProduct } from '../../utils'
|
||||||
|
|
||||||
import { Product } from '@vercel/commerce/types/product'
|
import { Product } from '@vercel/commerce/types/product'
|
||||||
@ -7,15 +9,15 @@ import { Provider, SwellConfig } from '../'
|
|||||||
export default function getProductOperation({
|
export default function getProductOperation({
|
||||||
commerce,
|
commerce,
|
||||||
}: OperationContext<Provider>) {
|
}: OperationContext<Provider>) {
|
||||||
async function getProduct({
|
async function getProduct<T extends GetProductOperation>({
|
||||||
variables,
|
variables,
|
||||||
config: cfg,
|
config: cfg,
|
||||||
}: {
|
}: {
|
||||||
query?: string
|
query?: string
|
||||||
variables: { slug: string }
|
variables: T['variables']
|
||||||
config?: Partial<SwellConfig>
|
config?: Partial<SwellConfig>
|
||||||
preview?: boolean
|
preview?: boolean
|
||||||
}): Promise<Product | {} | any> {
|
}): Promise<T['data']> {
|
||||||
const config = commerce.getConfig(cfg)
|
const config = commerce.getConfig(cfg)
|
||||||
|
|
||||||
const product = await config.fetch('products', 'get', [variables.slug])
|
const product = await config.fetch('products', 'get', [variables.slug])
|
||||||
@ -24,9 +26,7 @@ export default function getProductOperation({
|
|||||||
product.variants = product.variants?.results
|
product.variants = product.variants?.results
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return product ? { product: normalizeProduct(product) } : {}
|
||||||
product: product ? normalizeProduct(product) : null,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return getProduct
|
return getProduct
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
import { Product } from '@vercel/commerce/types/product'
|
import type {
|
||||||
import { OperationContext } from '@vercel/commerce/api/operations'
|
Product,
|
||||||
import { Provider, VendureConfig } from '../'
|
GetProductOperation,
|
||||||
import { GetProductQuery } from '../../../schema'
|
} from '@vercel/commerce/types/product'
|
||||||
|
import type { OperationContext } from '@vercel/commerce/api/operations'
|
||||||
|
import type { Provider, VendureConfig } from '../'
|
||||||
|
|
||||||
|
import type { GetProductQuery } from '../../../schema'
|
||||||
import { getProductQuery } from '../../utils/queries/get-product-query'
|
import { getProductQuery } from '../../utils/queries/get-product-query'
|
||||||
|
|
||||||
export default function getProductOperation({
|
export default function getProductOperation({
|
||||||
commerce,
|
commerce,
|
||||||
}: OperationContext<Provider>) {
|
}: OperationContext<Provider>) {
|
||||||
async function getProduct({
|
async function getProduct<T extends GetProductOperation>({
|
||||||
query = getProductQuery,
|
query = getProductQuery,
|
||||||
variables,
|
variables,
|
||||||
config: cfg,
|
config: cfg,
|
||||||
}: {
|
}: {
|
||||||
query?: string
|
query?: string
|
||||||
variables: { slug: string }
|
variables: T['variables']
|
||||||
config?: Partial<VendureConfig>
|
config?: Partial<VendureConfig>
|
||||||
preview?: boolean
|
preview?: boolean
|
||||||
}): Promise<Product | {} | any> {
|
}): Promise<T['data']> {
|
||||||
const config = commerce.getConfig(cfg)
|
const config = commerce.getConfig(cfg)
|
||||||
|
|
||||||
const locale = config.locale
|
const locale = config.locale
|
||||||
|
226
pnpm-lock.yaml
generated
226
pnpm-lock.yaml
generated
@ -15,6 +15,7 @@ importers:
|
|||||||
packages/bigcommerce:
|
packages/bigcommerce:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@cfworker/uuid': ^1.12.4
|
'@cfworker/uuid': ^1.12.4
|
||||||
|
'@manifoldco/swagger-to-ts': ^2.1.0
|
||||||
'@taskr/clear': ^1.1.0
|
'@taskr/clear': ^1.1.0
|
||||||
'@taskr/esnext': ^1.1.0
|
'@taskr/esnext': ^1.1.0
|
||||||
'@taskr/watch': ^1.1.0
|
'@taskr/watch': ^1.1.0
|
||||||
@ -33,6 +34,7 @@ importers:
|
|||||||
lint-staged: ^12.1.7
|
lint-staged: ^12.1.7
|
||||||
lodash.debounce: ^4.0.8
|
lodash.debounce: ^4.0.8
|
||||||
next: ^12.0.8
|
next: ^12.0.8
|
||||||
|
node-fetch: ^2.6.7
|
||||||
prettier: ^2.5.1
|
prettier: ^2.5.1
|
||||||
react: ^18.2.0
|
react: ^18.2.0
|
||||||
react-dom: ^18.2.0
|
react-dom: ^18.2.0
|
||||||
@ -51,6 +53,7 @@ importers:
|
|||||||
lodash.debounce: 4.0.8
|
lodash.debounce: 4.0.8
|
||||||
uuidv4: 6.2.13
|
uuidv4: 6.2.13
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@manifoldco/swagger-to-ts': 2.1.0
|
||||||
'@taskr/clear': 1.1.0
|
'@taskr/clear': 1.1.0
|
||||||
'@taskr/esnext': 1.1.0
|
'@taskr/esnext': 1.1.0
|
||||||
'@taskr/watch': 1.1.0
|
'@taskr/watch': 1.1.0
|
||||||
@ -62,6 +65,7 @@ importers:
|
|||||||
'@types/react': 18.0.20
|
'@types/react': 18.0.20
|
||||||
lint-staged: 12.5.0
|
lint-staged: 12.5.0
|
||||||
next: 12.3.0_biqbaboplfbrettd7655fr4n2y
|
next: 12.3.0_biqbaboplfbrettd7655fr4n2y
|
||||||
|
node-fetch: 2.6.7
|
||||||
prettier: 2.7.1
|
prettier: 2.7.1
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
@ -3493,6 +3497,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
/@motionone/animation/10.15.1:
|
/@motionone/animation/10.15.1:
|
||||||
resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==}
|
resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3539,6 +3544,19 @@ packages:
|
|||||||
hey-listen: 1.0.8
|
hey-listen: 1.0.8
|
||||||
tslib: 2.4.0
|
tslib: 2.4.0
|
||||||
dev: false
|
dev: false
|
||||||
|
=======
|
||||||
|
/@manifoldco/swagger-to-ts/2.1.0:
|
||||||
|
resolution: {integrity: sha512-IH0FAHhwWHR3Gs3rnVHNEscZujGn+K6/2Zu5cWfZre3Vz2tx1SvvJKEbSM89MztfDDRjOpb+6pQD/vqdEoTBVg==}
|
||||||
|
engines: {node: '>= 10.0.0'}
|
||||||
|
deprecated: This package has changed to openapi-typescript
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
js-yaml: 3.14.1
|
||||||
|
meow: 7.1.1
|
||||||
|
prettier: 2.7.1
|
||||||
|
dev: true
|
||||||
|
>>>>>>> 1ba9d3bd6e79da1f0b05df2f1c0c1ca3632b6c16
|
||||||
|
|
||||||
/@next/bundle-analyzer/12.3.0:
|
/@next/bundle-analyzer/12.3.0:
|
||||||
resolution: {integrity: sha512-hzRLHIrtwOiGEku9rmG7qZk+OQhnqQOL+ycl2XrjBaztBN/xaqnjoG4+HEf9L7ELN943BR+K/ZlaF2OEgbGm+Q==}
|
resolution: {integrity: sha512-hzRLHIrtwOiGEku9rmG7qZk+OQhnqQOL+ycl2XrjBaztBN/xaqnjoG4+HEf9L7ELN943BR+K/ZlaF2OEgbGm+Q==}
|
||||||
@ -4431,6 +4449,10 @@ packages:
|
|||||||
/@types/lodash/4.14.185:
|
/@types/lodash/4.14.185:
|
||||||
resolution: {integrity: sha512-evMDG1bC4rgQg4ku9tKpuMh5iBNEwNa3tf9zRHdP1qlv+1WUg44xat4IxCE14gIpZRGUUWAx2VhItCZc25NfMA==}
|
resolution: {integrity: sha512-evMDG1bC4rgQg4ku9tKpuMh5iBNEwNa3tf9zRHdP1qlv+1WUg44xat4IxCE14gIpZRGUUWAx2VhItCZc25NfMA==}
|
||||||
|
|
||||||
|
/@types/minimist/1.2.2:
|
||||||
|
resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/node-fetch/2.6.2:
|
/@types/node-fetch/2.6.2:
|
||||||
resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==}
|
resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -4446,6 +4468,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==}
|
resolution: {integrity: sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/normalize-package-data/2.4.1:
|
||||||
|
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/parse-json/4.0.0:
|
/@types/parse-json/4.0.0:
|
||||||
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
|
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
|
||||||
|
|
||||||
@ -4768,6 +4794,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/argparse/1.0.10:
|
||||||
|
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||||
|
dependencies:
|
||||||
|
sprintf-js: 1.0.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/argparse/2.0.1:
|
/argparse/2.0.1:
|
||||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -4870,6 +4902,11 @@ packages:
|
|||||||
es-shim-unscopables: 1.0.0
|
es-shim-unscopables: 1.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/arrify/1.0.1:
|
||||||
|
resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/asap/2.0.6:
|
/asap/2.0.6:
|
||||||
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
|
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -5204,6 +5241,15 @@ packages:
|
|||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/camelcase-keys/6.2.2:
|
||||||
|
resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
camelcase: 5.3.1
|
||||||
|
map-obj: 4.3.0
|
||||||
|
quick-lru: 4.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/camelcase/5.3.1:
|
/camelcase/5.3.1:
|
||||||
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
|
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@ -5728,6 +5774,14 @@ packages:
|
|||||||
supports-color: 9.2.3
|
supports-color: 9.2.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/decamelize-keys/1.1.1:
|
||||||
|
resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dependencies:
|
||||||
|
decamelize: 1.2.0
|
||||||
|
map-obj: 1.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/decamelize/1.2.0:
|
/decamelize/1.2.0:
|
||||||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -6285,6 +6339,12 @@ packages:
|
|||||||
eslint-visitor-keys: 3.3.0
|
eslint-visitor-keys: 3.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/esprima/4.0.1:
|
||||||
|
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
/esquery/1.4.0:
|
/esquery/1.4.0:
|
||||||
resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
|
resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
|
||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
@ -6971,6 +7031,11 @@ packages:
|
|||||||
duplexer: 0.1.2
|
duplexer: 0.1.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/hard-rejection/2.1.0:
|
||||||
|
resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/has-ansi/2.0.0:
|
/has-ansi/2.0.0:
|
||||||
resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==}
|
resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -7052,6 +7117,7 @@ packages:
|
|||||||
tslib: 2.4.0
|
tslib: 2.4.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
/hey-listen/1.0.8:
|
/hey-listen/1.0.8:
|
||||||
resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==}
|
resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -7061,6 +7127,11 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react-is: 16.13.1
|
react-is: 16.13.1
|
||||||
dev: false
|
dev: false
|
||||||
|
=======
|
||||||
|
/hosted-git-info/2.8.9:
|
||||||
|
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
|
||||||
|
dev: true
|
||||||
|
>>>>>>> 1ba9d3bd6e79da1f0b05df2f1c0c1ca3632b6c16
|
||||||
|
|
||||||
/http-cache-semantics/4.1.0:
|
/http-cache-semantics/4.1.0:
|
||||||
resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==}
|
resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==}
|
||||||
@ -7512,6 +7583,11 @@ packages:
|
|||||||
symbol-observable: 1.2.0
|
symbol-observable: 1.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/is-plain-obj/1.1.0:
|
||||||
|
resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/is-plain-object/2.0.4:
|
/is-plain-object/2.0.4:
|
||||||
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
|
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -7660,6 +7736,14 @@ packages:
|
|||||||
/js-tokens/4.0.0:
|
/js-tokens/4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
|
/js-yaml/3.14.1:
|
||||||
|
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
argparse: 1.0.10
|
||||||
|
esprima: 4.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/js-yaml/4.1.0:
|
/js-yaml/4.1.0:
|
||||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@ -8154,6 +8238,16 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/map-obj/1.0.1:
|
||||||
|
resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/map-obj/4.3.0:
|
||||||
|
resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/map-visit/1.0.0:
|
/map-visit/1.0.0:
|
||||||
resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==}
|
resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -8172,6 +8266,23 @@ packages:
|
|||||||
resolution: {integrity: sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==}
|
resolution: {integrity: sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/meow/7.1.1:
|
||||||
|
resolution: {integrity: sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
'@types/minimist': 1.2.2
|
||||||
|
camelcase-keys: 6.2.2
|
||||||
|
decamelize-keys: 1.1.1
|
||||||
|
hard-rejection: 2.1.0
|
||||||
|
minimist-options: 4.1.0
|
||||||
|
normalize-package-data: 2.5.0
|
||||||
|
read-pkg-up: 7.0.1
|
||||||
|
redent: 3.0.0
|
||||||
|
trim-newlines: 3.0.1
|
||||||
|
type-fest: 0.13.1
|
||||||
|
yargs-parser: 18.1.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/merge-stream/2.0.0:
|
/merge-stream/2.0.0:
|
||||||
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -8283,6 +8394,11 @@ packages:
|
|||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/min-indent/1.0.1:
|
||||||
|
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/minimatch/3.1.2:
|
/minimatch/3.1.2:
|
||||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -8295,6 +8411,15 @@ packages:
|
|||||||
brace-expansion: 1.1.11
|
brace-expansion: 1.1.11
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/minimist-options/4.1.0:
|
||||||
|
resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
dependencies:
|
||||||
|
arrify: 1.0.1
|
||||||
|
is-plain-obj: 1.1.0
|
||||||
|
kind-of: 6.0.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/minimist/1.2.6:
|
/minimist/1.2.6:
|
||||||
resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
|
resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
|
||||||
|
|
||||||
@ -8513,6 +8638,15 @@ packages:
|
|||||||
/node-releases/2.0.6:
|
/node-releases/2.0.6:
|
||||||
resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
|
resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
|
||||||
|
|
||||||
|
/normalize-package-data/2.5.0:
|
||||||
|
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
||||||
|
dependencies:
|
||||||
|
hosted-git-info: 2.8.9
|
||||||
|
resolve: 1.22.1
|
||||||
|
semver: 5.7.1
|
||||||
|
validate-npm-package-license: 3.0.4
|
||||||
|
dev: true
|
||||||
|
|
||||||
/normalize-path/2.1.1:
|
/normalize-path/2.1.1:
|
||||||
resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==}
|
resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -9424,6 +9558,11 @@ packages:
|
|||||||
/queue-microtask/1.2.3:
|
/queue-microtask/1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
|
/quick-lru/4.0.1:
|
||||||
|
resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/quick-lru/5.1.1:
|
/quick-lru/5.1.1:
|
||||||
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
|
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -9628,6 +9767,25 @@ packages:
|
|||||||
pify: 2.3.0
|
pify: 2.3.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/read-pkg-up/7.0.1:
|
||||||
|
resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
find-up: 4.1.0
|
||||||
|
read-pkg: 5.2.0
|
||||||
|
type-fest: 0.8.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/read-pkg/5.2.0:
|
||||||
|
resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
'@types/normalize-package-data': 2.4.1
|
||||||
|
normalize-package-data: 2.5.0
|
||||||
|
parse-json: 5.2.0
|
||||||
|
type-fest: 0.6.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/readable-stream/2.3.7:
|
/readable-stream/2.3.7:
|
||||||
resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==}
|
resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -9666,6 +9824,14 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
|
|
||||||
|
/redent/3.0.0:
|
||||||
|
resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
indent-string: 4.0.0
|
||||||
|
strip-indent: 3.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/redis-commands/1.7.0:
|
/redis-commands/1.7.0:
|
||||||
resolution: {integrity: sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==}
|
resolution: {integrity: sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -10123,6 +10289,28 @@ packages:
|
|||||||
resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
|
resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
/spdx-correct/3.1.1:
|
||||||
|
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
|
||||||
|
dependencies:
|
||||||
|
spdx-expression-parse: 3.0.1
|
||||||
|
spdx-license-ids: 3.0.12
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/spdx-exceptions/2.3.0:
|
||||||
|
resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/spdx-expression-parse/3.0.1:
|
||||||
|
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
|
||||||
|
dependencies:
|
||||||
|
spdx-exceptions: 2.3.0
|
||||||
|
spdx-license-ids: 3.0.12
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/spdx-license-ids/3.0.12:
|
||||||
|
resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/split-string/3.1.0:
|
/split-string/3.1.0:
|
||||||
resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==}
|
resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -10136,6 +10324,10 @@ packages:
|
|||||||
tslib: 2.4.0
|
tslib: 2.4.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/sprintf-js/1.0.3:
|
||||||
|
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ssri/8.0.1:
|
/ssri/8.0.1:
|
||||||
resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==}
|
resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@ -10292,6 +10484,13 @@ packages:
|
|||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/strip-indent/3.0.0:
|
||||||
|
resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
min-indent: 1.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/strip-json-comments/2.0.1:
|
/strip-json-comments/2.0.1:
|
||||||
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
|
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -10529,6 +10728,11 @@ packages:
|
|||||||
/tr46/0.0.3:
|
/tr46/0.0.3:
|
||||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||||
|
|
||||||
|
/trim-newlines/3.0.1:
|
||||||
|
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ts-log/2.2.4:
|
/ts-log/2.2.4:
|
||||||
resolution: {integrity: sha512-DEQrfv6l7IvN2jlzc/VTdZJYsWUnQNCsueYjMkC/iXoEoi5fNan6MjeDqkvhfzbmHgdz9UxDUluX3V5HdjTydQ==}
|
resolution: {integrity: sha512-DEQrfv6l7IvN2jlzc/VTdZJYsWUnQNCsueYjMkC/iXoEoi5fNan6MjeDqkvhfzbmHgdz9UxDUluX3V5HdjTydQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -10764,6 +10968,11 @@ packages:
|
|||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/type-fest/0.13.1:
|
||||||
|
resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/type-fest/0.20.2:
|
/type-fest/0.20.2:
|
||||||
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -10774,6 +10983,16 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/type-fest/0.6.0:
|
||||||
|
resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/type-fest/0.8.1:
|
||||||
|
resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/typescript/4.7.4:
|
/typescript/4.7.4:
|
||||||
resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==}
|
resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==}
|
||||||
engines: {node: '>=4.2.0'}
|
engines: {node: '>=4.2.0'}
|
||||||
@ -10972,6 +11191,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/validate-npm-package-license/3.0.4:
|
||||||
|
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
||||||
|
dependencies:
|
||||||
|
spdx-correct: 3.1.1
|
||||||
|
spdx-expression-parse: 3.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/value-or-promise/1.0.11:
|
/value-or-promise/1.0.11:
|
||||||
resolution: {integrity: sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==}
|
resolution: {integrity: sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import type { CustomField } from '@commerce/types/common'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
customFields: CustomField[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProductCustomFields: React.FC<Props> = ({ customFields }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{customFields.map((field) => (
|
||||||
|
<div
|
||||||
|
key={field.id}
|
||||||
|
className="flex gap-2 border-b py-3 border-accent-2 border-dashed last:border-b-0"
|
||||||
|
>
|
||||||
|
<strong className="leading-7">{field.name}:</strong>
|
||||||
|
<span>{field.value}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProductCustomFields
|
1
site/components/product/ProductCustomFields/index.ts
Normal file
1
site/components/product/ProductCustomFields/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as ProductCustomFields } from './ProductCustomFields'
|
@ -0,0 +1,29 @@
|
|||||||
|
import type { FC } from 'react'
|
||||||
|
import type { Metafields } from '@commerce/types/common'
|
||||||
|
import Text from '@components/ui/Text'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
metafields: Metafields
|
||||||
|
/**
|
||||||
|
* The namespace of the metafields to display.
|
||||||
|
*/
|
||||||
|
namespace: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProductMetafields: FC<Props> = ({ metafields, namespace }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{Object.values(metafields[namespace] ?? {}).map((field) => (
|
||||||
|
<div
|
||||||
|
key={field.key}
|
||||||
|
className="flex gap-2 border-b py-3 border-accent-2 border-dashed last:border-b-0"
|
||||||
|
>
|
||||||
|
<strong className="leading-7">{field.name}:</strong>
|
||||||
|
<Text html={field.valueHtml || field.value} className="!mx-0" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProductMetafields
|
1
site/components/product/ProductMetafields/index.ts
Normal file
1
site/components/product/ProductMetafields/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as ProductMetafields } from './ProductMetafields'
|
@ -10,6 +10,8 @@ import {
|
|||||||
SelectedOptions,
|
SelectedOptions,
|
||||||
} from '../helpers'
|
} from '../helpers'
|
||||||
import ErrorMessage from '@components/ui/ErrorMessage'
|
import ErrorMessage from '@components/ui/ErrorMessage'
|
||||||
|
import { ProductCustomFields } from '../ProductCustomFields'
|
||||||
|
import { ProductMetafields } from '../ProductMetafields'
|
||||||
|
|
||||||
interface ProductSidebarProps {
|
interface ProductSidebarProps {
|
||||||
product: Product
|
product: Product
|
||||||
@ -62,10 +64,16 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
|||||||
className="pb-4 break-words w-full max-w-xl"
|
className="pb-4 break-words w-full max-w-xl"
|
||||||
html={product.descriptionHtml || product.description}
|
html={product.descriptionHtml || product.description}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{product.metafields?.reviews?.rating && (
|
||||||
<div className="flex flex-row justify-between items-center">
|
<div className="flex flex-row justify-between items-center">
|
||||||
<Rating value={4} />
|
<Rating value={product.metafields.reviews.rating.value} />
|
||||||
<div className="text-accent-6 pr-1 font-medium text-sm">36 reviews</div>
|
<div className="text-accent-6 pr-1 font-medium text-sm">
|
||||||
|
{product.metafields.reviews.count?.value ?? 0} reviews
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{error && <ErrorMessage error={error} className="my-5" />}
|
{error && <ErrorMessage error={error} className="my-5" />}
|
||||||
{process.env.COMMERCE_CART_ENABLED && (
|
{process.env.COMMERCE_CART_ENABLED && (
|
||||||
@ -88,11 +96,27 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
|||||||
This is a limited edition production run. Printing starts when the
|
This is a limited edition production run. Printing starts when the
|
||||||
drop ends.
|
drop ends.
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
<Collapse title="Details">
|
<Collapse title="Details">
|
||||||
This is a limited edition production run. Printing starts when the
|
This is a limited edition production run. Printing starts when the
|
||||||
drop ends. Reminder: Bad Boys For Life. Shipping may take 10+ days due
|
drop ends. Reminder: Bad Boys For Life. Shipping may take 10+ days due
|
||||||
to COVID-19.
|
to COVID-19.
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
|
{product.customFields && product.customFields?.length > 0 && (
|
||||||
|
<Collapse title="Specifications">
|
||||||
|
<ProductCustomFields customFields={product.customFields} />
|
||||||
|
</Collapse>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{product.metafields?.my_fields && (
|
||||||
|
<Collapse title="Specifications">
|
||||||
|
<ProductMetafields
|
||||||
|
metafields={product.metafields}
|
||||||
|
namespace="my_fields"
|
||||||
|
/>
|
||||||
|
</Collapse>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -3,11 +3,22 @@ import type {
|
|||||||
GetStaticPropsContext,
|
GetStaticPropsContext,
|
||||||
InferGetStaticPropsType,
|
InferGetStaticPropsType,
|
||||||
} from 'next'
|
} from 'next'
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import commerce from '@lib/api/commerce'
|
import commerce from '@lib/api/commerce'
|
||||||
|
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
import { Layout } from '@components/common'
|
import { Layout } from '@components/common'
|
||||||
import { ProductView } from '@components/product'
|
import { ProductView } from '@components/product'
|
||||||
|
|
||||||
|
// Used by the Shopify Example
|
||||||
|
const withMetafields = [
|
||||||
|
{ namespace: 'reviews', key: 'rating' },
|
||||||
|
{ namespace: 'reviews', key: 'count' },
|
||||||
|
{ namespace: 'my_fields', key: 'width' },
|
||||||
|
{ namespace: 'my_fields', key: 'weight' },
|
||||||
|
{ namespace: 'my_fields', key: 'length' },
|
||||||
|
]
|
||||||
|
|
||||||
export async function getStaticProps({
|
export async function getStaticProps({
|
||||||
params,
|
params,
|
||||||
locale,
|
locale,
|
||||||
@ -18,7 +29,10 @@ export async function getStaticProps({
|
|||||||
const pagesPromise = commerce.getAllPages({ config, preview })
|
const pagesPromise = commerce.getAllPages({ config, preview })
|
||||||
const siteInfoPromise = commerce.getSiteInfo({ config, preview })
|
const siteInfoPromise = commerce.getSiteInfo({ config, preview })
|
||||||
const productPromise = commerce.getProduct({
|
const productPromise = commerce.getProduct({
|
||||||
variables: { slug: params!.slug },
|
variables: {
|
||||||
|
slug: params!.slug,
|
||||||
|
withMetafields,
|
||||||
|
},
|
||||||
config,
|
config,
|
||||||
preview,
|
preview,
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user