@faststore/core 3.0.14 → 3.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +15 -15
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/cache/eslint/.cache_1gneedd +1 -1
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/next-minimal-server.js.nft.json +1 -1
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.js +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/routes-manifest.json +1 -1
- package/.next/server/chunks/640.js +1 -1
- package/.next/server/chunks/646.js +2 -2
- package/.next/server/chunks/659.js +2 -2
- package/.next/server/functions-config-manifest.json +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.js +1 -1
- package/.next/server/pages/500.js +1 -1
- package/.next/server/pages/[...slug].js +1 -1
- package/.next/server/pages/[slug]/p.js +1 -1
- package/.next/server/pages/account.js +1 -1
- package/.next/server/pages/api/preview.js +1 -1
- package/.next/server/pages/checkout.js +1 -1
- package/.next/server/pages/en-US/404.html +2 -2
- package/.next/server/pages/en-US/404.json +1 -1
- package/.next/server/pages/en-US/500.html +2 -2
- package/.next/server/pages/en-US/500.json +1 -1
- package/.next/server/pages/en-US/account.html +2 -2
- package/.next/server/pages/en-US/account.json +1 -1
- package/.next/server/pages/en-US/checkout.html +2 -2
- package/.next/server/pages/en-US/checkout.json +1 -1
- package/.next/server/pages/en-US/login.html +2 -2
- package/.next/server/pages/en-US/login.json +1 -1
- package/.next/server/pages/en-US/s.html +2 -2
- package/.next/server/pages/en-US/s.json +1 -1
- package/.next/server/pages/en-US.html +4 -4
- package/.next/server/pages/en-US.json +1 -1
- package/.next/server/pages/index.js +1 -1
- package/.next/server/pages/login.js +1 -1
- package/.next/server/pages/s.js +1 -1
- package/.next/server/pages-manifest.json +1 -1
- package/.next/static/{OJ8d-uEqYHb0KXVz0Trnt → RX_p788L2keTWhdlgySlF}/_buildManifest.js +1 -1
- package/.next/static/chunks/104-4f83b1d87ad36358.js +1 -0
- package/.next/static/chunks/pages/{[...slug]-506fd56c3ad48553.js → [...slug]-84926ee31c8f6fee.js} +1 -1
- package/.next/static/chunks/pages/[slug]/{p-a4f7d7c00fdf4157.js → p-5fb8fe2c80ec1608.js} +1 -1
- package/.next/static/chunks/pages/{index-00798cca3b47590d.js → index-cd109119d65df8e3.js} +1 -1
- package/.next/static/chunks/pages/{s-e78f09767764a172.js → s-26e475975386c51a.js} +1 -1
- package/.next/trace +91 -91
- package/.turbo/turbo-test.log +10 -10
- package/cms/faststore/content-types.json +21 -2
- package/cypress/integration/seo.test.js +2 -6
- package/package.json +2 -2
- package/src/components/cms/GlobalSections.tsx +4 -4
- package/src/components/templates/LandingPage/LandingPage.tsx +6 -6
- package/src/pages/[...slug].tsx +6 -11
- package/src/pages/[slug]/p.tsx +6 -25
- package/src/pages/index.tsx +3 -3
- package/src/pages/s.tsx +3 -3
- package/src/server/{cms.ts → cms/index.ts} +35 -57
- package/src/server/cms/pdp.ts +82 -0
- package/src/utils/utilities.ts +65 -0
- package/.next/static/chunks/104-35697aed442af6cd.js +0 -1
- /package/.next/static/{OJ8d-uEqYHb0KXVz0Trnt → RX_p788L2keTWhdlgySlF}/_ssgManifest.js +0 -0
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
$ jest
|
|
2
|
-
PASS test/server/index.test.ts (
|
|
2
|
+
PASS test/server/index.test.ts (21.654 s)
|
|
3
3
|
FastStore GraphQL Layer
|
|
4
4
|
@faststore/api
|
|
5
|
-
✓ should return a valid GraphQL schema (
|
|
6
|
-
✓ should return a valid GraphQL schema contain all expected types (
|
|
7
|
-
✓ should return a valid GraphQL schema contain all expected queries (
|
|
5
|
+
✓ should return a valid GraphQL schema (17 ms)
|
|
6
|
+
✓ should return a valid GraphQL schema contain all expected types (14 ms)
|
|
7
|
+
✓ should return a valid GraphQL schema contain all expected queries (2 ms)
|
|
8
8
|
✓ should return a valid GraphQL schema contain all expected mutations
|
|
9
9
|
VTEX API Extension
|
|
10
|
-
✓ getTypeDefsFromFolder function should return an Array (
|
|
10
|
+
✓ getTypeDefsFromFolder function should return an Array (8 ms)
|
|
11
11
|
Third Party API Extension
|
|
12
|
-
✓ getTypeDefsFromFolder function should return an Array (
|
|
12
|
+
✓ getTypeDefsFromFolder function should return an Array (29 ms)
|
|
13
13
|
Final Schema after merging
|
|
14
|
-
✓ should return a valid merged GraphQL schema (
|
|
14
|
+
✓ should return a valid merged GraphQL schema (42 ms)
|
|
15
15
|
Envelop
|
|
16
|
-
✓ should exist with its plugins (
|
|
17
|
-
✓ should handle options and execute (
|
|
16
|
+
✓ should exist with its plugins (42 ms)
|
|
17
|
+
✓ should handle options and execute (372 ms)
|
|
18
18
|
|
|
19
19
|
Test Suites: 1 passed, 1 total
|
|
20
20
|
Tests: 9 passed, 9 total
|
|
21
21
|
Snapshots: 0 total
|
|
22
|
-
Time:
|
|
22
|
+
Time: 21.713 s
|
|
23
23
|
Ran all test suites.
|
|
@@ -103,8 +103,27 @@
|
|
|
103
103
|
"id": "pdp",
|
|
104
104
|
"name": "Product Page",
|
|
105
105
|
"scopes": ["pdp"],
|
|
106
|
-
"
|
|
107
|
-
|
|
106
|
+
"configurationSchemaSets": [
|
|
107
|
+
{
|
|
108
|
+
"name": "Settings",
|
|
109
|
+
"configurations": [
|
|
110
|
+
{
|
|
111
|
+
"name": "template",
|
|
112
|
+
"schema": {
|
|
113
|
+
"title": "Template",
|
|
114
|
+
"type": "object",
|
|
115
|
+
"properties": {
|
|
116
|
+
"value": {
|
|
117
|
+
"title": "PDP template value (e.g. Slug: /apple-magic-mouse/p OR /department/category/subcategory/)",
|
|
118
|
+
"type": "string",
|
|
119
|
+
"description": "Possible values: the PDP slug template (e.g. '/apple-magic-mouse/p'); A PLP template (e.g. '/department/' OR '/department/category/' OR '/department/category/subcategory/'). If empty, this template will be the generic PDP."
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
]
|
|
108
127
|
},
|
|
109
128
|
{
|
|
110
129
|
"id": "plp",
|
|
@@ -176,9 +176,7 @@ describe('Collection Page Seo', () => {
|
|
|
176
176
|
cy.get('link[rel="canonical"]')
|
|
177
177
|
.should('exist')
|
|
178
178
|
.should(($link) => {
|
|
179
|
-
expect($link.attr('href')).to.eq(
|
|
180
|
-
`${storeUrl}${pages.collection}`
|
|
181
|
-
)
|
|
179
|
+
expect($link.attr('href')).to.eq(`${storeUrl}${pages.collection}`)
|
|
182
180
|
})
|
|
183
181
|
})
|
|
184
182
|
|
|
@@ -233,9 +231,7 @@ describe('Filtered Collection Page Seo', () => {
|
|
|
233
231
|
cy.get('link[rel="canonical"]')
|
|
234
232
|
.should('exist')
|
|
235
233
|
.should(($link) => {
|
|
236
|
-
expect($link.attr('href')).to.eq(
|
|
237
|
-
`${storeUrl}${pages.collection}`
|
|
238
|
-
)
|
|
234
|
+
expect($link.attr('href')).to.eq(`${storeUrl}${pages.collection}`)
|
|
239
235
|
})
|
|
240
236
|
})
|
|
241
237
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faststore/core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.16",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": "vtex/faststore",
|
|
6
6
|
"browserslist": "supports es6-module and not dead",
|
|
@@ -128,5 +128,5 @@
|
|
|
128
128
|
"node": "18.19.0",
|
|
129
129
|
"yarn": "1.19.1"
|
|
130
130
|
},
|
|
131
|
-
"gitHead": "
|
|
131
|
+
"gitHead": "ae371cd0fb3a8eab012ed79c65a55aedf074070e"
|
|
132
132
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { Locator, Section } from '@vtex/client-cms'
|
|
2
|
+
import storeConfig from 'faststore.config'
|
|
2
3
|
import type { ComponentType } from 'react'
|
|
3
4
|
import { PropsWithChildren, lazy } from 'react'
|
|
4
|
-
import storeConfig from 'faststore.config'
|
|
5
5
|
import CUSTOM_COMPONENTS from 'src/customizations/src/components'
|
|
6
|
-
import { PageContentType, getPage
|
|
6
|
+
import { PageContentType, getPage } from 'src/server/cms'
|
|
7
7
|
|
|
8
8
|
import Toast from 'src/components/common/Toast'
|
|
9
9
|
import RenderSections from './RenderSections'
|
|
10
10
|
|
|
11
11
|
import { OverriddenDefaultAlert as Alert } from 'src/components/sections/Alert/OverriddenDefaultAlert'
|
|
12
|
+
import Footer from 'src/components/sections/Footer'
|
|
12
13
|
import { OverriddenDefaultNavbar as Navbar } from 'src/components/sections/Navbar/OverriddenDefaultNavbar'
|
|
13
14
|
import { OverriddenDefaultRegionBar as RegionBar } from 'src/components/sections/RegionBar/OverriddenDefaultRegionBar'
|
|
14
|
-
import Footer from 'src/components/sections/Footer'
|
|
15
15
|
|
|
16
16
|
const RegionModal = lazy(() => import('src/components/region/RegionModal'))
|
|
17
17
|
const CartSidebar = lazy(() => import('src/components/cart/CartSidebar'))
|
|
@@ -56,7 +56,7 @@ export const getGlobalSectionsData = async (
|
|
|
56
56
|
const page = cmsData[GLOBAL_SECTIONS_CONTENT_TYPE][0]
|
|
57
57
|
|
|
58
58
|
if (page) {
|
|
59
|
-
const pageData = await
|
|
59
|
+
const pageData = await getPage<PageContentType>({
|
|
60
60
|
contentType: GLOBAL_SECTIONS_CONTENT_TYPE,
|
|
61
61
|
documentId: page.documentId,
|
|
62
62
|
versionId: page.versionId,
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
+
import type { Locator } from '@vtex/client-cms'
|
|
1
2
|
import { NextSeo, SiteLinksSearchBoxJsonLd } from 'next-seo'
|
|
2
3
|
import type { ComponentType } from 'react'
|
|
3
|
-
import type { Locator } from '@vtex/client-cms'
|
|
4
4
|
|
|
5
|
-
import MissingContentError from 'src/sdk/error/MissingContentError/MissingContentError'
|
|
6
5
|
import RenderSections from 'src/components/cms/RenderSections'
|
|
7
6
|
import { OverriddenDefaultBannerText as BannerText } from 'src/components/sections/BannerText/OverriddenDefaultBannerText'
|
|
8
7
|
import { OverriddenDefaultHero as Hero } from 'src/components/sections/Hero/OverriddenDefaultHero'
|
|
8
|
+
import Incentives from 'src/components/sections/Incentives'
|
|
9
9
|
import { OverriddenDefaultNewsletter as Newsletter } from 'src/components/sections/Newsletter/OverriddenDefaultNewsletter'
|
|
10
10
|
import { OverriddenDefaultProductShelf as ProductShelf } from 'src/components/sections/ProductShelf/OverriddenDefaultProductShelf'
|
|
11
|
-
import Incentives from 'src/components/sections/Incentives'
|
|
12
11
|
import ProductTiles from 'src/components/sections/ProductTiles'
|
|
13
|
-
import { getPage, getPageByVersionId } from 'src/server/cms'
|
|
14
|
-
import type { PageContentType } from 'src/server/cms'
|
|
15
12
|
import CUSTOM_COMPONENTS from 'src/customizations/src/components'
|
|
13
|
+
import MissingContentError from 'src/sdk/error/MissingContentError/MissingContentError'
|
|
14
|
+
import type { PageContentType } from 'src/server/cms'
|
|
15
|
+
import { getPage } from 'src/server/cms'
|
|
16
16
|
|
|
17
17
|
import storeConfig from 'faststore.config'
|
|
18
18
|
|
|
@@ -92,7 +92,7 @@ export const getLandingPageBySlug = async (
|
|
|
92
92
|
})
|
|
93
93
|
|
|
94
94
|
if (pageBySlug) {
|
|
95
|
-
const landingPageData = await
|
|
95
|
+
const landingPageData = await getPage<PageContentType>({
|
|
96
96
|
contentType: 'landingPage',
|
|
97
97
|
documentId: pageBySlug.documentId,
|
|
98
98
|
versionId: pageBySlug.versionId,
|
package/src/pages/[...slug].tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isNotFoundError } from '@faststore/api'
|
|
2
|
-
import type { GetStaticPaths, GetStaticProps } from 'next'
|
|
3
2
|
import storeConfig from 'faststore.config'
|
|
3
|
+
import type { GetStaticPaths, GetStaticProps } from 'next'
|
|
4
4
|
|
|
5
5
|
import { gql } from '@generated'
|
|
6
6
|
import type {
|
|
@@ -15,19 +15,14 @@ import GlobalSections, {
|
|
|
15
15
|
getGlobalSectionsData,
|
|
16
16
|
GlobalSectionsData,
|
|
17
17
|
} from 'src/components/cms/GlobalSections'
|
|
18
|
-
import {
|
|
19
|
-
getPage,
|
|
20
|
-
getPageByVersionId,
|
|
21
|
-
PageContentType,
|
|
22
|
-
PLPContentType,
|
|
23
|
-
} from 'src/server/cms'
|
|
24
|
-
import ProductListingPage, {
|
|
25
|
-
ProductListingPageProps,
|
|
26
|
-
} from 'src/components/templates/ProductListingPage'
|
|
27
18
|
import LandingPage, {
|
|
28
19
|
getLandingPageBySlug,
|
|
29
20
|
LandingPageProps,
|
|
30
21
|
} from 'src/components/templates/LandingPage'
|
|
22
|
+
import ProductListingPage, {
|
|
23
|
+
ProductListingPageProps,
|
|
24
|
+
} from 'src/components/templates/ProductListingPage'
|
|
25
|
+
import { getPage, PageContentType, PLPContentType } from 'src/server/cms'
|
|
31
26
|
|
|
32
27
|
type BaseProps = {
|
|
33
28
|
globalSections: GlobalSectionsData
|
|
@@ -123,7 +118,7 @@ export const getStaticProps: GetStaticProps<
|
|
|
123
118
|
const page = cmsData['plp'][0]
|
|
124
119
|
|
|
125
120
|
if (page) {
|
|
126
|
-
pageData = await
|
|
121
|
+
pageData = await getPage<PLPContentType>({
|
|
127
122
|
contentType: 'plp',
|
|
128
123
|
documentId: page.documentId,
|
|
129
124
|
versionId: page.versionId,
|
package/src/pages/[slug]/p.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { isNotFoundError } from '@faststore/api'
|
|
2
2
|
import type { Locator } from '@vtex/client-cms'
|
|
3
|
+
import deepmerge from 'deepmerge'
|
|
3
4
|
import type { GetStaticPaths, GetStaticProps } from 'next'
|
|
4
5
|
import { BreadcrumbJsonLd, NextSeo, ProductJsonLd } from 'next-seo'
|
|
5
6
|
import type { ComponentType } from 'react'
|
|
6
|
-
import deepmerge from 'deepmerge'
|
|
7
7
|
|
|
8
8
|
import { gql } from '@generated'
|
|
9
9
|
import {
|
|
@@ -20,16 +20,15 @@ import CUSTOM_COMPONENTS from 'src/customizations/src/components'
|
|
|
20
20
|
import { useSession } from 'src/sdk/session'
|
|
21
21
|
import { mark } from 'src/sdk/tests/mark'
|
|
22
22
|
import { execute } from 'src/server'
|
|
23
|
-
import type { PDPContentType } from 'src/server/cms'
|
|
24
|
-
import { getPage, getPageByVersionId } from 'src/server/cms'
|
|
25
23
|
|
|
24
|
+
import storeConfig from 'faststore.config'
|
|
26
25
|
import GlobalSections, {
|
|
27
26
|
GlobalSectionsData,
|
|
28
27
|
getGlobalSectionsData,
|
|
29
28
|
} from 'src/components/cms/GlobalSections'
|
|
30
|
-
import storeConfig from 'faststore.config'
|
|
31
|
-
import { useProductQuery } from 'src/sdk/product/useProductQuery'
|
|
32
29
|
import PageProvider, { PDPContext } from 'src/sdk/overrides/PageProvider'
|
|
30
|
+
import { useProductQuery } from 'src/sdk/product/useProductQuery'
|
|
31
|
+
import { PDPContentType, getPDP } from 'src/server/cms/pdp'
|
|
33
32
|
|
|
34
33
|
/**
|
|
35
34
|
* Sections: Components imported from each store's custom components and '../components/sections' only.
|
|
@@ -208,26 +207,6 @@ export const getStaticProps: GetStaticProps<
|
|
|
208
207
|
getGlobalSectionsData(previewData),
|
|
209
208
|
])
|
|
210
209
|
|
|
211
|
-
let cmsPage
|
|
212
|
-
|
|
213
|
-
if (storeConfig.cms.data) {
|
|
214
|
-
const cmsData = JSON.parse(storeConfig.cms.data)
|
|
215
|
-
const page = cmsData['pdp'][0]
|
|
216
|
-
|
|
217
|
-
if (page) {
|
|
218
|
-
cmsPage = getPageByVersionId<PDPContentType>({
|
|
219
|
-
contentType: 'pdp',
|
|
220
|
-
documentId: page.documentId,
|
|
221
|
-
versionId: page.versionId,
|
|
222
|
-
})
|
|
223
|
-
}
|
|
224
|
-
} else {
|
|
225
|
-
cmsPage = getPage<PDPContentType>({
|
|
226
|
-
...(previewData?.contentType === 'pdp' ? previewData : null),
|
|
227
|
-
contentType: 'pdp',
|
|
228
|
-
})
|
|
229
|
-
}
|
|
230
|
-
|
|
231
210
|
const { data, errors = [] } = searchResult
|
|
232
211
|
|
|
233
212
|
const notFound = errors.find(isNotFoundError)
|
|
@@ -242,6 +221,8 @@ export const getStaticProps: GetStaticProps<
|
|
|
242
221
|
throw errors[0]
|
|
243
222
|
}
|
|
244
223
|
|
|
224
|
+
const cmsPage: PDPContentType = await getPDP(slug, data.product, previewData)
|
|
225
|
+
|
|
245
226
|
const { seo } = data.product
|
|
246
227
|
const title = seo.title || storeConfig.seo.title
|
|
247
228
|
const description = seo.description || storeConfig.seo.description
|
package/src/pages/index.tsx
CHANGED
|
@@ -6,14 +6,14 @@ import type { ComponentType } from 'react'
|
|
|
6
6
|
import RenderSections from 'src/components/cms/RenderSections'
|
|
7
7
|
import { OverriddenDefaultBannerText as BannerText } from 'src/components/sections/BannerText/OverriddenDefaultBannerText'
|
|
8
8
|
import { OverriddenDefaultHero as Hero } from 'src/components/sections/Hero/OverriddenDefaultHero'
|
|
9
|
+
import Incentives from 'src/components/sections/Incentives'
|
|
9
10
|
import { OverriddenDefaultNewsletter as Newsletter } from 'src/components/sections/Newsletter/OverriddenDefaultNewsletter'
|
|
10
11
|
import { OverriddenDefaultProductShelf as ProductShelf } from 'src/components/sections/ProductShelf/OverriddenDefaultProductShelf'
|
|
11
|
-
import Incentives from 'src/components/sections/Incentives'
|
|
12
12
|
import ProductTiles from 'src/components/sections/ProductTiles'
|
|
13
13
|
import CUSTOM_COMPONENTS from 'src/customizations/src/components'
|
|
14
14
|
import { mark } from 'src/sdk/tests/mark'
|
|
15
15
|
import type { PageContentType } from 'src/server/cms'
|
|
16
|
-
import { getPage
|
|
16
|
+
import { getPage } from 'src/server/cms'
|
|
17
17
|
|
|
18
18
|
import GlobalSections, {
|
|
19
19
|
GlobalSectionsData,
|
|
@@ -92,7 +92,7 @@ export const getStaticProps: GetStaticProps<
|
|
|
92
92
|
const page = cmsData['home'][0]
|
|
93
93
|
|
|
94
94
|
if (page) {
|
|
95
|
-
const pageData = await
|
|
95
|
+
const pageData = await getPage<PageContentType>({
|
|
96
96
|
contentType: 'home',
|
|
97
97
|
documentId: page.documentId,
|
|
98
98
|
versionId: page.versionId,
|
package/src/pages/s.tsx
CHANGED
|
@@ -14,14 +14,14 @@ import { useApplySearchState } from 'src/sdk/search/state'
|
|
|
14
14
|
import { mark } from 'src/sdk/tests/mark'
|
|
15
15
|
|
|
16
16
|
import { Locator } from '@vtex/client-cms'
|
|
17
|
+
import storeConfig from 'faststore.config'
|
|
17
18
|
import { GetStaticProps } from 'next'
|
|
18
19
|
import GlobalSections, {
|
|
19
20
|
getGlobalSectionsData,
|
|
20
21
|
GlobalSectionsData,
|
|
21
22
|
} from 'src/components/cms/GlobalSections'
|
|
22
|
-
import { getPage, getPageByVersionId, SearchContentType } from 'src/server/cms'
|
|
23
|
-
import storeConfig from 'faststore.config'
|
|
24
23
|
import SearchPage from 'src/components/templates/SearchPage/SearchPage'
|
|
24
|
+
import { getPage, SearchContentType } from 'src/server/cms'
|
|
25
25
|
|
|
26
26
|
type Props = {
|
|
27
27
|
page: SearchContentType
|
|
@@ -125,7 +125,7 @@ export const getStaticProps: GetStaticProps<
|
|
|
125
125
|
const page = cmsData['search'][0]
|
|
126
126
|
|
|
127
127
|
if (page) {
|
|
128
|
-
const pageData = await
|
|
128
|
+
const pageData = await getPage<SearchContentType>({
|
|
129
129
|
contentType: 'search',
|
|
130
130
|
documentId: page.documentId,
|
|
131
131
|
versionId: page.versionId,
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import type { ContentData, ContentTypeOptions, Locator } from '@vtex/client-cms'
|
|
2
2
|
import ClientCMS from '@vtex/client-cms'
|
|
3
|
-
|
|
4
|
-
import config from '../../faststore.config'
|
|
5
|
-
import MultipleContentError from 'src/sdk/error/MultipleContentError'
|
|
6
3
|
import MissingContentError from 'src/sdk/error/MissingContentError'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
workspace: config.api.workspace,
|
|
10
|
-
tenant: config.api.storeId,
|
|
11
|
-
})
|
|
4
|
+
import MultipleContentError from 'src/sdk/error/MultipleContentError'
|
|
5
|
+
import config from '../../../faststore.config'
|
|
12
6
|
|
|
13
7
|
export type Options =
|
|
14
8
|
| Locator
|
|
@@ -17,54 +11,6 @@ export type Options =
|
|
|
17
11
|
filters?: Partial<ContentTypeOptions>
|
|
18
12
|
}
|
|
19
13
|
|
|
20
|
-
const isLocator = (x: any): x is Locator =>
|
|
21
|
-
typeof x.contentType === 'string' &&
|
|
22
|
-
(typeof x.releaseId === 'string' || typeof x.documentId === 'string')
|
|
23
|
-
|
|
24
|
-
export const getPage = async <T extends ContentData>(options: Options) => {
|
|
25
|
-
const result = await (isLocator(options)
|
|
26
|
-
? clientCMS.getCMSPage(options).then((page) => ({ data: [page] }))
|
|
27
|
-
: clientCMS.getCMSPagesByContentType(options.contentType, options.filters))
|
|
28
|
-
|
|
29
|
-
const pages = result.data
|
|
30
|
-
|
|
31
|
-
if (!pages[0]) {
|
|
32
|
-
throw new MissingContentError(options)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (pages.length !== 1) {
|
|
36
|
-
throw new MultipleContentError(options)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return pages[0] as T
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export type VersionOptions = {
|
|
43
|
-
contentType: string
|
|
44
|
-
documentId: string
|
|
45
|
-
versionId: string
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const getPageByVersionId = async <T extends ContentData>(
|
|
49
|
-
options: VersionOptions
|
|
50
|
-
) => {
|
|
51
|
-
const result = await clientCMS
|
|
52
|
-
.getCMSPage(options)
|
|
53
|
-
.then((page) => ({ data: [page] }))
|
|
54
|
-
|
|
55
|
-
const pages = result.data
|
|
56
|
-
|
|
57
|
-
if (!pages[0]) {
|
|
58
|
-
throw new MissingContentError(options)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (pages.length !== 1) {
|
|
62
|
-
throw new MultipleContentError(options)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return pages[0] as T
|
|
66
|
-
}
|
|
67
|
-
|
|
68
14
|
type ProductGallerySettings = {
|
|
69
15
|
settings: {
|
|
70
16
|
productGallery: {
|
|
@@ -74,7 +20,6 @@ type ProductGallerySettings = {
|
|
|
74
20
|
}
|
|
75
21
|
}
|
|
76
22
|
|
|
77
|
-
export type PDPContentType = ContentData
|
|
78
23
|
export type PLPContentType = ContentData & ProductGallerySettings
|
|
79
24
|
export type SearchContentType = ContentData & ProductGallerySettings
|
|
80
25
|
|
|
@@ -88,3 +33,36 @@ export type PageContentType = ContentData & {
|
|
|
88
33
|
}
|
|
89
34
|
}
|
|
90
35
|
}
|
|
36
|
+
|
|
37
|
+
const isLocator = (x: any): x is Locator =>
|
|
38
|
+
typeof x.contentType === 'string' &&
|
|
39
|
+
(typeof x.releaseId === 'string' ||
|
|
40
|
+
typeof x.documentId === 'string' ||
|
|
41
|
+
typeof x.versionId === 'string')
|
|
42
|
+
|
|
43
|
+
export const clientCMS = new ClientCMS({
|
|
44
|
+
workspace: config.api.workspace,
|
|
45
|
+
tenant: config.api.storeId,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
export const getCMSPage = async (options: Options) => {
|
|
49
|
+
return await (isLocator(options)
|
|
50
|
+
? clientCMS.getCMSPage(options).then((page) => ({ data: [page] }))
|
|
51
|
+
: clientCMS.getCMSPagesByContentType(options.contentType, options.filters))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const getPage = async <T extends ContentData>(options: Options) => {
|
|
55
|
+
const result = await getCMSPage(options)
|
|
56
|
+
|
|
57
|
+
const pages = result.data
|
|
58
|
+
|
|
59
|
+
if (!pages[0]) {
|
|
60
|
+
throw new MissingContentError(options)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (pages.length !== 1) {
|
|
64
|
+
throw new MultipleContentError(options)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return pages[0] as T
|
|
68
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ServerProductQueryQuery } from '@generated/graphql'
|
|
2
|
+
import type { ContentData, Locator } from '@vtex/client-cms'
|
|
3
|
+
import MissingContentError from 'src/sdk/error/MissingContentError'
|
|
4
|
+
import { findBestPDPTemplate } from 'src/utils/utilities'
|
|
5
|
+
import { Options, getCMSPage, getPage } from '.'
|
|
6
|
+
import config from '../../../faststore.config'
|
|
7
|
+
|
|
8
|
+
type PDPSettings = {
|
|
9
|
+
settings: {
|
|
10
|
+
template?: {
|
|
11
|
+
value?: string
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type PDPfromCmsEnvData = {
|
|
17
|
+
documentId: string
|
|
18
|
+
versionId: string
|
|
19
|
+
} & PDPSettings
|
|
20
|
+
|
|
21
|
+
export type PDPContentType = ContentData & PDPSettings
|
|
22
|
+
|
|
23
|
+
export const getPDP = async (
|
|
24
|
+
slug: string,
|
|
25
|
+
product: ServerProductQueryQuery['product'],
|
|
26
|
+
previewData: Locator
|
|
27
|
+
) => {
|
|
28
|
+
if (config.cms.data) {
|
|
29
|
+
const cmsData = JSON.parse(config.cms.data)
|
|
30
|
+
const allPDPsFromCmsEnvData: PDPfromCmsEnvData[] = cmsData['pdp']
|
|
31
|
+
|
|
32
|
+
return await getPDPFromCmsEnvData(
|
|
33
|
+
`/${slug}/p`,
|
|
34
|
+
product,
|
|
35
|
+
allPDPsFromCmsEnvData,
|
|
36
|
+
{
|
|
37
|
+
...(previewData?.contentType === 'pdp' ? previewData : null),
|
|
38
|
+
contentType: 'pdp',
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (await getPDPFromCms(`/${slug}/p`, product, {
|
|
44
|
+
...(previewData?.contentType === 'pdp' ? previewData : null),
|
|
45
|
+
contentType: 'pdp',
|
|
46
|
+
})) as PDPContentType
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const getPDPFromCmsEnvData = async (
|
|
50
|
+
slug: string,
|
|
51
|
+
product: ServerProductQueryQuery['product'],
|
|
52
|
+
allPDPsFromCMSData: PDPfromCmsEnvData[],
|
|
53
|
+
options: Options
|
|
54
|
+
): Promise<PDPContentType> => {
|
|
55
|
+
const pages: PDPfromCmsEnvData[] = allPDPsFromCMSData ?? []
|
|
56
|
+
|
|
57
|
+
if (!pages[0]) {
|
|
58
|
+
throw new MissingContentError(options)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const template = findBestPDPTemplate(pages, slug, product)
|
|
62
|
+
|
|
63
|
+
return getPage<PDPContentType>({
|
|
64
|
+
contentType: 'pdp',
|
|
65
|
+
documentId: template.documentId as string,
|
|
66
|
+
versionId: template.versionId,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const getPDPFromCms = async (
|
|
71
|
+
slug: string,
|
|
72
|
+
product: ServerProductQueryQuery['product'],
|
|
73
|
+
options: Options
|
|
74
|
+
): Promise<Partial<PDPContentType>> => {
|
|
75
|
+
const pages = (await getCMSPage(options)).data
|
|
76
|
+
|
|
77
|
+
if (!pages[0]) {
|
|
78
|
+
throw new MissingContentError(options)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return findBestPDPTemplate(pages, slug, product)
|
|
82
|
+
}
|
package/src/utils/utilities.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { ServerProductQueryQuery } from '@generated/graphql'
|
|
2
|
+
import { PDPContentType } from 'src/server/cms/pdp'
|
|
3
|
+
|
|
1
4
|
//Input "Example Text!". Output: example-text
|
|
2
5
|
export function textToKebabCase(text: string): string {
|
|
3
6
|
// Replace spaces and special characters with hyphens
|
|
@@ -11,3 +14,65 @@ export function textToKebabCase(text: string): string {
|
|
|
11
14
|
|
|
12
15
|
return kebabCase ?? ''
|
|
13
16
|
}
|
|
17
|
+
|
|
18
|
+
export function normalizePDPTemplate(templateValue: string) {
|
|
19
|
+
// Remove extra slashes, white spaces at the beginning and end
|
|
20
|
+
let formattedValue = templateValue.trim().replace(/^\/+|\/+$/g, '')
|
|
21
|
+
|
|
22
|
+
// Add a slash at the beginning if not present
|
|
23
|
+
if (!formattedValue.startsWith('/')) {
|
|
24
|
+
formattedValue = '/' + formattedValue
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Add a slash at the end if not ending with '/p'
|
|
28
|
+
if (!formattedValue.endsWith('/p')) {
|
|
29
|
+
formattedValue += '/'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Remove duplicate slashes and white spaces throughout the string
|
|
33
|
+
formattedValue = formattedValue.replace(/\/+/g, '/').replace(/\s+/g, '')
|
|
34
|
+
|
|
35
|
+
return formattedValue.toLowerCase()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Find the best PDP template from the CMS based on the slug or in the product category tree.
|
|
40
|
+
* Prioritizing the following order:
|
|
41
|
+
*
|
|
42
|
+
* 1. A PDP template that matches the page slug (e.g. slug = /apple-magic-mouse/p).
|
|
43
|
+
* 2. A PDP template that matches the product subcategory (e.g. /department/category/subcategory).
|
|
44
|
+
* 3. A PDP template that matches the product category (e.g. /department/category).
|
|
45
|
+
* 4. A PDP template that matches the product department (e.g. /department).
|
|
46
|
+
* 5. If no matches are found, use the generic PDP template.
|
|
47
|
+
*
|
|
48
|
+
* @param pages
|
|
49
|
+
* @param originalSlug
|
|
50
|
+
* @param product
|
|
51
|
+
* @returns The best PDP template page for the slug
|
|
52
|
+
*/
|
|
53
|
+
export function findBestPDPTemplate(
|
|
54
|
+
pages: Partial<PDPContentType>[],
|
|
55
|
+
slug: string,
|
|
56
|
+
product: ServerProductQueryQuery['product']
|
|
57
|
+
) {
|
|
58
|
+
// productSlugAndCategoryTree with the prioritized order. [slug, subcategory tree, category tree, department]
|
|
59
|
+
const productSlugAndCategoryTree = product?.breadcrumbList?.itemListElement
|
|
60
|
+
? [...product?.breadcrumbList?.itemListElement]
|
|
61
|
+
.reverse()
|
|
62
|
+
.map(({ item }) => item)
|
|
63
|
+
: []
|
|
64
|
+
productSlugAndCategoryTree.unshift(slug)
|
|
65
|
+
|
|
66
|
+
for (const item of productSlugAndCategoryTree) {
|
|
67
|
+
for (const page of pages) {
|
|
68
|
+
if (!page.settings?.template?.value) continue
|
|
69
|
+
|
|
70
|
+
const templateValue = normalizePDPTemplate(page.settings.template.value)
|
|
71
|
+
if (templateValue === item) {
|
|
72
|
+
return page
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return pages.find((page) => !page.settings?.template?.value) || pages[0]
|
|
78
|
+
}
|