@salesforce/retail-react-app 1.0.0-preview.0

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 (425) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc.js +25 -0
  3. package/.prettierignore +4 -0
  4. package/.prettierrc.yaml +7 -0
  5. package/CHANGELOG.md +173 -0
  6. package/LICENSE +14 -0
  7. package/README.md +48 -0
  8. package/app/assets/svg/account.svg +3 -0
  9. package/app/assets/svg/alert.svg +3 -0
  10. package/app/assets/svg/basket.svg +3 -0
  11. package/app/assets/svg/brand-logo.svg +10 -0
  12. package/app/assets/svg/cc-amex.svg +7 -0
  13. package/app/assets/svg/cc-cvv.svg +8 -0
  14. package/app/assets/svg/cc-discover.svg +14 -0
  15. package/app/assets/svg/cc-mastercard.svg +8 -0
  16. package/app/assets/svg/cc-visa.svg +11 -0
  17. package/app/assets/svg/check-circle.svg +3 -0
  18. package/app/assets/svg/check.svg +3 -0
  19. package/app/assets/svg/chevron-down.svg +3 -0
  20. package/app/assets/svg/chevron-left.svg +3 -0
  21. package/app/assets/svg/chevron-right.svg +3 -0
  22. package/app/assets/svg/chevron-up.svg +3 -0
  23. package/app/assets/svg/close.svg +3 -0
  24. package/app/assets/svg/dashboard.svg +4 -0
  25. package/app/assets/svg/figma-logo.svg +14 -0
  26. package/app/assets/svg/file.svg +3 -0
  27. package/app/assets/svg/filter.svg +3 -0
  28. package/app/assets/svg/flag-ca.svg +5 -0
  29. package/app/assets/svg/flag-cn.svg +19 -0
  30. package/app/assets/svg/flag-fr.svg +19 -0
  31. package/app/assets/svg/flag-gb.svg +16 -0
  32. package/app/assets/svg/flag-it.svg +29 -0
  33. package/app/assets/svg/flag-jp.svg +10 -0
  34. package/app/assets/svg/flag-us.svg +7 -0
  35. package/app/assets/svg/github-logo.svg +40 -0
  36. package/app/assets/svg/hamburger.svg +8 -0
  37. package/app/assets/svg/heart-solid.svg +7 -0
  38. package/app/assets/svg/heart.svg +3 -0
  39. package/app/assets/svg/info.svg +3 -0
  40. package/app/assets/svg/like.svg +4 -0
  41. package/app/assets/svg/location.svg +3 -0
  42. package/app/assets/svg/lock.svg +3 -0
  43. package/app/assets/svg/paypal.svg +19 -0
  44. package/app/assets/svg/plug.svg +3 -0
  45. package/app/assets/svg/plus.svg +3 -0
  46. package/app/assets/svg/receipt.svg +3 -0
  47. package/app/assets/svg/search.svg +8 -0
  48. package/app/assets/svg/signout.svg +3 -0
  49. package/app/assets/svg/social-facebook.svg +3 -0
  50. package/app/assets/svg/social-instagram.svg +3 -0
  51. package/app/assets/svg/social-pinterest.svg +4 -0
  52. package/app/assets/svg/social-twitter.svg +3 -0
  53. package/app/assets/svg/social-youtube.svg +3 -0
  54. package/app/assets/svg/user.svg +3 -0
  55. package/app/assets/svg/visibility-off.svg +5 -0
  56. package/app/assets/svg/visibility.svg +3 -0
  57. package/app/components/_app/index.jsx +401 -0
  58. package/app/components/_app/index.test.js +85 -0
  59. package/app/components/_app/partials/above-header.jsx +10 -0
  60. package/app/components/_app-config/index.jsx +125 -0
  61. package/app/components/_app-config/index.test.js +77 -0
  62. package/app/components/_error/index.jsx +142 -0
  63. package/app/components/_error/index.test.js +25 -0
  64. package/app/components/action-card/index.jsx +75 -0
  65. package/app/components/address-display/index.jsx +30 -0
  66. package/app/components/basic-tile/index.jsx +65 -0
  67. package/app/components/basic-tile/index.test.js +23 -0
  68. package/app/components/breadcrumb/index.jsx +67 -0
  69. package/app/components/breadcrumb/index.test.js +30 -0
  70. package/app/components/confirmation-modal/index.jsx +111 -0
  71. package/app/components/confirmation-modal/index.test.js +98 -0
  72. package/app/components/drawer-menu/index.jsx +405 -0
  73. package/app/components/drawer-menu/index.test.js +33 -0
  74. package/app/components/dynamic-image/index.jsx +56 -0
  75. package/app/components/field/index.jsx +161 -0
  76. package/app/components/footer/index.jsx +269 -0
  77. package/app/components/footer/index.test.js +22 -0
  78. package/app/components/forms/address-fields.jsx +49 -0
  79. package/app/components/forms/credit-card-fields.jsx +149 -0
  80. package/app/components/forms/form-action-buttons.jsx +55 -0
  81. package/app/components/forms/login-fields.jsx +31 -0
  82. package/app/components/forms/password-requirements.jsx +99 -0
  83. package/app/components/forms/post-checkout-registration-fields.jsx +43 -0
  84. package/app/components/forms/profile-fields.jsx +36 -0
  85. package/app/components/forms/promo-code-fields.jsx +43 -0
  86. package/app/components/forms/registration-fields.jsx +42 -0
  87. package/app/components/forms/reset-password-fields.jsx +31 -0
  88. package/app/components/forms/state-province-options.jsx +75 -0
  89. package/app/components/forms/update-password-fields.jsx +49 -0
  90. package/app/components/forms/useAddressFields.jsx +196 -0
  91. package/app/components/forms/useCreditCardFields.jsx +146 -0
  92. package/app/components/forms/useLoginFields.jsx +52 -0
  93. package/app/components/forms/useProfileFields.jsx +95 -0
  94. package/app/components/forms/usePromoCodeFields.jsx +39 -0
  95. package/app/components/forms/useRegistrationFields.jsx +136 -0
  96. package/app/components/forms/useResetPasswordFields.jsx +40 -0
  97. package/app/components/forms/useUpdatePasswordFields.jsx +89 -0
  98. package/app/components/header/index.jsx +290 -0
  99. package/app/components/header/index.test.js +217 -0
  100. package/app/components/hero/index.jsx +84 -0
  101. package/app/components/hero/index.test.js +40 -0
  102. package/app/components/icons/index.jsx +158 -0
  103. package/app/components/icons/index.test.js +20 -0
  104. package/app/components/image-gallery/index.jsx +176 -0
  105. package/app/components/image-gallery/index.test.js +485 -0
  106. package/app/components/item-variant/index.jsx +33 -0
  107. package/app/components/item-variant/item-attributes.jsx +107 -0
  108. package/app/components/item-variant/item-image.jsx +73 -0
  109. package/app/components/item-variant/item-name.jsx +28 -0
  110. package/app/components/item-variant/item-price.jsx +117 -0
  111. package/app/components/link/index.jsx +32 -0
  112. package/app/components/link/index.test.js +72 -0
  113. package/app/components/links-list/index.jsx +89 -0
  114. package/app/components/links-list/index.test.js +62 -0
  115. package/app/components/list-menu/index.jsx +280 -0
  116. package/app/components/list-menu/index.test.js +44 -0
  117. package/app/components/loading-spinner/index.jsx +46 -0
  118. package/app/components/locale-selector/index.jsx +124 -0
  119. package/app/components/locale-selector/index.test.js +37 -0
  120. package/app/components/locale-text/index.jsx +97 -0
  121. package/app/components/locale-text/index.test.js +36 -0
  122. package/app/components/login/index.jsx +96 -0
  123. package/app/components/nested-accordion/index.jsx +185 -0
  124. package/app/components/nested-accordion/index.test.js +98 -0
  125. package/app/components/offline-banner/index.jsx +40 -0
  126. package/app/components/offline-banner/index.test.js +15 -0
  127. package/app/components/offline-boundary/index.jsx +104 -0
  128. package/app/components/offline-boundary/index.test.js +123 -0
  129. package/app/components/order-summary/index.jsx +331 -0
  130. package/app/components/page-action-placeholder/index.jsx +50 -0
  131. package/app/components/pagination/index.jsx +134 -0
  132. package/app/components/pagination/index.test.js +25 -0
  133. package/app/components/product-item/index.jsx +146 -0
  134. package/app/components/product-item/index.test.js +38 -0
  135. package/app/components/product-scroller/index.jsx +172 -0
  136. package/app/components/product-scroller/index.test.js +98 -0
  137. package/app/components/product-tile/index.jsx +195 -0
  138. package/app/components/product-tile/index.test.js +96 -0
  139. package/app/components/product-view/index.jsx +538 -0
  140. package/app/components/product-view/index.test.js +224 -0
  141. package/app/components/product-view-modal/index.jsx +48 -0
  142. package/app/components/product-view-modal/index.test.js +72 -0
  143. package/app/components/promo-code/index.jsx +162 -0
  144. package/app/components/promo-popover/index.jsx +83 -0
  145. package/app/components/quantity-picker/index.jsx +58 -0
  146. package/app/components/radio-card/index.jsx +75 -0
  147. package/app/components/recommended-products/index.jsx +227 -0
  148. package/app/components/register/index.jsx +114 -0
  149. package/app/components/reset-password/index.jsx +87 -0
  150. package/app/components/responsive/index.jsx +29 -0
  151. package/app/components/scroll-to-top/index.jsx +24 -0
  152. package/app/components/scroll-to-top/index.test.js +46 -0
  153. package/app/components/search/index.jsx +279 -0
  154. package/app/components/search/index.test.js +127 -0
  155. package/app/components/search/partials/recent-searches.jsx +76 -0
  156. package/app/components/search/partials/search-suggestions.jsx +45 -0
  157. package/app/components/search/partials/suggestions.jsx +43 -0
  158. package/app/components/section/index.jsx +68 -0
  159. package/app/components/seo/index.jsx +33 -0
  160. package/app/components/social-icons/index.jsx +101 -0
  161. package/app/components/social-icons/index.test.js +30 -0
  162. package/app/components/swatch-group/index.jsx +77 -0
  163. package/app/components/swatch-group/index.test.js +136 -0
  164. package/app/components/swatch-group/swatch.jsx +94 -0
  165. package/app/components/toggle-card/index.jsx +97 -0
  166. package/app/components/with-registration/index.jsx +58 -0
  167. package/app/components/with-registration/index.test.js +85 -0
  168. package/app/constants.js +109 -0
  169. package/app/contexts/index.js +92 -0
  170. package/app/hooks/einstein-mock-data.js +916 -0
  171. package/app/hooks/index.js +17 -0
  172. package/app/hooks/use-add-to-cart-modal.js +344 -0
  173. package/app/hooks/use-add-to-cart-modal.test.js +625 -0
  174. package/app/hooks/use-auth-modal.js +337 -0
  175. package/app/hooks/use-auth-modal.test.js +365 -0
  176. package/app/hooks/use-currency.js +20 -0
  177. package/app/hooks/use-currency.test.js +41 -0
  178. package/app/hooks/use-current-basket.js +39 -0
  179. package/app/hooks/use-current-customer.js +29 -0
  180. package/app/hooks/use-derived-product.js +77 -0
  181. package/app/hooks/use-derived-product.test.js +69 -0
  182. package/app/hooks/use-einstein.js +512 -0
  183. package/app/hooks/use-einstein.test.js +224 -0
  184. package/app/hooks/use-intersection-observer.js +64 -0
  185. package/app/hooks/use-limit-urls.js +31 -0
  186. package/app/hooks/use-limit-urls.test.js +40 -0
  187. package/app/hooks/use-multi-site.js +36 -0
  188. package/app/hooks/use-multi-site.test.js +53 -0
  189. package/app/hooks/use-navigation.js +37 -0
  190. package/app/hooks/use-navigation.test.js +109 -0
  191. package/app/hooks/use-page-urls.js +35 -0
  192. package/app/hooks/use-page-urls.test.js +39 -0
  193. package/app/hooks/use-pdp-search-params.js +16 -0
  194. package/app/hooks/use-pdp-search-params.test.js +52 -0
  195. package/app/hooks/use-previous.js +17 -0
  196. package/app/hooks/use-product-view-modal.js +93 -0
  197. package/app/hooks/use-product-view-modal.test.js +172 -0
  198. package/app/hooks/use-search-params.js +96 -0
  199. package/app/hooks/use-search-params.test.js +91 -0
  200. package/app/hooks/use-sort-urls.js +33 -0
  201. package/app/hooks/use-sort-urls.test.js +42 -0
  202. package/app/hooks/use-toast.js +68 -0
  203. package/app/hooks/use-toast.test.js +58 -0
  204. package/app/hooks/use-variant.js +32 -0
  205. package/app/hooks/use-variant.test.js +81 -0
  206. package/app/hooks/use-variation-attributes.js +138 -0
  207. package/app/hooks/use-variation-attributes.test.js +119 -0
  208. package/app/hooks/use-variation-params.js +31 -0
  209. package/app/hooks/use-variation-params.test.js +73 -0
  210. package/app/hooks/use-wish-list.js +42 -0
  211. package/app/main.jsx +14 -0
  212. package/app/mocks/basket-with-suit.js +146 -0
  213. package/app/mocks/empty-basket.js +39 -0
  214. package/app/mocks/mock-data.js +5632 -0
  215. package/app/mocks/product-set-winter-lookM.js +1224 -0
  216. package/app/mocks/searchResults.js +144 -0
  217. package/app/mocks/variant-750518699578M.js +434 -0
  218. package/app/page-designer/README.md +102 -0
  219. package/app/page-designer/assets/image-tile/index.jsx +51 -0
  220. package/app/page-designer/assets/image-tile/index.test.js +30 -0
  221. package/app/page-designer/assets/image-with-text/index.jsx +140 -0
  222. package/app/page-designer/assets/image-with-text/index.test.js +38 -0
  223. package/app/page-designer/assets/index.js +9 -0
  224. package/app/page-designer/index.js +10 -0
  225. package/app/page-designer/layouts/carousel/index.jsx +222 -0
  226. package/app/page-designer/layouts/carousel/index.test.js +43 -0
  227. package/app/page-designer/layouts/index.js +14 -0
  228. package/app/page-designer/layouts/mobileGrid1r1c/index.jsx +36 -0
  229. package/app/page-designer/layouts/mobileGrid1r1c/index.test.js +35 -0
  230. package/app/page-designer/layouts/mobileGrid2r1c/index.jsx +37 -0
  231. package/app/page-designer/layouts/mobileGrid2r1c/index.test.js +47 -0
  232. package/app/page-designer/layouts/mobileGrid2r2c/index.jsx +37 -0
  233. package/app/page-designer/layouts/mobileGrid2r2c/index.test.js +71 -0
  234. package/app/page-designer/layouts/mobileGrid2r3c/index.jsx +37 -0
  235. package/app/page-designer/layouts/mobileGrid2r3c/index.test.js +95 -0
  236. package/app/page-designer/layouts/mobileGrid3r1c/index.jsx +37 -0
  237. package/app/page-designer/layouts/mobileGrid3r1c/index.test.js +59 -0
  238. package/app/page-designer/layouts/mobileGrid3r2c/index.jsx +37 -0
  239. package/app/page-designer/layouts/mobileGrid3r2c/index.test.js +95 -0
  240. package/app/page-designer/utils.js +14 -0
  241. package/app/pages/account/addresses.jsx +382 -0
  242. package/app/pages/account/addresses.test.js +120 -0
  243. package/app/pages/account/constant.js +57 -0
  244. package/app/pages/account/index.jsx +237 -0
  245. package/app/pages/account/index.test.js +188 -0
  246. package/app/pages/account/order-detail.jsx +397 -0
  247. package/app/pages/account/order-history.jsx +264 -0
  248. package/app/pages/account/orders.jsx +30 -0
  249. package/app/pages/account/orders.test.js +95 -0
  250. package/app/pages/account/profile.jsx +357 -0
  251. package/app/pages/account/wishlist/index.jsx +195 -0
  252. package/app/pages/account/wishlist/index.mock.js +1481 -0
  253. package/app/pages/account/wishlist/index.test.js +56 -0
  254. package/app/pages/account/wishlist/partials/wishlist-primary-action.jsx +170 -0
  255. package/app/pages/account/wishlist/partials/wishlist-primary-action.mock.js +1623 -0
  256. package/app/pages/account/wishlist/partials/wishlist-primary-action.test.js +99 -0
  257. package/app/pages/account/wishlist/partials/wishlist-secondary-button-group.jsx +120 -0
  258. package/app/pages/account/wishlist/partials/wishlist-secondary-button-group.test.js +391 -0
  259. package/app/pages/cart/index.jsx +476 -0
  260. package/app/pages/cart/index.test.js +481 -0
  261. package/app/pages/cart/partials/cart-cta.jsx +46 -0
  262. package/app/pages/cart/partials/cart-secondary-button-group.jsx +135 -0
  263. package/app/pages/cart/partials/cart-secondary-button-group.test.js +103 -0
  264. package/app/pages/cart/partials/cart-skeleton.jsx +93 -0
  265. package/app/pages/cart/partials/cart-title.jsx +27 -0
  266. package/app/pages/cart/partials/empty-cart.jsx +86 -0
  267. package/app/pages/checkout/confirmation.jsx +541 -0
  268. package/app/pages/checkout/confirmation.mock.js +450 -0
  269. package/app/pages/checkout/confirmation.test.js +114 -0
  270. package/app/pages/checkout/index.jsx +169 -0
  271. package/app/pages/checkout/index.test.js +582 -0
  272. package/app/pages/checkout/partials/cc-radio-group.jsx +122 -0
  273. package/app/pages/checkout/partials/checkout-footer.jsx +140 -0
  274. package/app/pages/checkout/partials/checkout-footer.test.js +16 -0
  275. package/app/pages/checkout/partials/checkout-header.jsx +54 -0
  276. package/app/pages/checkout/partials/checkout-header.test.js +16 -0
  277. package/app/pages/checkout/partials/checkout-skeleton.jsx +52 -0
  278. package/app/pages/checkout/partials/contact-info.jsx +251 -0
  279. package/app/pages/checkout/partials/contact-info.test.js +43 -0
  280. package/app/pages/checkout/partials/payment-form.jsx +97 -0
  281. package/app/pages/checkout/partials/payment.jsx +276 -0
  282. package/app/pages/checkout/partials/shipping-address-selection.jsx +377 -0
  283. package/app/pages/checkout/partials/shipping-address.jsx +132 -0
  284. package/app/pages/checkout/partials/shipping-options.jsx +232 -0
  285. package/app/pages/checkout/util/checkout-context.js +94 -0
  286. package/app/pages/home/data.js +134 -0
  287. package/app/pages/home/index.jsx +301 -0
  288. package/app/pages/home/index.test.js +23 -0
  289. package/app/pages/login/index.jsx +123 -0
  290. package/app/pages/login/index.test.js +229 -0
  291. package/app/pages/login-redirect/index.jsx +23 -0
  292. package/app/pages/login-redirect/index.test.js +16 -0
  293. package/app/pages/page-not-found/index.jsx +90 -0
  294. package/app/pages/page-not-found/index.test.js +31 -0
  295. package/app/pages/product-detail/index.jsx +394 -0
  296. package/app/pages/product-detail/index.mock.js +197 -0
  297. package/app/pages/product-detail/index.test.js +162 -0
  298. package/app/pages/product-detail/partials/information-accordion.jsx +121 -0
  299. package/app/pages/product-list/index.jsx +735 -0
  300. package/app/pages/product-list/index.test.js +180 -0
  301. package/app/pages/product-list/partials/above-page-header.jsx +10 -0
  302. package/app/pages/product-list/partials/checkbox-refinements.jsx +41 -0
  303. package/app/pages/product-list/partials/checkbox-refinements.test.js +53 -0
  304. package/app/pages/product-list/partials/color-refinements.jsx +88 -0
  305. package/app/pages/product-list/partials/empty-results.jsx +118 -0
  306. package/app/pages/product-list/partials/link-refinements.jsx +38 -0
  307. package/app/pages/product-list/partials/page-header.jsx +42 -0
  308. package/app/pages/product-list/partials/radio-refinements.jsx +60 -0
  309. package/app/pages/product-list/partials/refinements.jsx +144 -0
  310. package/app/pages/product-list/partials/selected-refinements.jsx +100 -0
  311. package/app/pages/product-list/partials/size-refinements.jsx +55 -0
  312. package/app/pages/registration/index.jsx +87 -0
  313. package/app/pages/registration/index.test.jsx +132 -0
  314. package/app/pages/reset-password/index.jsx +112 -0
  315. package/app/pages/reset-password/index.test.jsx +141 -0
  316. package/app/request-processor.js +118 -0
  317. package/app/request-processor.test.js +23 -0
  318. package/app/routes.jsx +111 -0
  319. package/app/routes.test.js +13 -0
  320. package/app/ssr.js +70 -0
  321. package/app/static/ico/favicon.ico +0 -0
  322. package/app/static/img/global/app-icon-192.png +0 -0
  323. package/app/static/img/global/app-icon-512.png +0 -0
  324. package/app/static/img/global/apple-touch-icon.png +0 -0
  325. package/app/static/img/hero.png +0 -0
  326. package/app/static/manifest.json +19 -0
  327. package/app/static/robots.txt +2 -0
  328. package/app/theme/components/base/accordion.js +21 -0
  329. package/app/theme/components/base/alert.js +17 -0
  330. package/app/theme/components/base/badge.js +25 -0
  331. package/app/theme/components/base/button.js +77 -0
  332. package/app/theme/components/base/checkbox.js +30 -0
  333. package/app/theme/components/base/container.js +17 -0
  334. package/app/theme/components/base/drawer.js +26 -0
  335. package/app/theme/components/base/formLabel.js +13 -0
  336. package/app/theme/components/base/icon.js +13 -0
  337. package/app/theme/components/base/input.js +44 -0
  338. package/app/theme/components/base/modal.js +11 -0
  339. package/app/theme/components/base/popover.js +61 -0
  340. package/app/theme/components/base/radio.js +33 -0
  341. package/app/theme/components/base/select.js +15 -0
  342. package/app/theme/components/base/skeleton.js +12 -0
  343. package/app/theme/components/base/tooltip.js +19 -0
  344. package/app/theme/components/project/_app.js +25 -0
  345. package/app/theme/components/project/breadcrumb.js +25 -0
  346. package/app/theme/components/project/checkout-footer.js +35 -0
  347. package/app/theme/components/project/drawer-menu.js +66 -0
  348. package/app/theme/components/project/footer.js +84 -0
  349. package/app/theme/components/project/header.js +84 -0
  350. package/app/theme/components/project/image-gallery.js +59 -0
  351. package/app/theme/components/project/links-list.js +43 -0
  352. package/app/theme/components/project/list-menu.js +91 -0
  353. package/app/theme/components/project/locale-selector.js +42 -0
  354. package/app/theme/components/project/nested-accordion.js +26 -0
  355. package/app/theme/components/project/offline-banner.js +25 -0
  356. package/app/theme/components/project/pagination.js +22 -0
  357. package/app/theme/components/project/product-tile.js +32 -0
  358. package/app/theme/components/project/social-icons.js +52 -0
  359. package/app/theme/components/project/swatch-group.js +115 -0
  360. package/app/theme/foundations/colors.js +170 -0
  361. package/app/theme/foundations/gradients.js +9 -0
  362. package/app/theme/foundations/layerStyles.js +41 -0
  363. package/app/theme/foundations/shadows.js +9 -0
  364. package/app/theme/foundations/sizes.js +18 -0
  365. package/app/theme/foundations/space.js +9 -0
  366. package/app/theme/foundations/styles.js +21 -0
  367. package/app/theme/index.js +104 -0
  368. package/app/utils/cc-utils.js +112 -0
  369. package/app/utils/cc-utils.test.js +41 -0
  370. package/app/utils/image-groups-utils.js +62 -0
  371. package/app/utils/image-groups-utils.test.js +65 -0
  372. package/app/utils/locale.js +78 -0
  373. package/app/utils/locale.test.js +112 -0
  374. package/app/utils/password-utils.js +21 -0
  375. package/app/utils/phone-utils.js +22 -0
  376. package/app/utils/phone-utils.test.js +15 -0
  377. package/app/utils/product-utils.js +35 -0
  378. package/app/utils/product-utils.test.js +51 -0
  379. package/app/utils/responsive-image.js +198 -0
  380. package/app/utils/responsive-image.test.js +170 -0
  381. package/app/utils/routes-utils.js +111 -0
  382. package/app/utils/routes-utils.test.js +291 -0
  383. package/app/utils/site-utils.js +222 -0
  384. package/app/utils/site-utils.test.js +376 -0
  385. package/app/utils/test-utils.js +257 -0
  386. package/app/utils/url.js +291 -0
  387. package/app/utils/url.test.js +421 -0
  388. package/app/utils/utils.js +201 -0
  389. package/app/utils/utils.test.js +182 -0
  390. package/babel.config.js +7 -0
  391. package/cache-hash-config.json +8 -0
  392. package/config/default.js +64 -0
  393. package/config/mocks/default.js +131 -0
  394. package/config/sites.js +78 -0
  395. package/jest-setup.js +191 -0
  396. package/jest.config.js +50 -0
  397. package/jsconfig.json +13 -0
  398. package/package.json +105 -0
  399. package/scripts/extract-default-messages.js +92 -0
  400. package/tests/lighthouserc.js +37 -0
  401. package/translations/README.md +127 -0
  402. package/translations/compiled/de-DE.json +3212 -0
  403. package/translations/compiled/en-GB.json +3212 -0
  404. package/translations/compiled/en-US.json +3212 -0
  405. package/translations/compiled/en-XA.json +6948 -0
  406. package/translations/compiled/es-MX.json +3216 -0
  407. package/translations/compiled/fr-FR.json +3216 -0
  408. package/translations/compiled/it-IT.json +3188 -0
  409. package/translations/compiled/ja-JP.json +3200 -0
  410. package/translations/compiled/ko-KR.json +3180 -0
  411. package/translations/compiled/pt-BR.json +3220 -0
  412. package/translations/compiled/zh-CN.json +3212 -0
  413. package/translations/compiled/zh-TW.json +3208 -0
  414. package/translations/de-DE.json +1417 -0
  415. package/translations/en-GB.json +1417 -0
  416. package/translations/en-US.json +1417 -0
  417. package/translations/es-MX.json +1417 -0
  418. package/translations/fr-FR.json +1417 -0
  419. package/translations/it-IT.json +1417 -0
  420. package/translations/ja-JP.json +1417 -0
  421. package/translations/ko-KR.json +1417 -0
  422. package/translations/pt-BR.json +1417 -0
  423. package/translations/zh-CN.json +1417 -0
  424. package/translations/zh-TW.json +1417 -0
  425. package/worker/main.js +36 -0
@@ -0,0 +1,15 @@
1
+ /*
2
+ * Copyright (c) 2021, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+ import * as utils from '@salesforce/retail-react-app/app/utils/phone-utils'
8
+
9
+ test('formats a phone number', () => {
10
+ expect(utils.formatPhoneNumber()).toBeUndefined()
11
+ expect(utils.formatPhoneNumber('')).toBe('')
12
+ expect(utils.formatPhoneNumber('727')).toBe('727')
13
+ expect(utils.formatPhoneNumber('727555')).toBe('(727) 555')
14
+ expect(utils.formatPhoneNumber('7275551234')).toBe('(727) 555-1234')
15
+ })
@@ -0,0 +1,35 @@
1
+ /*
2
+ * Copyright (c) 2023, Salesforce, Inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+
8
+ /**
9
+ * Get the human-friendly version of the variation values that users have selected.
10
+ * Useful for displaying these values in the UI.
11
+ *
12
+ * @param {Object} variationAttributes - The products variation attributes.
13
+ * @param {Object} values - The variations selected attribute values.
14
+ * @returns {Object} - A key value map of the display name and display value.
15
+ *
16
+ * @example
17
+ * const displayValues = getDisplayVariationValues(
18
+ * [ { "id": "color", "name": "Colour", "values": [ { "name": "royal", "orderable": true, "value": "JJ5FUXX" } ] } ],
19
+ * { "color": "JJ5FUXX" }
20
+ * )
21
+ * // returns { "Colour": "royal" }
22
+ */
23
+ export const getDisplayVariationValues = (variationAttributes, values = {}) => {
24
+ const returnVal = Object.entries(values).reduce((acc, [id, value]) => {
25
+ const attribute = variationAttributes.find(({id: attributeId}) => attributeId === id)
26
+ const attributeValue = attribute.values.find(
27
+ ({value: attributeValue}) => attributeValue === value
28
+ )
29
+ return {
30
+ ...acc,
31
+ [attribute.name]: attributeValue.name
32
+ }
33
+ }, {})
34
+ return returnVal
35
+ }
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Copyright (c) 2023, Salesforce, Inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+
8
+ import {getDisplayVariationValues} from '@salesforce/retail-react-app/app/utils/product-utils'
9
+
10
+ const variationAttributes = [
11
+ {
12
+ id: 'color',
13
+ name: 'Colour',
14
+ values: [
15
+ {name: 'Black', orderable: true, value: 'BLACKLE'},
16
+ {name: 'Taupe', orderable: true, value: 'TAUPETX'}
17
+ ]
18
+ },
19
+ {
20
+ id: 'size',
21
+ name: 'Size',
22
+ values: [
23
+ {name: '6', orderable: true, value: '060'},
24
+ {name: '6.5', orderable: true, value: '065'},
25
+ {name: '7', orderable: true, value: '070'},
26
+ {name: '7.5', orderable: true, value: '075'},
27
+ {name: '8', orderable: true, value: '080'},
28
+ {name: '8.5', orderable: true, value: '085'},
29
+ {name: '9', orderable: true, value: '090'},
30
+ {name: '9.5', orderable: true, value: '095'},
31
+ {name: '10', orderable: true, value: '100'},
32
+ {name: '11', orderable: true, value: '110'}
33
+ ]
34
+ },
35
+ {id: 'width', name: 'Width', values: [{name: 'M', orderable: true, value: 'M'}]}
36
+ ]
37
+
38
+ test('getDisplayVariationValues', () => {
39
+ const selectedValues = {
40
+ color: 'TAUPETX',
41
+ size: '065',
42
+ width: 'M'
43
+ }
44
+ const result = getDisplayVariationValues(variationAttributes, selectedValues)
45
+
46
+ expect(result).toEqual({
47
+ Colour: 'Taupe',
48
+ Size: '6.5',
49
+ Width: 'M'
50
+ })
51
+ })
@@ -0,0 +1,198 @@
1
+ /*
2
+ * Copyright (c) 2021, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+
8
+ import theme from '@chakra-ui/theme'
9
+
10
+ /**
11
+ * @param {Object} breakpoints
12
+ * @return {string[]} Breakpoint labels ordered from smallest. For example: ['base', 'sm', 'md', 'lg', 'xl', '2xl']
13
+ */
14
+ const getBreakpointLabels = (breakpoints) =>
15
+ Object.entries(breakpoints)
16
+ .sort((a, b) => parseFloat(a[1]) - parseFloat(b[1]))
17
+ .map(([key]) => key)
18
+
19
+ const {breakpoints: defaultBreakpoints} = theme
20
+ let themeBreakpoints = defaultBreakpoints
21
+ let breakpointLabels = getBreakpointLabels(themeBreakpoints)
22
+
23
+ /**
24
+ * @param {Object} props
25
+ * @param {string} props.src - Dynamic src having an optional param that can vary with widths. For example: `image[_{width}].jpg` or `image.jpg[?sw={width}&q=60]`
26
+ * @param {(number[]|string[]|Object)} [props.widths] - Image widths relative to the breakpoints, whose units can either be px or vw or unit-less. They will be mapped to the corresponding `sizes` and `srcSet`.
27
+ * @param {Object} [props.breakpoints] - The current theme's breakpoints. If not given, Chakra's default breakpoints will be used.
28
+ * @return {Object} src, sizes, and srcSet props for your image component
29
+ */
30
+ export const getResponsiveImageAttributes = ({src, widths, breakpoints = defaultBreakpoints}) => {
31
+ if (!widths) {
32
+ return {
33
+ src: getSrcWithoutOptionalParams(src)
34
+ }
35
+ }
36
+
37
+ themeBreakpoints = breakpoints
38
+ breakpointLabels = getBreakpointLabels(themeBreakpoints)
39
+
40
+ return {
41
+ src: getSrcWithoutOptionalParams(src),
42
+ sizes: mapWidthsToSizes(widths),
43
+ srcSet: mapWidthsToSrcSet(widths, src)
44
+ }
45
+ }
46
+
47
+ /**
48
+ * @param {(number[]|string[]|Object)} widths
49
+ * @return {string}
50
+ */
51
+ const mapWidthsToSizes = (widths) => {
52
+ const _widths = withUnit(Array.isArray(widths) ? widths : widthsAsArray(widths))
53
+
54
+ return breakpointLabels
55
+ .slice(0, _widths.length)
56
+ .map((bp, i) => {
57
+ return i === 0 ? _widths[i] : `(min-width: ${themeBreakpoints[bp]}) ${_widths[i]}`
58
+ })
59
+ .reverse()
60
+ .join(', ')
61
+ }
62
+
63
+ /**
64
+ * @param {(number[]|string[]|Object)} widths
65
+ * @return {string}
66
+ */
67
+ const mapWidthsToSrcSet = (widths, dynamicSrc) => {
68
+ let _widths = isObject(widths) ? widthsAsArray(widths) : widths.slice(0)
69
+
70
+ if (_widths.length < breakpointLabels.length) {
71
+ const lastWidth = _widths[_widths.length - 1]
72
+ const amountToPad = breakpointLabels.length - _widths.length
73
+
74
+ _widths = [..._widths, ...Array(amountToPad).fill(lastWidth)]
75
+ }
76
+
77
+ _widths = uniqueArray(convertToPxNumbers(_widths)).sort()
78
+
79
+ const srcSet = []
80
+ _widths.forEach((width) => {
81
+ srcSet.push(width)
82
+ srcSet.push(width * 2) // for devices with higher pixel density
83
+ })
84
+
85
+ return srcSet.map((imageWidth) => `${getSrc(dynamicSrc, imageWidth)} ${imageWidth}w`).join(', ')
86
+ }
87
+
88
+ const vwValue = /^\d+vw$/
89
+ const pxValue = /^\d+px$/
90
+
91
+ /**
92
+ * @param {string[]|number[]} widths
93
+ * @return {number[]}
94
+ */
95
+ const convertToPxNumbers = (widths) => {
96
+ return widths
97
+ .map((width, i) => {
98
+ if (typeof width === 'number') {
99
+ return width
100
+ }
101
+
102
+ if (vwValue.test(width)) {
103
+ const vw = parseFloat(width)
104
+ const currentBp = breakpointLabels[i]
105
+ // We imagine the biggest image for the current breakpoint
106
+ // to be when the viewport is closely approaching the _next breakpoint_.
107
+ const nextBp = breakpointLabels[i + 1]
108
+
109
+ if (nextBp) {
110
+ return vwToPx(vw, nextBp)
111
+ } else {
112
+ // We're already at the last breakpoint
113
+ return widths[i] !== widths[i - 1] ? vwToPx(vw, currentBp) : undefined
114
+ }
115
+ } else if (pxValue.test(width)) {
116
+ return parseInt(width)
117
+ } else {
118
+ console.error('Expecting to see values with vw or px unit only')
119
+ return 0
120
+ }
121
+ })
122
+ .filter((width) => width !== undefined)
123
+ }
124
+
125
+ const uniqueArray = (array) => [...new Set(array)]
126
+
127
+ /**
128
+ * @param {(number[]|string[])} widths
129
+ */
130
+ const withUnit = (widths) =>
131
+ // By default, unitless value is interpreted as px
132
+ widths.map((width) => (typeof width === 'number' ? `${width}px` : width))
133
+
134
+ const isObject = (o) => o?.constructor === Object
135
+
136
+ /**
137
+ * @param {Object} widths
138
+ * @example
139
+ * // returns the array [10, 10, 10, 50]
140
+ * widthsAsArray({base: 10, lg: 50})
141
+ */
142
+ const widthsAsArray = (widths) => {
143
+ const biggestBreakpoint = breakpointLabels.filter((bp) => Boolean(widths[bp])).pop()
144
+
145
+ let mostRecent
146
+ return breakpointLabels.slice(0, breakpointLabels.indexOf(biggestBreakpoint) + 1).map((bp) => {
147
+ if (widths[bp]) {
148
+ mostRecent = widths[bp]
149
+ return widths[bp]
150
+ } else {
151
+ return mostRecent
152
+ }
153
+ })
154
+ }
155
+
156
+ /**
157
+ * @param {number} vw
158
+ * @param {string} breakpoint
159
+ */
160
+ const vwToPx = (vw, breakpoint) => {
161
+ let result = (vw / 100) * parseFloat(themeBreakpoints[breakpoint])
162
+ const breakpointsDefinedInPx = Object.values(themeBreakpoints).some((val) => pxValue.test(val))
163
+
164
+ // Assumes theme's breakpoints are defined in either em or px
165
+ // See https://chakra-ui.com/docs/features/responsive-styles#customizing-breakpoints
166
+ return breakpointsDefinedInPx ? result : emToPx(result)
167
+ }
168
+
169
+ /**
170
+ * @param {number} em
171
+ * @param {number} [browserDefaultFontSize]
172
+ */
173
+ const emToPx = (em, browserDefaultFontSize = 16) => Math.round(em * browserDefaultFontSize)
174
+
175
+ /**
176
+ * @param {string} dynamicSrc
177
+ * @param {number} imageWidth
178
+ * @return {string} Image url having the given width
179
+ * @example
180
+ * // returns https://example.com/image_720.jpg
181
+ * getSrc('https://example.com/image[_{width}].jpg', 720)
182
+ */
183
+ export const getSrc = (dynamicSrc, imageWidth) => {
184
+ // 1. remove the surrounding []
185
+ // 2. replace {...} with imageWidth
186
+ return dynamicSrc.replace(/\[([^\]]+)\]/g, '$1').replace(/\{[^}]+\}/g, imageWidth)
187
+ }
188
+
189
+ /**
190
+ * @param {string} dynamicSrc
191
+ * @example
192
+ * // Returns 'https://example.com/image.jpg'
193
+ * getSrcWithoutOptionalParams('https://example.com/image.jpg[?sw={width}]')
194
+ */
195
+ const getSrcWithoutOptionalParams = (dynamicSrc) => {
196
+ const optionalParams = /\[[^\]]+\]/g
197
+ return dynamicSrc.replace(optionalParams, '')
198
+ }
@@ -0,0 +1,170 @@
1
+ /*
2
+ * Copyright (c) 2021, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+
8
+ import {
9
+ getResponsiveImageAttributes,
10
+ getSrc
11
+ } from '@salesforce/retail-react-app/app/utils/responsive-image'
12
+
13
+ const disImageURL = {
14
+ withOptionalParams:
15
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1e4fcb17/images/large/PG.10212867.JJ3XYXX.PZ.jpg[?sw={width}&q=60]',
16
+ withoutOptionalParams:
17
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw1e4fcb17/images/large/PG.10212867.JJ3XYXX.PZ.jpg'
18
+ }
19
+
20
+ const buildSrcSet = (pxWidths) => {
21
+ const uniqueArray = [...new Set(pxWidths)]
22
+ const widths = uniqueArray.sort()
23
+
24
+ return widths
25
+ .map((width) => {
26
+ return `${urlWithWidth(width)} ${width}w, ${urlWithWidth(width * 2)} ${width * 2}w`
27
+ })
28
+ .join(', ')
29
+ }
30
+ const urlWithWidth = (width) => getSrc(disImageURL.withOptionalParams, width)
31
+
32
+ test('vw widths', () => {
33
+ let props = getResponsiveImageAttributes({
34
+ src: disImageURL.withOptionalParams,
35
+ widths: ['100vw', '100vw', '50vw']
36
+ })
37
+
38
+ // Breakpoints
39
+ // sm: "30em",
40
+ // md: "48em",
41
+ // lg: "62em",
42
+ // xl: "80em",
43
+ // "2xl": "96em",
44
+
45
+ // 100vw of sm => 30em => 30 * 16 = 480px
46
+ // 100vw of md => 48em => 768px
47
+ // 50vw of lg => 31em => 496px
48
+ // 50vw of xl => 40em => 640
49
+ // 50vw of 2xl => 48em => 768px
50
+
51
+ expect(props).toStrictEqual({
52
+ src: disImageURL.withoutOptionalParams,
53
+ sizes: '(min-width: 48em) 50vw, (min-width: 30em) 100vw, 100vw',
54
+ srcSet: buildSrcSet([480, 768, 496, 640, 768])
55
+ })
56
+
57
+ // This time as _object_
58
+ props = getResponsiveImageAttributes({
59
+ src: disImageURL.withOptionalParams,
60
+ widths: {
61
+ base: '100vw',
62
+ sm: '100vw',
63
+ md: '50vw'
64
+ }
65
+ })
66
+ expect(props).toStrictEqual({
67
+ src: disImageURL.withoutOptionalParams,
68
+ sizes: '(min-width: 48em) 50vw, (min-width: 30em) 100vw, 100vw',
69
+ srcSet: buildSrcSet([480, 768, 496, 640, 768])
70
+ })
71
+
72
+ // Edge case: testing changing width at the very last breakpoint (2xl)
73
+ props = getResponsiveImageAttributes({
74
+ src: disImageURL.withOptionalParams,
75
+ widths: {
76
+ base: '100vw',
77
+ '2xl': '50vw'
78
+ }
79
+ })
80
+ // 100vw of sm => 30em => 30 * 16 = 480px
81
+ // 100vw of md => 48em => 768px
82
+ // 100vw of lg => 62em => 992px
83
+ // 100vw of xl => 80em => 1280px
84
+ // 100vw of 2xl => 96em => 1536px
85
+ // 50vw of 2xl => 48em => 768px
86
+ expect(props).toStrictEqual({
87
+ src: disImageURL.withoutOptionalParams,
88
+ sizes: '(min-width: 96em) 50vw, (min-width: 80em) 100vw, (min-width: 62em) 100vw, (min-width: 48em) 100vw, (min-width: 30em) 100vw, 100vw',
89
+ srcSet: buildSrcSet([480, 768, 992, 1280, 1536, 768])
90
+ })
91
+ })
92
+
93
+ test('px values', () => {
94
+ // widths in array format
95
+ let props = getResponsiveImageAttributes({
96
+ src: disImageURL.withOptionalParams,
97
+ widths: [100, 500, 1000]
98
+ })
99
+ expect(props).toStrictEqual({
100
+ src: disImageURL.withoutOptionalParams,
101
+ sizes: '(min-width: 48em) 1000px, (min-width: 30em) 500px, 100px',
102
+ srcSet: buildSrcSet([100, 500, 1000])
103
+ })
104
+
105
+ // widths in object format
106
+ props = getResponsiveImageAttributes({
107
+ src: disImageURL.withOptionalParams,
108
+ widths: {
109
+ base: 100,
110
+ sm: 500,
111
+ md: 1000
112
+ }
113
+ })
114
+ expect(props).toStrictEqual({
115
+ src: disImageURL.withoutOptionalParams,
116
+ sizes: '(min-width: 48em) 1000px, (min-width: 30em) 500px, 100px',
117
+ srcSet: buildSrcSet([100, 500, 1000])
118
+ })
119
+ })
120
+
121
+ test('mixture of px and vw values', () => {
122
+ let props = getResponsiveImageAttributes({
123
+ src: disImageURL.withOptionalParams,
124
+ widths: ['100vw', '720px', 500]
125
+ })
126
+ // 100vw of sm => 30em => 30 * 16 = 480px
127
+
128
+ expect(props).toStrictEqual({
129
+ src: disImageURL.withoutOptionalParams,
130
+ sizes: '(min-width: 48em) 500px, (min-width: 30em) 720px, 100vw',
131
+ srcSet: buildSrcSet([480, 500, 720])
132
+ })
133
+ })
134
+
135
+ test('only src', () => {
136
+ let props = getResponsiveImageAttributes({
137
+ src: disImageURL.withoutOptionalParams
138
+ })
139
+ expect(props).toStrictEqual({
140
+ src: disImageURL.withoutOptionalParams
141
+ })
142
+
143
+ // This time _with_ the optional params
144
+ props = getResponsiveImageAttributes({
145
+ src: disImageURL.withOptionalParams
146
+ })
147
+ expect(props).toStrictEqual({
148
+ src: disImageURL.withoutOptionalParams
149
+ })
150
+ })
151
+
152
+ test('passing in theme breakpoints', () => {
153
+ const props = getResponsiveImageAttributes({
154
+ src: disImageURL.withOptionalParams,
155
+ widths: ['100vw', 360],
156
+ breakpoints: {
157
+ base: '0px',
158
+ sm: '320px',
159
+ md: '768px',
160
+ lg: '960px',
161
+ xl: '1200px',
162
+ '2xl': '1536px'
163
+ }
164
+ })
165
+ expect(props).toStrictEqual({
166
+ src: disImageURL.withoutOptionalParams,
167
+ sizes: '(min-width: 320px) 360px, 100vw',
168
+ srcSet: buildSrcSet([320, 360])
169
+ })
170
+ })
@@ -0,0 +1,111 @@
1
+ /*
2
+ * Copyright (c) 2021, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+
8
+ import {getSites} from '@salesforce/retail-react-app/app/utils/site-utils'
9
+ import {urlPartPositions} from '@salesforce/retail-react-app/app/constants'
10
+
11
+ /**
12
+ * Construct literal routes based on url config
13
+ * with site and locale references (ids and aliases) from each in your application config
14
+ *
15
+ * @param {array} routes - array of routes to be reconstructed
16
+ * @param {object} urlConfig
17
+ * @param {object} options - options if there are any
18
+ * @param {array} options.ignoredRoutes - routes that does not need be reconstructed
19
+ * @return {array} - list of routes objects that has site and locale refs
20
+ */
21
+ export const configureRoutes = (routes = [], config, {ignoredRoutes = []}) => {
22
+ if (!routes.length) return []
23
+ if (!config) return routes
24
+
25
+ const {url: urlConfig} = config.app
26
+
27
+ const allSites = getSites()
28
+ if (!allSites) return routes
29
+
30
+ let outputRoutes = []
31
+ for (let i = 0; i < routes.length; i++) {
32
+ const {path, ...rest} = routes[i]
33
+
34
+ if (ignoredRoutes.includes(path)) {
35
+ outputRoutes.push(routes[i])
36
+ } else {
37
+ allSites.forEach((site) => {
38
+ // append site ids and aliases to an array
39
+ const siteRefs = [site.alias, site.id].filter(Boolean)
40
+ let localeRefs = []
41
+ // append locale ids and aliases to an array
42
+ site.l10n.supportedLocales.forEach((locale) => {
43
+ localeRefs.push(locale.alias)
44
+ localeRefs.push(locale.id)
45
+ })
46
+ localeRefs = localeRefs.filter(Boolean)
47
+ const {locale: localePosition, site: sitePosition} = urlConfig
48
+
49
+ if (
50
+ localePosition === urlPartPositions.PATH &&
51
+ sitePosition === urlPartPositions.PATH
52
+ ) {
53
+ siteRefs.forEach((site) => {
54
+ // append the route that only has site
55
+ outputRoutes.push({
56
+ path: `/${site}${path}`,
57
+ ...rest
58
+ })
59
+ localeRefs.forEach((locale) => {
60
+ // app the route that has both site and locale
61
+ outputRoutes.push({
62
+ path: `/${site}/${locale}${path}`,
63
+ ...rest
64
+ })
65
+ // append the route that only has locale
66
+ outputRoutes.push({
67
+ path: `/${locale}${path}`,
68
+ ...rest
69
+ })
70
+ })
71
+ })
72
+ }
73
+
74
+ if (
75
+ localePosition !== urlPartPositions.PATH &&
76
+ sitePosition === urlPartPositions.PATH
77
+ ) {
78
+ // construct the routes that only has site id or alias
79
+ siteRefs.forEach((site) => {
80
+ outputRoutes.push({
81
+ path: `/${site}${path}`,
82
+ ...rest
83
+ })
84
+ })
85
+ }
86
+ if (
87
+ localePosition === urlPartPositions.PATH &&
88
+ sitePosition !== urlPartPositions.PATH
89
+ ) {
90
+ // construct the routes that only has locale id or alias
91
+ localeRefs.forEach((locale) => {
92
+ outputRoutes.push({
93
+ path: `/${locale}${path}`,
94
+ ...rest
95
+ })
96
+ })
97
+ }
98
+ })
99
+ // origin route will be at the bottom
100
+ outputRoutes.push(routes[i])
101
+ }
102
+ }
103
+ // Remove any duplicate routes
104
+ outputRoutes = outputRoutes.reduce((res, route) => {
105
+ if (!res.some(({path}) => path === route.path)) {
106
+ res.push(route)
107
+ }
108
+ return res
109
+ }, [])
110
+ return outputRoutes
111
+ }