@faststore/core 3.0.16 → 3.0.20

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.
Files changed (50) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/cache/.tsbuildinfo +1 -1
  4. package/.next/cache/config.json +3 -3
  5. package/.next/cache/eslint/.cache_1gneedd +1 -1
  6. package/.next/cache/webpack/client-production/0.pack +0 -0
  7. package/.next/cache/webpack/client-production/index.pack +0 -0
  8. package/.next/cache/webpack/server-production/0.pack +0 -0
  9. package/.next/cache/webpack/server-production/index.pack +0 -0
  10. package/.next/next-minimal-server.js.nft.json +1 -1
  11. package/.next/next-server.js.nft.json +1 -1
  12. package/.next/prerender-manifest.js +1 -1
  13. package/.next/prerender-manifest.json +1 -1
  14. package/.next/routes-manifest.json +1 -1
  15. package/.next/server/chunks/242.js +1 -1
  16. package/.next/server/chunks/640.js +1 -1
  17. package/.next/server/chunks/646.js +2 -2
  18. package/.next/server/chunks/659.js +2 -2
  19. package/.next/server/middleware-build-manifest.js +1 -1
  20. package/.next/server/pages/404.js +1 -1
  21. package/.next/server/pages/500.js +1 -1
  22. package/.next/server/pages/[...slug].js +1 -1
  23. package/.next/server/pages/[slug]/p.js +1 -1
  24. package/.next/server/pages/account.js +1 -1
  25. package/.next/server/pages/api/graphql.js +1 -1
  26. package/.next/server/pages/checkout.js +1 -1
  27. package/.next/server/pages/en-US/404.html +2 -2
  28. package/.next/server/pages/en-US/500.html +2 -2
  29. package/.next/server/pages/en-US/account.html +2 -2
  30. package/.next/server/pages/en-US/checkout.html +2 -2
  31. package/.next/server/pages/en-US/login.html +2 -2
  32. package/.next/server/pages/en-US/s.html +2 -2
  33. package/.next/server/pages/en-US.html +2 -2
  34. package/.next/server/pages/index.js +1 -1
  35. package/.next/server/pages/login.js +1 -1
  36. package/.next/server/pages/s.js +1 -1
  37. package/.next/server/pages-manifest.json +1 -1
  38. package/.next/static/chunks/pages/{[...slug]-84926ee31c8f6fee.js → [...slug]-bcaf61b01157d8cb.js} +1 -1
  39. package/.next/static/{RX_p788L2keTWhdlgySlF → rG9HjHHbItuqOEnXtbNZ5}/_buildManifest.js +1 -1
  40. package/.next/trace +91 -91
  41. package/.turbo/turbo-test.log +9 -9
  42. package/cms/faststore/content-types.json +14 -1
  43. package/package.json +3 -3
  44. package/src/components/templates/ProductListingPage/ProductListing.tsx +4 -4
  45. package/src/components/templates/ProductListingPage/ProductListingPage.tsx +1 -1
  46. package/src/pages/[...slug].tsx +5 -24
  47. package/src/server/cms/index.ts +1 -1
  48. package/src/server/cms/plp.ts +74 -0
  49. package/src/utils/utilities.ts +53 -0
  50. /package/.next/static/{RX_p788L2keTWhdlgySlF → rG9HjHHbItuqOEnXtbNZ5}/_ssgManifest.js +0 -0
@@ -1,23 +1,23 @@
1
1
  $ jest
2
- PASS test/server/index.test.ts (21.654 s)
2
+ PASS test/server/index.test.ts (21.291 s)
3
3
  FastStore GraphQL Layer
4
4
  @faststore/api
5
- ✓ should return a valid GraphQL schema (17 ms)
6
- ✓ should return a valid GraphQL schema contain all expected types (14 ms)
5
+ ✓ should return a valid GraphQL schema (10 ms)
6
+ ✓ should return a valid GraphQL schema contain all expected types (7 ms)
7
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 (8 ms)
10
+ ✓ getTypeDefsFromFolder function should return an Array (31 ms)
11
11
  Third Party API Extension
12
- ✓ getTypeDefsFromFolder function should return an Array (29 ms)
12
+ ✓ getTypeDefsFromFolder function should return an Array (8 ms)
13
13
  Final Schema after merging
14
- ✓ should return a valid merged GraphQL schema (42 ms)
14
+ ✓ should return a valid merged GraphQL schema (51 ms)
15
15
  Envelop
16
- ✓ should exist with its plugins (42 ms)
17
- ✓ should handle options and execute (372 ms)
16
+ ✓ should exist with its plugins (58 ms)
17
+ ✓ should handle options and execute (174 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: 21.713 s
22
+ Time: 21.381 s
23
23
  Ran all test suites.
@@ -129,11 +129,24 @@
129
129
  "id": "plp",
130
130
  "name": "Product List Page",
131
131
  "scopes": ["plp"],
132
- "isSingleton": true,
133
132
  "configurationSchemaSets": [
134
133
  {
135
134
  "name": "Settings",
136
135
  "configurations": [
136
+ {
137
+ "name": "template",
138
+ "schema": {
139
+ "title": "Template",
140
+ "type": "object",
141
+ "properties": {
142
+ "value": {
143
+ "title": "PLP template value (e.g. Slug: /department/category/subcategory)",
144
+ "type": "string",
145
+ "description": "PLP slug template (e.g. /office or /office/chairs) representing the /department/category/subcategory template). If this field is left empty, the generic PLP template will be applied."
146
+ }
147
+ }
148
+ }
149
+ },
137
150
  {
138
151
  "name": "productGallery",
139
152
  "schema": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "3.0.16",
3
+ "version": "3.0.20",
4
4
  "license": "MIT",
5
5
  "repository": "vtex/faststore",
6
6
  "browserslist": "supports es6-module and not dead",
@@ -39,7 +39,7 @@
39
39
  "@envelop/graphql-jit": "^1.1.1",
40
40
  "@envelop/parser-cache": "^2.2.0",
41
41
  "@envelop/validation-cache": "^2.2.0",
42
- "@faststore/api": "^3.0.7",
42
+ "@faststore/api": "^3.0.20",
43
43
  "@faststore/components": "^3.0.7",
44
44
  "@faststore/graphql-utils": "^3.0.13",
45
45
  "@faststore/sdk": "^3.0.7",
@@ -128,5 +128,5 @@
128
128
  "node": "18.19.0",
129
129
  "yarn": "1.19.1"
130
130
  },
131
- "gitHead": "ae371cd0fb3a8eab012ed79c65a55aedf074070e"
131
+ "gitHead": "c75d1f53d7efeae52483db94d9e66ddbebb8de5f"
132
132
  }
@@ -1,24 +1,24 @@
1
+ import { useSearch } from '@faststore/sdk'
1
2
  import type { ServerCollectionPageQueryQuery } from '@generated/graphql'
3
+ import deepmerge from 'deepmerge'
2
4
  import { OverriddenDefaultBreadcrumb as Breadcrumb } from 'src/components/sections/Breadcrumb/OverriddenDefaultBreadcrumb'
3
5
  import { OverriddenDefaultHero as Hero } from 'src/components/sections/Hero/OverriddenDefaultHero'
4
6
  import { OverriddenDefaultProductGallery as ProductGallery } from 'src/components/sections/ProductGallery/OverriddenDefaultProductGallery'
5
7
  import { OverriddenDefaultProductShelf as ProductShelf } from 'src/components/sections/ProductShelf/OverriddenDefaultProductShelf'
6
8
  import ScrollToTopButton from 'src/components/sections/ScrollToTopButton'
7
9
  import { ITEMS_PER_PAGE } from 'src/constants'
8
- import deepmerge from 'deepmerge'
9
- import { useSearch } from '@faststore/sdk'
10
10
 
11
11
  import type { ComponentType } from 'react'
12
12
  import RenderSections from 'src/components/cms/RenderSections'
13
13
  import CUSTOM_COMPONENTS from 'src/customizations/src/components'
14
- import { PLPContentType } from 'src/server/cms'
14
+ import { PLPContentType } from 'src/server/cms/plp'
15
15
 
16
+ import PageProvider, { PLPContext } from 'src/sdk/overrides/PageProvider'
16
17
  import {
17
18
  useCreateUseGalleryPage,
18
19
  UseGalleryPageContext,
19
20
  } from 'src/sdk/product/usePageProductsQuery'
20
21
  import { useProductGalleryQuery } from 'src/sdk/product/useProductGalleryQuery'
21
- import PageProvider, { PLPContext } from 'src/sdk/overrides/PageProvider'
22
22
 
23
23
  export type ProductListingPageProps = {
24
24
  data: ServerCollectionPageQueryQuery
@@ -12,7 +12,7 @@ import type { ServerCollectionPageQueryQuery } from '@generated/graphql'
12
12
  import { ITEMS_PER_PAGE } from 'src/constants'
13
13
  import { useApplySearchState } from 'src/sdk/search/state'
14
14
 
15
- import { PLPContentType } from 'src/server/cms'
15
+ import { PLPContentType } from 'src/server/cms/plp'
16
16
 
17
17
  import storeConfig from '../../../../faststore.config'
18
18
  import ProductListing from './ProductListing'
@@ -1,5 +1,4 @@
1
1
  import { isNotFoundError } from '@faststore/api'
2
- import storeConfig from 'faststore.config'
3
2
  import type { GetStaticPaths, GetStaticProps } from 'next'
4
3
 
5
4
  import { gql } from '@generated'
@@ -22,7 +21,8 @@ import LandingPage, {
22
21
  import ProductListingPage, {
23
22
  ProductListingPageProps,
24
23
  } from 'src/components/templates/ProductListingPage'
25
- import { getPage, PageContentType, PLPContentType } from 'src/server/cms'
24
+ import { PageContentType } from 'src/server/cms'
25
+ import { getPLP, PLPContentType } from 'src/server/cms/plp'
26
26
 
27
27
  type BaseProps = {
28
28
  globalSections: GlobalSectionsData
@@ -101,7 +101,7 @@ export const getStaticProps: GetStaticProps<
101
101
  }
102
102
  }
103
103
 
104
- const [{ data, errors = [] }] = await Promise.all([
104
+ const [{ data, errors = [] }, cmsPage] = await Promise.all([
105
105
  execute<
106
106
  ServerCollectionPageQueryQueryVariables,
107
107
  ServerCollectionPageQueryQuery
@@ -109,28 +109,9 @@ export const getStaticProps: GetStaticProps<
109
109
  variables: { slug },
110
110
  operation: query,
111
111
  }),
112
+ getPLP(slug, previewData),
112
113
  ])
113
114
 
114
- let pageData
115
-
116
- if (storeConfig.cms.data) {
117
- const cmsData = JSON.parse(storeConfig.cms.data)
118
- const page = cmsData['plp'][0]
119
-
120
- if (page) {
121
- pageData = await getPage<PLPContentType>({
122
- contentType: 'plp',
123
- documentId: page.documentId,
124
- versionId: page.versionId,
125
- })
126
- }
127
- } else {
128
- pageData = await getPage<PLPContentType>({
129
- ...(previewData?.contentType === 'plp' ? previewData : null),
130
- contentType: 'plp',
131
- })
132
- }
133
-
134
115
  const notFound = errors.find(isNotFoundError)
135
116
 
136
117
  if (notFound) {
@@ -147,7 +128,7 @@ export const getStaticProps: GetStaticProps<
147
128
  return {
148
129
  props: {
149
130
  data,
150
- page: pageData,
131
+ page: cmsPage,
151
132
  globalSections: await globalSectionsPromise,
152
133
  type: 'plp',
153
134
  key: slug,
@@ -1,5 +1,6 @@
1
1
  import type { ContentData, ContentTypeOptions, Locator } from '@vtex/client-cms'
2
2
  import ClientCMS from '@vtex/client-cms'
3
+
3
4
  import MissingContentError from 'src/sdk/error/MissingContentError'
4
5
  import MultipleContentError from 'src/sdk/error/MultipleContentError'
5
6
  import config from '../../../faststore.config'
@@ -20,7 +21,6 @@ type ProductGallerySettings = {
20
21
  }
21
22
  }
22
23
 
23
- export type PLPContentType = ContentData & ProductGallerySettings
24
24
  export type SearchContentType = ContentData & ProductGallerySettings
25
25
 
26
26
  export type PageContentType = ContentData & {
@@ -0,0 +1,74 @@
1
+ import { ContentData, Locator } from '@vtex/client-cms'
2
+ import MissingContentError from 'src/sdk/error/MissingContentError'
3
+ import { findBestPLPTemplate } from 'src/utils/utilities'
4
+ import config from '../../../faststore.config'
5
+ import { Options, getCMSPage, getPage } from '../cms'
6
+
7
+ type PLPSettings = {
8
+ settings: {
9
+ template?: {
10
+ value?: string
11
+ }
12
+ productGallery: {
13
+ itemsPerPage: number
14
+ sortBySelection: string
15
+ }
16
+ }
17
+ }
18
+
19
+ type PLPfromCmsEnvData = {
20
+ documentId: string
21
+ versionId: string
22
+ } & PLPSettings
23
+
24
+ export type PLPContentType = ContentData & PLPSettings
25
+
26
+ export const getPLP = async (slug: string, previewData: Locator) => {
27
+ if (config.cms.data) {
28
+ const cmsData = JSON.parse(config.cms.data)
29
+ const allPLPsFromCmsEnvData: PLPfromCmsEnvData[] = cmsData['plp']
30
+
31
+ return await getPLPFromCmsEnvData(`/${slug}/p`, allPLPsFromCmsEnvData, {
32
+ ...(previewData?.contentType === 'plp' ? previewData : null),
33
+ contentType: 'plp',
34
+ })
35
+ }
36
+
37
+ return (await getPLPFromCms(`/${slug}/p`, {
38
+ ...(previewData?.contentType === 'plp' ? previewData : null),
39
+ contentType: 'plp',
40
+ })) as PLPContentType
41
+ }
42
+
43
+ const getPLPFromCmsEnvData = async (
44
+ slug: string,
45
+ allPLPsFromCMSData: PLPfromCmsEnvData[],
46
+ options: Options
47
+ ): Promise<PLPContentType> => {
48
+ const pages: PLPfromCmsEnvData[] = allPLPsFromCMSData ?? []
49
+
50
+ if (!pages[0]) {
51
+ throw new MissingContentError(options)
52
+ }
53
+
54
+ const template = findBestPLPTemplate(pages, slug)
55
+
56
+ return getPage<PLPContentType>({
57
+ contentType: 'plp',
58
+ documentId: template.documentId as string,
59
+ versionId: template.versionId,
60
+ })
61
+ }
62
+
63
+ export const getPLPFromCms = async (
64
+ slug: string,
65
+ options: Options
66
+ ): Promise<Partial<PLPContentType>> => {
67
+ const pages = (await getCMSPage(options)).data
68
+
69
+ if (!pages[0]) {
70
+ throw new MissingContentError(options)
71
+ }
72
+
73
+ return findBestPLPTemplate(pages, slug)
74
+ }
@@ -1,5 +1,6 @@
1
1
  import { ServerProductQueryQuery } from '@generated/graphql'
2
2
  import { PDPContentType } from 'src/server/cms/pdp'
3
+ import { PLPContentType } from 'src/server/cms/plp'
3
4
 
4
5
  //Input "Example Text!". Output: example-text
5
6
  export function textToKebabCase(text: string): string {
@@ -15,6 +16,58 @@ export function textToKebabCase(text: string): string {
15
16
  return kebabCase ?? ''
16
17
  }
17
18
 
19
+ export function normalizePLPSlug(slug: string) {
20
+ // Remove extra slashes at the beginning and end
21
+ let normalizedSlug = slug.replace(/^\/+|\/+$/g, '')
22
+
23
+ // Remove duplicate slashes and white spaces throughout the string
24
+ normalizedSlug = normalizedSlug.replace(/\/+/g, '/').replace(/\s+/g, '')
25
+
26
+ return '/' + normalizedSlug.toLowerCase()
27
+ }
28
+
29
+ function findPLPTemplateBySlug(pages: Partial<PLPContentType>[], slug: string) {
30
+ return pages.find((page) => {
31
+ // generic PLP template
32
+ if (!page.settings?.template?.value) return false
33
+
34
+ const templateValue = normalizePLPSlug(page.settings?.template?.value)
35
+ return templateValue === slug
36
+ })
37
+ }
38
+
39
+ /**
40
+ * Find the best PLP template from the CMS based on the slug passed as param.
41
+ *
42
+ * This function iterates the slug until there is no slashes (/), prioritizing the following order:
43
+ * 1. A PLP template that matches the subcategory (e.g. slug = /department/category/subcategory).
44
+ * 2. A PLP template that matches the category (e.g. slug = /department/category).
45
+ * 3. A PLP template that matches the department (e.g. slug = /department).
46
+ * 4. If no matches are found, use the generic PLP template.
47
+ *
48
+ * @param pages
49
+ * @param originalSlug
50
+ * @returns The best PLP template page for the slug
51
+ */
52
+ export function findBestPLPTemplate(
53
+ pages: Partial<PLPContentType>[],
54
+ originalSlug: string
55
+ ) {
56
+ let slug = normalizePLPSlug(originalSlug)
57
+ let foundPageTemplate = findPLPTemplateBySlug(pages, slug)
58
+
59
+ while (!foundPageTemplate && slug.lastIndexOf('/') !== -1) {
60
+ slug = slug.substring(0, slug.lastIndexOf('/'))
61
+ foundPageTemplate = findPLPTemplateBySlug(pages, slug)
62
+ }
63
+
64
+ return (
65
+ foundPageTemplate ||
66
+ pages.find((page) => !page.settings?.template?.value) ||
67
+ pages[0]
68
+ )
69
+ }
70
+
18
71
  export function normalizePDPTemplate(templateValue: string) {
19
72
  // Remove extra slashes, white spaces at the beginning and end
20
73
  let formattedValue = templateValue.trim().replace(/^\/+|\/+$/g, '')