@faststore/core 3.0.60 → 3.0.62

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 (53) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +11 -11
  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/fetch-cache/50912854cb7c781522a6ff8792d714e549515fcbbbfd660761961b06afe01c07 +1 -1
  7. package/.next/cache/webpack/client-production/0.pack +0 -0
  8. package/.next/cache/webpack/client-production/index.pack +0 -0
  9. package/.next/cache/webpack/server-production/0.pack +0 -0
  10. package/.next/cache/webpack/server-production/index.pack +0 -0
  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/app/_not-found.html +1 -1
  16. package/.next/server/app/_not-found.rsc +1 -1
  17. package/.next/server/app/fs-next-update.html +1 -1
  18. package/.next/server/app/fs-next-update.rsc +1 -1
  19. package/.next/server/chunks/1481.js +1 -1
  20. package/.next/server/chunks/2381.js +1 -1
  21. package/.next/server/middleware-build-manifest.js +1 -1
  22. package/.next/server/pages/404.html +1 -1
  23. package/.next/server/pages/[slug]/p.js +5 -5
  24. package/.next/server/pages/en-US/404.html +2 -2
  25. package/.next/server/pages/en-US/404.json +1 -1
  26. package/.next/server/pages/en-US/500.html +2 -2
  27. package/.next/server/pages/en-US/500.json +1 -1
  28. package/.next/server/pages/en-US/account.html +2 -2
  29. package/.next/server/pages/en-US/account.json +1 -1
  30. package/.next/server/pages/en-US/checkout.html +2 -2
  31. package/.next/server/pages/en-US/checkout.json +1 -1
  32. package/.next/server/pages/en-US/login.html +2 -2
  33. package/.next/server/pages/en-US/login.json +1 -1
  34. package/.next/server/pages/en-US/s.html +2 -2
  35. package/.next/server/pages/en-US/s.json +1 -1
  36. package/.next/server/pages/en-US.html +3 -3
  37. package/.next/server/pages/en-US.json +1 -1
  38. package/.next/server/pages-manifest.json +1 -1
  39. package/.next/static/{6RpWP2aATNq_ljgOww_7v → a5j1XoAExpkrXk51Ikk-j}/_buildManifest.js +1 -1
  40. package/.next/static/chunks/{432-292475e2dafbb79e.js → 432-eb17ca619ec04a2a.js} +1 -1
  41. package/.next/trace +62 -62
  42. package/.turbo/turbo-build.log +2 -2
  43. package/.turbo/turbo-test.log +5 -5
  44. package/cms/faststore/sections.json +5 -0
  45. package/package.json +2 -2
  46. package/src/components/navigation/Navbar/Navbar.tsx +4 -1
  47. package/src/components/search/SearchInput/SearchInput.tsx +3 -1
  48. package/src/components/sections/Navbar/Navbar.tsx +1 -0
  49. package/src/pages/[slug]/p.tsx +1 -1
  50. package/src/server/cms/pdp.ts +7 -15
  51. package/src/utils/multipleTemplates.ts +57 -19
  52. package/test/utils/multipleTemplates.test.ts +80 -2
  53. /package/.next/static/{6RpWP2aATNq_ljgOww_7v → a5j1XoAExpkrXk51Ikk-j}/_ssgManifest.js +0 -0
@@ -1,7 +1,7 @@
1
1
  $ yarn partytown & yarn generate && next build
2
2
  $ partytown copylib ./public/~partytown
3
- Partytown lib copied to: /home/runner/work/faststore/faststore/packages/core/public/~partytown
4
3
  $ faststore generate-graphql -c
4
+ Partytown lib copied to: /home/runner/work/faststore/faststore/packages/core/public/~partytown
5
5
  success - GraphQL schema, types, and optimizations successfully generated 🎉
6
6
  ⚠ No build cache found. Please configure build caching for faster rebuilds. Read more: https://nextjs.org/docs/messages/no-cache
7
7
  Attention: Next.js now collects completely anonymous telemetry regarding usage.
@@ -24,8 +24,8 @@ Browserslist: caniuse-lite is outdated. Please run:
24
24
  Collecting page data ...
25
25
  Generating static pages (0/9) ...
26
26
 
27
27
  Generating static pages (2/9)
28
- Warning: Dynamic Content not found for the page: home. Refer to the Dynamic Content documentation at https://developers.vtex.com/docs/guides/faststore/dynamic-content-overview for mapping the page and the corresponding data-fetching function.
29
28
 
30
29
  Generating static pages (4/9)
30
+ Warning: Dynamic Content not found for the page: home. Refer to the Dynamic Content documentation at https://developers.vtex.com/docs/guides/faststore/dynamic-content-overview for mapping the page and the corresponding data-fetching function.
31
31
 
32
32
  Generating static pages (6/9)
33
33
 
34
34
  ✓ Generating static pages (9/9)
35
35
  Finalizing page optimization ...
@@ -1,10 +1,10 @@
1
1
  $ jest
2
- PASS test/utils/multipleTemplates.test.ts (32.235 s)
3
- PASS test/server/cms/index.test.ts (32.591 s)
4
- PASS test/server/index.test.ts (34.429 s)
2
+ PASS test/utils/multipleTemplates.test.ts (30.35 s)
3
+ PASS test/server/cms/index.test.ts (30.911 s)
4
+ PASS test/server/index.test.ts (32.992 s)
5
5
 
6
6
  Test Suites: 3 passed, 3 total
7
- Tests: 16 passed, 16 total
7
+ Tests: 19 passed, 19 total
8
8
  Snapshots: 0 total
9
- Time: 35.349 s
9
+ Time: 33.999 s
10
10
  Ran all test suites.
@@ -192,6 +192,11 @@
192
192
  "type": "object",
193
193
  "required": ["sort"],
194
194
  "properties": {
195
+ "placeholder": {
196
+ "title": "Placeholder for Search Bar",
197
+ "type": "string",
198
+ "default": "Search everything at the store"
199
+ },
195
200
  "sort": {
196
201
  "title": "Results default sort value",
197
202
  "type": "string",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "3.0.60",
3
+ "version": "3.0.62",
4
4
  "license": "MIT",
5
5
  "repository": "vtex/faststore",
6
6
  "browserslist": "supports es6-module and not dead",
@@ -125,5 +125,5 @@
125
125
  "node": "18.19.0",
126
126
  "yarn": "1.19.1"
127
127
  },
128
- "gitHead": "19bd97e7ce1d842d390dbdd1e7e1859c1624b5fb"
128
+ "gitHead": "e34151b9c25fb8b6bb545865bd7623cffdf3af10"
129
129
  }
@@ -120,7 +120,10 @@ function Navbar({
120
120
  </>
121
121
  )}
122
122
 
123
- <SearchInput sort={searchInput?.sort} />
123
+ <SearchInput
124
+ placeholder={searchInput?.placeholder}
125
+ sort={searchInput?.sort}
126
+ />
124
127
 
125
128
  <NavbarButtons.Component
126
129
  searchExpanded={searchExpanded}
@@ -36,6 +36,7 @@ export type SearchInputProps = {
36
36
  onSearchClick?: () => void
37
37
  buttonTestId?: string
38
38
  containerStyle?: CSSProperties
39
+ placeholder?: string
39
40
  sort?: string
40
41
  } & Omit<UISearchInputFieldProps, 'onSubmit'>
41
42
 
@@ -57,6 +58,7 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
57
58
  buttonTestId = 'fs-search-button',
58
59
  containerStyle,
59
60
  sort,
61
+ placeholder,
60
62
  ...otherProps
61
63
  },
62
64
  ref
@@ -113,7 +115,7 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
113
115
  onClick: onSearchClick,
114
116
  testId: buttonTestId,
115
117
  }}
116
- placeholder="Search everything at the store"
118
+ placeholder={placeholder}
117
119
  onChange={(e) => setSearchQuery(e.target.value)}
118
120
  onSubmit={(term) => {
119
121
  const path = formatSearchPath({
@@ -21,6 +21,7 @@ export interface NavbarProps {
21
21
  }
22
22
  }
23
23
  searchInput: {
24
+ placeholder?: string
24
25
  sort: string
25
26
  }
26
27
  signInButton: {
@@ -231,7 +231,7 @@ export const getStaticProps: GetStaticProps<
231
231
  throw errors[0]
232
232
  }
233
233
 
234
- const cmsPage: PDPContentType = await getPDP(slug, data.product, previewData)
234
+ const cmsPage: PDPContentType = await getPDP(data.product, previewData)
235
235
 
236
236
  const { seo } = data.product
237
237
  const title = seo.title || storeConfig.seo.title
@@ -21,7 +21,6 @@ type PDPfromCmsEnvData = {
21
21
  export type PDPContentType = ContentData & PDPSettings
22
22
 
23
23
  export const getPDP = async (
24
- slug: string,
25
24
  product: ServerProductQueryQuery['product'],
26
25
  previewData: Locator
27
26
  ) => {
@@ -29,25 +28,19 @@ export const getPDP = async (
29
28
  const cmsData = JSON.parse(config.cms.data)
30
29
  const allPDPsFromCmsEnvData: PDPfromCmsEnvData[] = cmsData['pdp']
31
30
 
32
- return await getPDPFromCmsEnvData(
33
- `/${slug}/p`,
34
- product,
35
- allPDPsFromCmsEnvData,
36
- {
37
- ...(previewData?.contentType === 'pdp' ? previewData : null),
38
- contentType: 'pdp',
39
- }
40
- )
31
+ return await getPDPFromCmsEnvData(product, allPDPsFromCmsEnvData, {
32
+ ...(previewData?.contentType === 'pdp' ? previewData : null),
33
+ contentType: 'pdp',
34
+ })
41
35
  }
42
36
 
43
- return (await getPDPFromCms(`/${slug}/p`, product, {
37
+ return (await getPDPFromCms(product, {
44
38
  ...(previewData?.contentType === 'pdp' ? previewData : null),
45
39
  contentType: 'pdp',
46
40
  })) as PDPContentType
47
41
  }
48
42
 
49
43
  const getPDPFromCmsEnvData = async (
50
- slug: string,
51
44
  product: ServerProductQueryQuery['product'],
52
45
  allPDPsFromCMSData: PDPfromCmsEnvData[],
53
46
  options: Options
@@ -58,7 +51,7 @@ const getPDPFromCmsEnvData = async (
58
51
  throw new MissingContentError(options)
59
52
  }
60
53
 
61
- const template = findBestPDPTemplate(pages, slug, product)
54
+ const template = findBestPDPTemplate(pages, product)
62
55
 
63
56
  return getPage<PDPContentType>({
64
57
  contentType: 'pdp',
@@ -68,7 +61,6 @@ const getPDPFromCmsEnvData = async (
68
61
  }
69
62
 
70
63
  const getPDPFromCms = async (
71
- slug: string,
72
64
  product: ServerProductQueryQuery['product'],
73
65
  options: Options
74
66
  ): Promise<Partial<PDPContentType>> => {
@@ -78,5 +70,5 @@ const getPDPFromCms = async (
78
70
  throw new MissingContentError(options)
79
71
  }
80
72
 
81
- return findBestPDPTemplate(pages, slug, product)
73
+ return findBestPDPTemplate(pages, product)
82
74
  }
@@ -135,40 +135,53 @@ export function normalizePDPTemplate(templateValue: string) {
135
135
  return formattedValue.toLowerCase()
136
136
  }
137
137
 
138
+ // Returns an array of slugs without the number at the end ("-{number}/p") at each interaction if it matches, otherwise returns the slug as is.
139
+ const getSlugsWithoutSkuIdFromPDP = (slug: string) => {
140
+ const slugs = []
141
+ let currentSlug = slug
142
+ let match = currentSlug.match(/-\d+\/p$/)
143
+
144
+ while (match) {
145
+ const newSlug = currentSlug.replace(/-\d+\/p$/, '/p')
146
+ slugs.push(newSlug)
147
+ currentSlug = newSlug
148
+ match = currentSlug.match(/-\d+\/p$/)
149
+ }
150
+
151
+ return slugs
152
+ }
153
+
138
154
  /**
139
155
  * Find the best PDP template from the CMS based on the slug or in the product category tree.
140
156
  * Prioritizing the following order:
141
157
  *
142
- * 1. A PDP template that matches the page slug (e.g. slug = /apple-magic-mouse/p).
143
- * 2. A PDP template that matches the product subcategory (e.g. /department/category/subcategory).
144
- * 3. A PDP template that matches the product category (e.g. /department/category).
145
- * 4. A PDP template that matches the product department (e.g. /department).
146
- * 5. If no matches are found, use the generic PDP template.
158
+ * 1. A PDP template that matches the page slug with skuId (e.g. slug = /apple-magic-mouse-12345/p).
159
+ * 2. A PDP template that matches the page slug (e.g. slug = /apple-magic-mouse/p).
160
+ * 3. A PDP template that matches the product subcategory (e.g. /department/category/subcategory).
161
+ * 4. A PDP template that matches the product category (e.g. /department/category).
162
+ * 5. A PDP template that matches the product department (e.g. /department).
163
+ * 6. If no matches are found, use the generic PDP template.
147
164
  *
148
165
  * @param pages
149
- * @param originalSlug
150
166
  * @param product
151
- * @returns The best PDP template page for the slug
167
+ * @returns The best PDP template page for the product
152
168
  */
153
169
  export function findBestPDPTemplate(
154
170
  pages: Partial<PDPContentType>[],
155
- slug: string,
156
171
  product: ServerProductQueryQuery['product']
157
172
  ) {
158
- // productSlugAndCategoryTree with the prioritized order. [slug, subcategory tree, category tree, department]
159
- const productSlugAndCategoryTree = product?.breadcrumbList?.itemListElement
160
- ? [...product?.breadcrumbList?.itemListElement]
161
- .reverse()
162
- .map(({ item }) => item)
163
- : []
164
- productSlugAndCategoryTree.unshift(slug)
165
-
166
- for (const item of productSlugAndCategoryTree) {
173
+ const templateValues = getPDPTemplateValues({
174
+ itemListElement: product.breadcrumbList.itemListElement ?? [],
175
+ })
176
+
177
+ for (const template of templateValues) {
167
178
  for (const page of pages) {
168
179
  if (!page.settings?.template?.value) continue
169
180
 
170
- const templateValue = normalizePDPTemplate(page.settings.template.value)
171
- if (templateValue === item) {
181
+ const templateValueFromCms = normalizePDPTemplate(
182
+ page.settings.template.value
183
+ )
184
+ if (templateValueFromCms === template) {
172
185
  return page
173
186
  }
174
187
  }
@@ -176,3 +189,28 @@ export function findBestPDPTemplate(
176
189
 
177
190
  return pages.find((page) => !page.settings?.template?.value) || pages[0]
178
191
  }
192
+
193
+ export function getPDPTemplateValues({
194
+ itemListElement = [],
195
+ }: {
196
+ itemListElement: ServerProductQueryQuery['product']['breadcrumbList']['itemListElement']
197
+ }) {
198
+ // productSlugAndCategoryTree with the prioritized order. [link-skuId/p, subcategory tree, category tree, department]
199
+ const productSlugAndCategoryTree = [...itemListElement]
200
+ .reverse()
201
+ .map(({ item }) => item)
202
+
203
+ // PDP slug comes from FastStore API with the format `${link}-${skuId}/p`, the most specific for multiple page templates,
204
+ // so it should be the first element
205
+ const slugWithSkuId = productSlugAndCategoryTree[0]
206
+
207
+ // PDP slug without skuId `${link}/p`, should be the second element
208
+ const slugsWithoutSkuId = getSlugsWithoutSkuIdFromPDP(slugWithSkuId)
209
+
210
+ // removes duplicated and undefined
211
+ return [
212
+ slugWithSkuId,
213
+ ...slugsWithoutSkuId,
214
+ ...productSlugAndCategoryTree.slice(1),
215
+ ].filter((item, index, arr) => item && arr.indexOf(item) === index)
216
+ }
@@ -1,8 +1,27 @@
1
1
  import type { Rewrite, RewritesConfig } from '../../src/utils/multipleTemplates'
2
- import { hasRewritesConfigForSlug } from '../../src/utils/multipleTemplates'
2
+ import {
3
+ getPDPTemplateValues,
4
+ hasRewritesConfigForSlug,
5
+ } from '../../src/utils/multipleTemplates'
6
+
7
+ const productItemListElementMock = [
8
+ { item: '/department/', name: 'test', position: 1 }, // Department
9
+ { item: '/department/category/', name: 'test', position: 1 }, // Category tree
10
+ { item: '/department/category/subcategory/', name: 'test', position: 1 }, // Subcategory tree
11
+ {
12
+ item: '/department/category/subcategory/subcategory2/',
13
+ name: 'test',
14
+ position: 1,
15
+ }, // Subcategory tree tree
16
+ {
17
+ item: '/department/category/subcategory/subcategory2/subcategory3/',
18
+ name: 'test',
19
+ position: 1,
20
+ }, // Subcategory tree
21
+ ]
3
22
 
4
23
  describe('Multiple page templates', () => {
5
- describe('hasRewritesConfigForSlug', () => {
24
+ describe('PLP hasRewritesConfigForSlug', () => {
6
25
  it('should return true when rewrites is an array of objects containing the desired destination and source', () => {
7
26
  const rewritesArr: Rewrite[] = [
8
27
  { source: '/test/my-office', destination: '/office' },
@@ -63,4 +82,63 @@ describe('Multiple page templates', () => {
63
82
  expect(result).toBe(false)
64
83
  })
65
84
  })
85
+
86
+ describe('PDP getPDPTemplateValues', () => {
87
+ it('should return correct template values when productItemListElement first item has skuId and numbers', () => {
88
+ const mock = [
89
+ ...productItemListElementMock,
90
+ { item: '/slug-product-test-111111-2222/p', name: 'test', position: 1 },
91
+ ] // PDP slug with skuId and number
92
+ const result = getPDPTemplateValues({
93
+ itemListElement: mock,
94
+ })
95
+ expect(result).toEqual([
96
+ '/slug-product-test-111111-2222/p', // PDP slug with skuId
97
+ '/slug-product-test-111111/p', // PDP slug without skuId
98
+ '/slug-product-test/p', // PDP slug without number
99
+ '/department/category/subcategory/subcategory2/subcategory3/', // Subcategory tree
100
+ '/department/category/subcategory/subcategory2/', // Subcategory tree
101
+ '/department/category/subcategory/', // Subcategory
102
+ '/department/category/', // Category
103
+ '/department/', // Department
104
+ ])
105
+ })
106
+
107
+ it('should return correct template values when productItemListElement first item have skuId', () => {
108
+ const mock = [
109
+ ...productItemListElementMock,
110
+ { item: '/slug-product-test-111111/p', name: 'test', position: 1 },
111
+ ] // PDP slug with skuId
112
+ const result = getPDPTemplateValues({
113
+ itemListElement: mock,
114
+ })
115
+ expect(result).toEqual([
116
+ '/slug-product-test-111111/p', // PDP slug with skuId
117
+ '/slug-product-test/p', // PDP slug without number
118
+ '/department/category/subcategory/subcategory2/subcategory3/', // Subcategory tree
119
+ '/department/category/subcategory/subcategory2/', // Subcategory tree
120
+ '/department/category/subcategory/', // Subcategory
121
+ '/department/category/', // Category
122
+ '/department/', // Department
123
+ ])
124
+ })
125
+
126
+ it('should return correct template values productItemListElement first item do not have skuId and other number', () => {
127
+ const mock = [
128
+ ...productItemListElementMock,
129
+ { item: '/slug-product-test/p', name: 'test', position: 1 },
130
+ ] // PDP slug with skuId
131
+ const result = getPDPTemplateValues({
132
+ itemListElement: mock,
133
+ })
134
+ expect(result).toEqual([
135
+ '/slug-product-test/p', // PDP slug without skuId
136
+ '/department/category/subcategory/subcategory2/subcategory3/', // Subcategory tree
137
+ '/department/category/subcategory/subcategory2/', // Subcategory tree
138
+ '/department/category/subcategory/', // Subcategory
139
+ '/department/category/', // Category
140
+ '/department/', // Department
141
+ ])
142
+ })
143
+ })
66
144
  })