@teleporthq/teleport-plugin-next-data-source 0.43.66 → 0.43.68

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 (334) hide show
  1. package/__tests__/array-mapper-cache-wiring.test.ts +11 -0
  2. package/__tests__/array-overlap-sql.test.ts +53 -0
  3. package/__tests__/blog-post-page-enrichment.test.ts +214 -0
  4. package/__tests__/browser-row-policy.test.ts +503 -0
  5. package/__tests__/data-source-cache-wrapping.test.ts +18 -2
  6. package/__tests__/data-source-request-locale.test.ts +313 -0
  7. package/__tests__/ecommerce-product-options.test.ts +206 -0
  8. package/__tests__/ecommerce-product-pages.test.ts +82 -0
  9. package/__tests__/ecommerce-product-pre-options.golden.json +505 -0
  10. package/__tests__/ecommerce-product-storefront-contract.test.ts +293 -0
  11. package/__tests__/ecommerce-product-subscriptions.test.ts +121 -0
  12. package/__tests__/fetchers.test.ts +2 -2
  13. package/__tests__/gated-data-provider.test.ts +88 -0
  14. package/__tests__/items-path.test.ts +150 -0
  15. package/__tests__/localized-columns.test.ts +217 -0
  16. package/__tests__/numbered-pagination.test.ts +15 -0
  17. package/__tests__/product-filter-fields.ts +230 -0
  18. package/__tests__/product-filter-sql.fixture.json +7 -0
  19. package/__tests__/product-options-storefront.fixture.json +2510 -0
  20. package/__tests__/product-page-url-fixtures.json +25 -0
  21. package/__tests__/product-price-sort.ts +4 -2
  22. package/__tests__/raw-rows-mode.test.ts +111 -0
  23. package/__tests__/refetch-loading-state.test.ts +23 -0
  24. package/__tests__/rest-api-response-cache.test.ts +434 -0
  25. package/__tests__/search-clear-button.test.ts +122 -0
  26. package/__tests__/table-read-route-protected-tables.test.ts +493 -0
  27. package/dist/cjs/array-overlap-sql.d.ts +21 -0
  28. package/dist/cjs/array-overlap-sql.d.ts.map +1 -0
  29. package/dist/cjs/array-overlap-sql.js +25 -0
  30. package/dist/cjs/array-overlap-sql.js.map +1 -0
  31. package/dist/cjs/content-localization.d.ts +37 -0
  32. package/dist/cjs/content-localization.d.ts.map +1 -0
  33. package/dist/cjs/content-localization.js +29 -0
  34. package/dist/cjs/content-localization.js.map +1 -0
  35. package/dist/cjs/count-effect.d.ts +11 -1
  36. package/dist/cjs/count-effect.d.ts.map +1 -1
  37. package/dist/cjs/count-effect.js +30 -2
  38. package/dist/cjs/count-effect.js.map +1 -1
  39. package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
  40. package/dist/cjs/data-source-fetchers.js +10 -4
  41. package/dist/cjs/data-source-fetchers.js.map +1 -1
  42. package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
  43. package/dist/cjs/fetchers/airtable.js +1 -1
  44. package/dist/cjs/fetchers/airtable.js.map +1 -1
  45. package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
  46. package/dist/cjs/fetchers/clickhouse.js +1 -1
  47. package/dist/cjs/fetchers/clickhouse.js.map +1 -1
  48. package/dist/cjs/fetchers/firestore.d.ts.map +1 -1
  49. package/dist/cjs/fetchers/firestore.js +1 -1
  50. package/dist/cjs/fetchers/firestore.js.map +1 -1
  51. package/dist/cjs/fetchers/javascript.d.ts.map +1 -1
  52. package/dist/cjs/fetchers/javascript.js +1 -1
  53. package/dist/cjs/fetchers/javascript.js.map +1 -1
  54. package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
  55. package/dist/cjs/fetchers/mariadb.js +1 -1
  56. package/dist/cjs/fetchers/mariadb.js.map +1 -1
  57. package/dist/cjs/fetchers/mongodb.d.ts.map +1 -1
  58. package/dist/cjs/fetchers/mongodb.js +1 -1
  59. package/dist/cjs/fetchers/mongodb.js.map +1 -1
  60. package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
  61. package/dist/cjs/fetchers/mysql.js +1 -1
  62. package/dist/cjs/fetchers/mysql.js.map +1 -1
  63. package/dist/cjs/fetchers/postgresql.d.ts +1 -1
  64. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
  65. package/dist/cjs/fetchers/postgresql.js +9 -4
  66. package/dist/cjs/fetchers/postgresql.js.map +1 -1
  67. package/dist/cjs/fetchers/raw-query.d.ts +8 -1
  68. package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
  69. package/dist/cjs/fetchers/raw-query.js +42 -4
  70. package/dist/cjs/fetchers/raw-query.js.map +1 -1
  71. package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
  72. package/dist/cjs/fetchers/redshift.js +1 -1
  73. package/dist/cjs/fetchers/redshift.js.map +1 -1
  74. package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
  75. package/dist/cjs/fetchers/rest-api.js +7 -2
  76. package/dist/cjs/fetchers/rest-api.js.map +1 -1
  77. package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
  78. package/dist/cjs/fetchers/teleport.js +16 -6
  79. package/dist/cjs/fetchers/teleport.js.map +1 -1
  80. package/dist/cjs/fetchers/turso.d.ts.map +1 -1
  81. package/dist/cjs/fetchers/turso.js +1 -1
  82. package/dist/cjs/fetchers/turso.js.map +1 -1
  83. package/dist/cjs/fetchers/utils/browser-row-policy.d.ts +43 -0
  84. package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -0
  85. package/dist/cjs/fetchers/utils/browser-row-policy.js +66 -0
  86. package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -0
  87. package/dist/cjs/fetchers/utils/request-sql-guards.d.ts +8 -0
  88. package/dist/cjs/fetchers/utils/request-sql-guards.d.ts.map +1 -0
  89. package/dist/cjs/fetchers/utils/request-sql-guards.js +12 -0
  90. package/dist/cjs/fetchers/utils/request-sql-guards.js.map +1 -0
  91. package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts +40 -0
  92. package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
  93. package/dist/cjs/fetchers/utils/rest-api-response-cache.js +64 -0
  94. package/dist/cjs/fetchers/utils/rest-api-response-cache.js.map +1 -0
  95. package/dist/cjs/fetchers/utils/table-access-guard.d.ts +23 -0
  96. package/dist/cjs/fetchers/utils/table-access-guard.d.ts.map +1 -0
  97. package/dist/cjs/fetchers/utils/table-access-guard.js +34 -0
  98. package/dist/cjs/fetchers/utils/table-access-guard.js.map +1 -0
  99. package/dist/cjs/index.d.ts +5 -0
  100. package/dist/cjs/index.d.ts.map +1 -1
  101. package/dist/cjs/index.js +60 -8
  102. package/dist/cjs/index.js.map +1 -1
  103. package/dist/cjs/items-path.d.ts +10 -0
  104. package/dist/cjs/items-path.d.ts.map +1 -0
  105. package/dist/cjs/items-path.js +86 -0
  106. package/dist/cjs/items-path.js.map +1 -0
  107. package/dist/cjs/loading-state.d.ts +16 -1
  108. package/dist/cjs/loading-state.d.ts.map +1 -1
  109. package/dist/cjs/loading-state.js +69 -3
  110. package/dist/cjs/loading-state.js.map +1 -1
  111. package/dist/cjs/localized-columns.d.ts +56 -0
  112. package/dist/cjs/localized-columns.d.ts.map +1 -0
  113. package/dist/cjs/localized-columns.js +137 -0
  114. package/dist/cjs/localized-columns.js.map +1 -0
  115. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  116. package/dist/cjs/pagination-plugin.js +140 -50
  117. package/dist/cjs/pagination-plugin.js.map +1 -1
  118. package/dist/cjs/product-filter-fields.d.ts +87 -0
  119. package/dist/cjs/product-filter-fields.d.ts.map +1 -0
  120. package/dist/cjs/product-filter-fields.js +161 -0
  121. package/dist/cjs/product-filter-fields.js.map +1 -0
  122. package/dist/cjs/request-locale.d.ts +49 -0
  123. package/dist/cjs/request-locale.d.ts.map +1 -0
  124. package/dist/cjs/request-locale.js +130 -0
  125. package/dist/cjs/request-locale.js.map +1 -0
  126. package/dist/cjs/search-clear.d.ts +31 -0
  127. package/dist/cjs/search-clear.d.ts.map +1 -0
  128. package/dist/cjs/search-clear.js +103 -0
  129. package/dist/cjs/search-clear.js.map +1 -0
  130. package/dist/cjs/transformations/blog-adjacent-posts.d.ts +16 -0
  131. package/dist/cjs/transformations/blog-adjacent-posts.d.ts.map +1 -0
  132. package/dist/cjs/transformations/blog-adjacent-posts.js +25 -0
  133. package/dist/cjs/transformations/blog-adjacent-posts.js.map +1 -0
  134. package/dist/cjs/transformations/blog-comments.d.ts +30 -0
  135. package/dist/cjs/transformations/blog-comments.d.ts.map +1 -0
  136. package/dist/cjs/transformations/blog-comments.js +34 -0
  137. package/dist/cjs/transformations/blog-comments.js.map +1 -0
  138. package/dist/cjs/transformations/blog-post.d.ts +2 -0
  139. package/dist/cjs/transformations/blog-post.d.ts.map +1 -1
  140. package/dist/cjs/transformations/blog-post.js +4 -1
  141. package/dist/cjs/transformations/blog-post.js.map +1 -1
  142. package/dist/cjs/transformations/ecommerce-product.d.ts +9 -1
  143. package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
  144. package/dist/cjs/transformations/ecommerce-product.js +28 -1
  145. package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
  146. package/dist/cjs/transformations/heading-anchors.d.ts +25 -0
  147. package/dist/cjs/transformations/heading-anchors.d.ts.map +1 -0
  148. package/dist/cjs/transformations/heading-anchors.js +29 -0
  149. package/dist/cjs/transformations/heading-anchors.js.map +1 -0
  150. package/dist/cjs/transformations/index.d.ts +61 -4
  151. package/dist/cjs/transformations/index.d.ts.map +1 -1
  152. package/dist/cjs/transformations/index.js +69 -19
  153. package/dist/cjs/transformations/index.js.map +1 -1
  154. package/dist/cjs/transformations/shared-utils.d.ts.map +1 -1
  155. package/dist/cjs/transformations/shared-utils.js +1 -1
  156. package/dist/cjs/transformations/shared-utils.js.map +1 -1
  157. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  158. package/dist/cjs/utils.d.ts +11 -1
  159. package/dist/cjs/utils.d.ts.map +1 -1
  160. package/dist/cjs/utils.js +60 -16
  161. package/dist/cjs/utils.js.map +1 -1
  162. package/dist/esm/array-overlap-sql.d.ts +21 -0
  163. package/dist/esm/array-overlap-sql.d.ts.map +1 -0
  164. package/dist/esm/array-overlap-sql.js +21 -0
  165. package/dist/esm/array-overlap-sql.js.map +1 -0
  166. package/dist/esm/content-localization.d.ts +37 -0
  167. package/dist/esm/content-localization.d.ts.map +1 -0
  168. package/dist/esm/content-localization.js +24 -0
  169. package/dist/esm/content-localization.js.map +1 -0
  170. package/dist/esm/count-effect.d.ts +11 -1
  171. package/dist/esm/count-effect.d.ts.map +1 -1
  172. package/dist/esm/count-effect.js +28 -1
  173. package/dist/esm/count-effect.js.map +1 -1
  174. package/dist/esm/data-source-fetchers.d.ts.map +1 -1
  175. package/dist/esm/data-source-fetchers.js +11 -5
  176. package/dist/esm/data-source-fetchers.js.map +1 -1
  177. package/dist/esm/fetchers/airtable.d.ts.map +1 -1
  178. package/dist/esm/fetchers/airtable.js +1 -1
  179. package/dist/esm/fetchers/airtable.js.map +1 -1
  180. package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
  181. package/dist/esm/fetchers/clickhouse.js +1 -1
  182. package/dist/esm/fetchers/clickhouse.js.map +1 -1
  183. package/dist/esm/fetchers/firestore.d.ts.map +1 -1
  184. package/dist/esm/fetchers/firestore.js +1 -1
  185. package/dist/esm/fetchers/firestore.js.map +1 -1
  186. package/dist/esm/fetchers/javascript.d.ts.map +1 -1
  187. package/dist/esm/fetchers/javascript.js +1 -1
  188. package/dist/esm/fetchers/javascript.js.map +1 -1
  189. package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
  190. package/dist/esm/fetchers/mariadb.js +1 -1
  191. package/dist/esm/fetchers/mariadb.js.map +1 -1
  192. package/dist/esm/fetchers/mongodb.d.ts.map +1 -1
  193. package/dist/esm/fetchers/mongodb.js +1 -1
  194. package/dist/esm/fetchers/mongodb.js.map +1 -1
  195. package/dist/esm/fetchers/mysql.d.ts.map +1 -1
  196. package/dist/esm/fetchers/mysql.js +1 -1
  197. package/dist/esm/fetchers/mysql.js.map +1 -1
  198. package/dist/esm/fetchers/postgresql.d.ts +1 -1
  199. package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
  200. package/dist/esm/fetchers/postgresql.js +9 -4
  201. package/dist/esm/fetchers/postgresql.js.map +1 -1
  202. package/dist/esm/fetchers/raw-query.d.ts +8 -1
  203. package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
  204. package/dist/esm/fetchers/raw-query.js +42 -4
  205. package/dist/esm/fetchers/raw-query.js.map +1 -1
  206. package/dist/esm/fetchers/redshift.d.ts.map +1 -1
  207. package/dist/esm/fetchers/redshift.js +1 -1
  208. package/dist/esm/fetchers/redshift.js.map +1 -1
  209. package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
  210. package/dist/esm/fetchers/rest-api.js +7 -2
  211. package/dist/esm/fetchers/rest-api.js.map +1 -1
  212. package/dist/esm/fetchers/teleport.d.ts.map +1 -1
  213. package/dist/esm/fetchers/teleport.js +16 -6
  214. package/dist/esm/fetchers/teleport.js.map +1 -1
  215. package/dist/esm/fetchers/turso.d.ts.map +1 -1
  216. package/dist/esm/fetchers/turso.js +1 -1
  217. package/dist/esm/fetchers/turso.js.map +1 -1
  218. package/dist/esm/fetchers/utils/browser-row-policy.d.ts +43 -0
  219. package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -0
  220. package/dist/esm/fetchers/utils/browser-row-policy.js +60 -0
  221. package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -0
  222. package/dist/esm/fetchers/utils/request-sql-guards.d.ts +8 -0
  223. package/dist/esm/fetchers/utils/request-sql-guards.d.ts.map +1 -0
  224. package/dist/esm/fetchers/utils/request-sql-guards.js +8 -0
  225. package/dist/esm/fetchers/utils/request-sql-guards.js.map +1 -0
  226. package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts +40 -0
  227. package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
  228. package/dist/esm/fetchers/utils/rest-api-response-cache.js +59 -0
  229. package/dist/esm/fetchers/utils/rest-api-response-cache.js.map +1 -0
  230. package/dist/esm/fetchers/utils/table-access-guard.d.ts +23 -0
  231. package/dist/esm/fetchers/utils/table-access-guard.d.ts.map +1 -0
  232. package/dist/esm/fetchers/utils/table-access-guard.js +29 -0
  233. package/dist/esm/fetchers/utils/table-access-guard.js.map +1 -0
  234. package/dist/esm/index.d.ts +5 -0
  235. package/dist/esm/index.d.ts.map +1 -1
  236. package/dist/esm/index.js +55 -7
  237. package/dist/esm/index.js.map +1 -1
  238. package/dist/esm/items-path.d.ts +10 -0
  239. package/dist/esm/items-path.d.ts.map +1 -0
  240. package/dist/esm/items-path.js +58 -0
  241. package/dist/esm/items-path.js.map +1 -0
  242. package/dist/esm/loading-state.d.ts +16 -1
  243. package/dist/esm/loading-state.d.ts.map +1 -1
  244. package/dist/esm/loading-state.js +69 -3
  245. package/dist/esm/loading-state.js.map +1 -1
  246. package/dist/esm/localized-columns.d.ts +56 -0
  247. package/dist/esm/localized-columns.d.ts.map +1 -0
  248. package/dist/esm/localized-columns.js +132 -0
  249. package/dist/esm/localized-columns.js.map +1 -0
  250. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  251. package/dist/esm/pagination-plugin.js +141 -51
  252. package/dist/esm/pagination-plugin.js.map +1 -1
  253. package/dist/esm/product-filter-fields.d.ts +87 -0
  254. package/dist/esm/product-filter-fields.d.ts.map +1 -0
  255. package/dist/esm/product-filter-fields.js +154 -0
  256. package/dist/esm/product-filter-fields.js.map +1 -0
  257. package/dist/esm/request-locale.d.ts +49 -0
  258. package/dist/esm/request-locale.d.ts.map +1 -0
  259. package/dist/esm/request-locale.js +98 -0
  260. package/dist/esm/request-locale.js.map +1 -0
  261. package/dist/esm/search-clear.d.ts +31 -0
  262. package/dist/esm/search-clear.d.ts.map +1 -0
  263. package/dist/esm/search-clear.js +74 -0
  264. package/dist/esm/search-clear.js.map +1 -0
  265. package/dist/esm/transformations/blog-adjacent-posts.d.ts +16 -0
  266. package/dist/esm/transformations/blog-adjacent-posts.d.ts.map +1 -0
  267. package/dist/esm/transformations/blog-adjacent-posts.js +21 -0
  268. package/dist/esm/transformations/blog-adjacent-posts.js.map +1 -0
  269. package/dist/esm/transformations/blog-comments.d.ts +30 -0
  270. package/dist/esm/transformations/blog-comments.d.ts.map +1 -0
  271. package/dist/esm/transformations/blog-comments.js +30 -0
  272. package/dist/esm/transformations/blog-comments.js.map +1 -0
  273. package/dist/esm/transformations/blog-post.d.ts +2 -0
  274. package/dist/esm/transformations/blog-post.d.ts.map +1 -1
  275. package/dist/esm/transformations/blog-post.js +4 -1
  276. package/dist/esm/transformations/blog-post.js.map +1 -1
  277. package/dist/esm/transformations/ecommerce-product.d.ts +9 -1
  278. package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
  279. package/dist/esm/transformations/ecommerce-product.js +29 -2
  280. package/dist/esm/transformations/ecommerce-product.js.map +1 -1
  281. package/dist/esm/transformations/heading-anchors.d.ts +25 -0
  282. package/dist/esm/transformations/heading-anchors.d.ts.map +1 -0
  283. package/dist/esm/transformations/heading-anchors.js +25 -0
  284. package/dist/esm/transformations/heading-anchors.js.map +1 -0
  285. package/dist/esm/transformations/index.d.ts +61 -4
  286. package/dist/esm/transformations/index.d.ts.map +1 -1
  287. package/dist/esm/transformations/index.js +69 -19
  288. package/dist/esm/transformations/index.js.map +1 -1
  289. package/dist/esm/transformations/shared-utils.d.ts.map +1 -1
  290. package/dist/esm/transformations/shared-utils.js +1 -1
  291. package/dist/esm/transformations/shared-utils.js.map +1 -1
  292. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  293. package/dist/esm/utils.d.ts +11 -1
  294. package/dist/esm/utils.d.ts.map +1 -1
  295. package/dist/esm/utils.js +60 -16
  296. package/dist/esm/utils.js.map +1 -1
  297. package/package.json +5 -5
  298. package/src/array-overlap-sql.ts +41 -0
  299. package/src/content-localization.ts +53 -0
  300. package/src/count-effect.ts +53 -2
  301. package/src/data-source-fetchers.ts +41 -5
  302. package/src/fetchers/airtable.ts +4 -2
  303. package/src/fetchers/clickhouse.ts +4 -2
  304. package/src/fetchers/firestore.ts +4 -2
  305. package/src/fetchers/javascript.ts +9 -1
  306. package/src/fetchers/mariadb.ts +4 -2
  307. package/src/fetchers/mongodb.ts +4 -2
  308. package/src/fetchers/mysql.ts +4 -2
  309. package/src/fetchers/postgresql.ts +53 -12
  310. package/src/fetchers/raw-query.ts +60 -5
  311. package/src/fetchers/redshift.ts +4 -2
  312. package/src/fetchers/rest-api.ts +27 -5
  313. package/src/fetchers/teleport.ts +85 -22
  314. package/src/fetchers/turso.ts +4 -2
  315. package/src/fetchers/utils/browser-row-policy.ts +212 -0
  316. package/src/fetchers/utils/request-sql-guards.ts +25 -0
  317. package/src/fetchers/utils/rest-api-response-cache.ts +188 -0
  318. package/src/fetchers/utils/table-access-guard.ts +39 -0
  319. package/src/index.ts +79 -11
  320. package/src/items-path.ts +74 -0
  321. package/src/loading-state.ts +134 -3
  322. package/src/localized-columns.ts +146 -0
  323. package/src/pagination-plugin.ts +206 -60
  324. package/src/product-filter-fields.ts +173 -0
  325. package/src/request-locale.ts +113 -0
  326. package/src/search-clear.ts +102 -0
  327. package/src/transformations/blog-adjacent-posts.ts +74 -0
  328. package/src/transformations/blog-comments.ts +132 -0
  329. package/src/transformations/blog-post.ts +38 -5
  330. package/src/transformations/ecommerce-product.ts +568 -5
  331. package/src/transformations/heading-anchors.ts +85 -0
  332. package/src/transformations/index.ts +191 -23
  333. package/src/transformations/shared-utils.ts +6 -0
  334. package/src/utils.ts +109 -19
@@ -0,0 +1,313 @@
1
+ import * as types from '@babel/types'
2
+ import generator from '@babel/generator'
3
+ import {
4
+ ChunkType,
5
+ FileType,
6
+ ComponentStructure,
7
+ ChunkDefinition,
8
+ GeneratorOptions,
9
+ UIDLDataSource,
10
+ UIDLDependency,
11
+ } from '@teleporthq/teleport-types'
12
+ import { createNextPagesDataSourcePlugin, createNextComponentDataSourcePlugin } from '../src/index'
13
+
14
+ /**
15
+ * A page in a multi-language project renders rows in ITS language: every
16
+ * data-source fetch — the build-time prefetch in getStaticProps and the
17
+ * client-side refetch behind pagination, search and filters — says which
18
+ * locale it is for, and a locale switch (a client-side transition on the same
19
+ * page instance) refetches. A single-language project generates exactly what
20
+ * it generated before: no locale, no router.
21
+ */
22
+
23
+ const DS_ID = 'ds1'
24
+ const TABLE = 'teleport_products'
25
+ /** A page writes the fetcher to `utils/`, a component straight into `api/`. */
26
+ const FETCHER_KEYS = ['utils/teleport-teleport_products-ds1', 'api/teleport-teleport_products-ds1']
27
+
28
+ const I18N: GeneratorOptions['internationalization'] = {
29
+ main: { name: 'English', locale: 'en' },
30
+ languages: { en: 'English', es: 'Spanish' },
31
+ }
32
+
33
+ const dataSource: UIDLDataSource = {
34
+ id: DS_ID,
35
+ name: 'TeleportHQ database',
36
+ type: 'teleport',
37
+ config: {
38
+ selectedTables: {
39
+ [TABLE]: {
40
+ columns: [{ name: 'id' }, { name: 'name' }, { name: 'slug' }, { name: 'es_name' }],
41
+ },
42
+ },
43
+ },
44
+ }
45
+
46
+ const jsx = (name: string, attrs: types.JSXAttribute[], children: types.JSXElement[] = []) =>
47
+ types.jsxElement(
48
+ types.jsxOpeningElement(types.jsxIdentifier(name), attrs, children.length === 0),
49
+ children.length === 0 ? null : types.jsxClosingElement(types.jsxIdentifier(name)),
50
+ children,
51
+ children.length === 0
52
+ )
53
+
54
+ const resourceDefinitionAttr = () =>
55
+ types.jsxAttribute(
56
+ types.jsxIdentifier('resourceDefinition'),
57
+ types.jsxExpressionContainer(
58
+ types.objectExpression([
59
+ types.objectProperty(types.stringLiteral('dataSourceId'), types.stringLiteral(DS_ID)),
60
+ types.objectProperty(types.stringLiteral('tableName'), types.stringLiteral(TABLE)),
61
+ types.objectProperty(
62
+ types.stringLiteral('dataSourceType'),
63
+ types.stringLiteral('teleport')
64
+ ),
65
+ ])
66
+ )
67
+ )
68
+
69
+ /** `<DataProvider name={'items'} resourceDefinition={…} renderSuccess={(items) => <Repeater …/>} />` */
70
+ const makeListProvider = (): types.JSXElement => {
71
+ const repeater = jsx('Repeater', [
72
+ types.jsxAttribute(
73
+ types.jsxIdentifier('items'),
74
+ types.jsxExpressionContainer(types.identifier('items'))
75
+ ),
76
+ types.jsxAttribute(
77
+ types.jsxIdentifier('renderItem'),
78
+ types.jsxExpressionContainer(
79
+ types.arrowFunctionExpression([types.identifier('product')], jsx('div', []))
80
+ )
81
+ ),
82
+ ])
83
+ return jsx('DataProvider', [
84
+ types.jsxAttribute(
85
+ types.jsxIdentifier('name'),
86
+ types.jsxExpressionContainer(types.stringLiteral('items'))
87
+ ),
88
+ resourceDefinitionAttr(),
89
+ types.jsxAttribute(
90
+ types.jsxIdentifier('renderSuccess'),
91
+ types.jsxExpressionContainer(
92
+ types.arrowFunctionExpression([types.identifier('items')], repeater)
93
+ )
94
+ ),
95
+ ])
96
+ }
97
+
98
+ /** `<DataProvider name={'item'} resourceDefinition={…} renderSuccess={(item) => <div/>} />` */
99
+ const makeItemProvider = (): types.JSXElement =>
100
+ jsx('DataProvider', [
101
+ types.jsxAttribute(
102
+ types.jsxIdentifier('name'),
103
+ types.jsxExpressionContainer(types.stringLiteral('item'))
104
+ ),
105
+ resourceDefinitionAttr(),
106
+ types.jsxAttribute(
107
+ types.jsxIdentifier('renderSuccess'),
108
+ types.jsxExpressionContainer(
109
+ types.arrowFunctionExpression([types.identifier('item')], jsx('div', []))
110
+ )
111
+ ),
112
+ ])
113
+
114
+ const makeComponentChunk = (provider: types.JSXElement): ChunkDefinition => ({
115
+ name: 'jsx-component',
116
+ type: ChunkType.AST,
117
+ fileType: FileType.JS,
118
+ linkAfter: [],
119
+ content: types.variableDeclaration('const', [
120
+ types.variableDeclarator(
121
+ types.identifier('ProductsList'),
122
+ types.arrowFunctionExpression(
123
+ [types.identifier('props')],
124
+ types.blockStatement([types.returnStatement(jsx('div', [], [provider]))])
125
+ )
126
+ ),
127
+ ]),
128
+ meta: { nodesLookup: { provider } },
129
+ })
130
+
131
+ // tslint:disable-next-line:no-any
132
+ const makeListNode = (paginated: boolean): any => ({
133
+ type: 'data-source-list',
134
+ content: {
135
+ renderPropIdentifier: 'items',
136
+ resourceDefinition: { dataSourceId: DS_ID, tableName: TABLE, dataSourceType: 'teleport' },
137
+ resource: {
138
+ id: 'TQ_products',
139
+ params: { queryColumns: { type: 'static', content: ['name', 'es_name'] } },
140
+ },
141
+ nodes: {
142
+ success: {
143
+ type: 'cms-list-repeater',
144
+ content: {
145
+ renderPropIdentifier: 'product',
146
+ paginated,
147
+ perPage: 20,
148
+ searchEnabled: false,
149
+ nodes: { list: { type: 'element', content: { elementType: 'div' } } },
150
+ },
151
+ },
152
+ },
153
+ },
154
+ })
155
+
156
+ /** An item whose params cannot be resolved at build time, so it fetches client-side. */
157
+ // tslint:disable-next-line:no-any
158
+ const makeDynamicItemNode = (withParams: boolean): any => ({
159
+ type: 'data-source-item',
160
+ content: {
161
+ renderPropIdentifier: 'item',
162
+ resourceDefinition: { dataSourceId: DS_ID, tableName: TABLE, dataSourceType: 'teleport' },
163
+ resource: {
164
+ params: withParams
165
+ ? { limit: { type: 'dynamic', content: { referenceType: 'prop', id: 'limit' } } }
166
+ : {},
167
+ },
168
+ children: [{ type: 'element', content: { elementType: 'div' } }],
169
+ },
170
+ })
171
+
172
+ const run = async (
173
+ plugin: ReturnType<typeof createNextPagesDataSourcePlugin>,
174
+ // tslint:disable-next-line:no-any
175
+ node: any,
176
+ provider: types.JSXElement,
177
+ localized: boolean
178
+ ) => {
179
+ const chunk = makeComponentChunk(provider)
180
+ const dependencies: Record<string, UIDLDependency> = {}
181
+ // tslint:disable-next-line:no-any
182
+ const extractedResources: Record<string, any> = {}
183
+ const structure: ComponentStructure = {
184
+ uidl: { name: 'ProductsList', node, outputOptions: { folderPath: [] } },
185
+ chunks: [chunk],
186
+ dependencies,
187
+ options: {
188
+ dataSources: { [DS_ID]: dataSource },
189
+ extractedResources,
190
+ ...(localized ? { internationalization: I18N } : {}),
191
+ },
192
+ } as never
193
+ await plugin(structure)
194
+ const getStaticProps = structure.chunks.find((c) => c.name === 'getStaticProps')
195
+ return {
196
+ code: generator(chunk.content as types.Node).code,
197
+ staticProps: getStaticProps ? generator(getStaticProps.content as types.Node).code : '',
198
+ dependencies,
199
+ fetcher: FETCHER_KEYS.map((key) => extractedResources[key]?.content).find(Boolean) as string,
200
+ }
201
+ }
202
+
203
+ const collapse = (code: string): string => code.replace(/\s+/g, ' ')
204
+
205
+ describe('data-source fetches in a multi-language project', () => {
206
+ const pagesPlugin = createNextPagesDataSourcePlugin()
207
+ const componentPlugin = createNextComponentDataSourcePlugin()
208
+
209
+ describe('a paginated list page', () => {
210
+ it('fetches the first page for the language getStaticProps runs for', async () => {
211
+ const { staticProps } = await run(pagesPlugin, makeListNode(true), makeListProvider(), true)
212
+ expect(collapse(staticProps)).toContain(
213
+ 'fetchData({ page: 1, perPage: 20, queryColumns: JSON.stringify(["name", "es_name"]), locale: context.locale })'
214
+ )
215
+ // The count is the same in every language.
216
+ expect(collapse(staticProps)).toContain('fetchCount().catch')
217
+ })
218
+
219
+ it('sends the client locale with every refetch and refetches when it changes', async () => {
220
+ const { code, dependencies } = await run(
221
+ pagesPlugin,
222
+ makeListNode(true),
223
+ makeListProvider(),
224
+ true
225
+ )
226
+ const flat = collapse(code)
227
+ expect(flat).toContain(
228
+ 'params={useMemo(() => ({ page: ds_0_page, perPage: 20, locale: router?.locale }), [ds_0_page, router?.locale])}'
229
+ )
230
+ expect(code).toContain('const router = useRouter()')
231
+ expect(dependencies.useRouter).toMatchObject({ type: 'library', path: 'next/router' })
232
+ })
233
+
234
+ it('bakes the per-language columns into the fetcher it emits', async () => {
235
+ const { fetcher } = await run(pagesPlugin, makeListNode(true), makeListProvider(), true)
236
+ expect(fetcher).toContain('var LOCALIZED_COLUMNS = {"name":{"es":"es_name"}}')
237
+ expect(fetcher).toContain('mainLanguage: "en"')
238
+ })
239
+ })
240
+
241
+ describe('a plain list page', () => {
242
+ it('prefetches and refetches in the page language', async () => {
243
+ const { code, staticProps } = await run(
244
+ pagesPlugin,
245
+ makeListNode(false),
246
+ makeListProvider(),
247
+ true
248
+ )
249
+ expect(collapse(staticProps)).toContain(
250
+ 'fetchData({ "queryColumns": JSON.stringify(["name", "es_name"]), locale: context.locale })'
251
+ )
252
+ expect(collapse(code)).toContain(
253
+ 'params={useMemo(() => ({ locale: router?.locale }), [router?.locale])}'
254
+ )
255
+ expect(code).toContain('const router = useRouter()')
256
+ })
257
+ })
258
+
259
+ describe('a component item that fetches client-side', () => {
260
+ it('adds the locale to the params it already sends', async () => {
261
+ const { code, dependencies } = await run(
262
+ componentPlugin,
263
+ makeDynamicItemNode(true),
264
+ makeItemProvider(),
265
+ true
266
+ )
267
+ const flat = collapse(code)
268
+ expect(flat).toContain('locale: router?.locale')
269
+ // The route is a template literal that interpolates the params.
270
+ expect(flat).toContain(
271
+ 'fetch(`/api/teleport-teleport_products-ds1?$' + '{new URLSearchParams(params)}`'
272
+ )
273
+ expect(code).toContain('const router = useRouter()')
274
+ expect(dependencies.useRouter).toBeDefined()
275
+ })
276
+
277
+ it('creates the params when the node declared none', async () => {
278
+ const { code } = await run(
279
+ componentPlugin,
280
+ makeDynamicItemNode(false),
281
+ makeItemProvider(),
282
+ true
283
+ )
284
+ const flat = collapse(code)
285
+ expect(flat).toContain('params={{ locale: router?.locale }}')
286
+ expect(flat).toContain('new URLSearchParams(params)')
287
+ })
288
+ })
289
+
290
+ describe('a single-language project', () => {
291
+ it.each([
292
+ ['a paginated list page', pagesPlugin, makeListNode(true), makeListProvider],
293
+ ['a plain list page', pagesPlugin, makeListNode(false), makeListProvider],
294
+ ['a component item', componentPlugin, makeDynamicItemNode(true), makeItemProvider],
295
+ ])(
296
+ 'generates %s exactly as before — no locale, no router',
297
+ async (_, plugin, node, provider) => {
298
+ const { code, staticProps, dependencies, fetcher } = await run(
299
+ plugin,
300
+ node,
301
+ provider(),
302
+ false
303
+ )
304
+ expect(code).not.toContain('locale')
305
+ expect(code).not.toContain('useRouter')
306
+ expect(staticProps).not.toContain('locale')
307
+ expect(dependencies.useRouter).toBeUndefined()
308
+ expect(fetcher).toContain('var LOCALIZED_COLUMNS = {}')
309
+ expect(fetcher).toContain('mainLanguage: null')
310
+ }
311
+ )
312
+ })
313
+ })
@@ -0,0 +1,206 @@
1
+ import { readFileSync } from 'fs'
2
+ import { join } from 'path'
3
+ import { ProductDiscounts, ProductOptions } from '@teleporthq/teleport-shared'
4
+ import { getTransformWrapperCode } from '../src/transformations'
5
+ import { generateEcommerceProductTransformationCode } from '../src/transformations/ecommerce-product'
6
+ import { generateSharedTransformationCode } from '../src/transformations/shared-utils'
7
+
8
+ /**
9
+ * Product options in the GENERATED storefront transform. The fixture is a
10
+ * byte-for-byte copy of the GUI's
11
+ * `features/e-commerce/utils/product-options/__tests__/fixtures/product-options-storefront.fixture.json`,
12
+ * which the canvas transform (`packages/renderer/src/utils/ecommerce-products.ts`)
13
+ * runs in `ecommerce-products-options.test.ts`: both transforms must emit
14
+ * exactly these fields, or the configurator, the configured price and the
15
+ * card's quick-add gate differ between the editor and the published store.
16
+ */
17
+
18
+ type Build = (record: unknown, options?: Record<string, unknown>) => Record<string, unknown>
19
+
20
+ interface FixtureCase {
21
+ name: string
22
+ includeDetail: boolean
23
+ taxRate: number
24
+ currentLanguage: string | null
25
+ mainLanguage: string | null
26
+ variants: Array<Record<string, unknown>> | null
27
+ record: Record<string, unknown>
28
+ expected: Record<string, unknown>
29
+ }
30
+
31
+ const readJson = <T>(file: string): T => JSON.parse(readFileSync(join(__dirname, file), 'utf8'))
32
+
33
+ const FIXTURE = readJson<{ cases: FixtureCase[] }>('product-options-storefront.fixture.json')
34
+ const PRE_OPTIONS_GOLDEN = readJson<{
35
+ records: Array<Record<string, unknown>>
36
+ variantsByProductId: Record<string, unknown>
37
+ assetMap: Record<string, unknown>
38
+ storefrontTaxRate: number
39
+ expected: Array<Record<string, unknown>>
40
+ }>('ecommerce-product-pre-options.golden.json')
41
+
42
+ const transformCode = (storefrontTaxRate: number): string =>
43
+ generateSharedTransformationCode() +
44
+ '\n' +
45
+ generateEcommerceProductTransformationCode({ storefrontTaxRate })
46
+
47
+ const buildProduct = (storefrontTaxRate: number): Build =>
48
+ new Function(transformCode(storefrontTaxRate) + '\nreturn buildEcommerceProduct;')() as Build
49
+
50
+ const OPTION_FIELDS = Object.keys(FIXTURE.cases[0].expected)
51
+
52
+ const pickOptionFields = (product: Record<string, unknown>) =>
53
+ OPTION_FIELDS.reduce<Record<string, unknown>>((picked, field) => {
54
+ picked[field] = product[field]
55
+ return picked
56
+ }, {})
57
+
58
+ const withoutOptionFields = (product: Record<string, unknown>) =>
59
+ Object.keys(product)
60
+ .filter((field) => OPTION_FIELDS.indexOf(field) === -1)
61
+ .reduce<Record<string, unknown>>((kept, field) => {
62
+ kept[field] = product[field]
63
+ return kept
64
+ }, {})
65
+
66
+ const EMPTY_OPTION_FIELDS = FIXTURE.cases.find(
67
+ (entry) => entry.name === 'a product without options'
68
+ )?.expected as Record<string, unknown>
69
+
70
+ const PRINT_GROUPS = [
71
+ {
72
+ key: 'paper',
73
+ label: 'Paper',
74
+ type: 'choice',
75
+ display: 'cards',
76
+ required: true,
77
+ values: [
78
+ {
79
+ key: 'matte',
80
+ label: 'Matte',
81
+ price: { mode: 'none' },
82
+ default: true,
83
+ imageUrl: 'TQ_matte',
84
+ },
85
+ ],
86
+ },
87
+ ]
88
+
89
+ describe('product transform — product options fixture (shared with the canvas)', () => {
90
+ it.each(FIXTURE.cases.map((entry) => [entry.name, entry] as [string, FixtureCase]))(
91
+ '%s',
92
+ (_name, entry) => {
93
+ const product = buildProduct(entry.taxRate)(entry.record, {
94
+ assetMap: {},
95
+ currentLanguage: entry.currentLanguage,
96
+ mainLanguage: entry.mainLanguage,
97
+ variantsByProductId: entry.variants ? { [String(entry.record.id)]: entry.variants } : null,
98
+ includeOptionDetail: entry.includeDetail,
99
+ })
100
+ expect(pickOptionFields(product)).toEqual(entry.expected)
101
+ }
102
+ )
103
+ })
104
+
105
+ describe('product transform — products without options are unchanged', () => {
106
+ it.each([false, true])(
107
+ 'equals the pre-options output plus empty fields (detail: %s)',
108
+ (detail) => {
109
+ const build = buildProduct(PRE_OPTIONS_GOLDEN.storefrontTaxRate)
110
+ PRE_OPTIONS_GOLDEN.records.forEach((record, index) => {
111
+ const product = build(record, {
112
+ assetMap: PRE_OPTIONS_GOLDEN.assetMap,
113
+ currentLanguage: null,
114
+ mainLanguage: null,
115
+ variantsByProductId: PRE_OPTIONS_GOLDEN.variantsByProductId,
116
+ ratingsByProductId: {},
117
+ reviewsByProductId: {},
118
+ includeOptionDetail: detail,
119
+ })
120
+ expect(JSON.parse(JSON.stringify(withoutOptionFields(product)))).toEqual(
121
+ PRE_OPTIONS_GOLDEN.expected[index]
122
+ )
123
+ expect(pickOptionFields(product)).toEqual(EMPTY_OPTION_FIELDS)
124
+ })
125
+ }
126
+ )
127
+ })
128
+
129
+ describe('product transform — option detail wiring', () => {
130
+ const record = { id: 'p1', name: 'Print', price: 10, option_groups: JSON.stringify(PRINT_GROUPS) }
131
+
132
+ it('resolves a value image stored as an asset id through the asset map', () => {
133
+ const product = buildProduct(0)(record, {
134
+ assetMap: { TQ_matte: { remoteSrc: 'https://cdn.example.com/matte.jpg' } },
135
+ includeOptionDetail: true,
136
+ })
137
+ const groups = product.optionGroups as Array<{ values: Array<Record<string, unknown>> }>
138
+ expect(groups[0].values[0]).toMatchObject({
139
+ imageUrl: 'https://cdn.example.com/matte.jpg',
140
+ showImage: 'true',
141
+ })
142
+ })
143
+
144
+ it('never hands a related product the option detail', () => {
145
+ const parent = buildProduct(0)(
146
+ { id: 'p0', name: 'Frame', price: 5, related_product_ids: '["p1"]' },
147
+ { includeOptionDetail: true, relatedProductsById: { p1: record } }
148
+ )
149
+ const [related] = parent.relatedProducts as Array<Record<string, unknown>>
150
+ expect(related.hasProductOptions).toBe('true')
151
+ expect(related.optionGroups).toEqual([])
152
+ expect(related.optionsPricingJson).toBe('{}')
153
+ })
154
+
155
+ it('splices the options helpers right after the discount helpers', () => {
156
+ const code = generateEcommerceProductTransformationCode({})
157
+ const discounts = code.indexOf(ProductDiscounts.generateProductDiscountHelperCode())
158
+ const options = code.indexOf(ProductOptions.generateProductOptionsHelperCode())
159
+ expect(discounts).toBeGreaterThan(-1)
160
+ expect(options).toBe(
161
+ discounts + ProductDiscounts.generateProductDiscountHelperCode().length + 1
162
+ )
163
+ })
164
+ })
165
+
166
+ describe('transform wrapper — option detail on the single-record fetch only', () => {
167
+ // Every lookup fails (no database): the maps fall back to their "unknown" or
168
+ // empty answers, which is all the transform needs here.
169
+ const offline = () => ({
170
+ connect: () => Promise.reject(new Error('offline')),
171
+ query: () => Promise.reject(new Error('offline')),
172
+ end: () => Promise.resolve(),
173
+ })
174
+ const transformRecords = new Function(
175
+ transformCode(0) +
176
+ '\n' +
177
+ getTransformWrapperCode('teleport_products') +
178
+ '\nreturn transformRecords;'
179
+ )() as (
180
+ records: unknown[],
181
+ getClient: () => unknown,
182
+ query: Record<string, unknown>
183
+ ) => Promise<Array<Record<string, unknown>>>
184
+
185
+ const product = (id: string) => ({
186
+ id,
187
+ name: id,
188
+ price: 10,
189
+ option_groups: JSON.stringify(PRINT_GROUPS),
190
+ })
191
+
192
+ it('emits the detail for the details page', async () => {
193
+ const [details] = await transformRecords([product('p1')], offline, {})
194
+ expect((details.optionGroups as unknown[]).length).toBe(1)
195
+ expect(details.optionsFreshKey).toBe('fresh:p1')
196
+ })
197
+
198
+ it('keeps a listing to the flags', async () => {
199
+ const listing = await transformRecords([product('p1'), product('p2')], offline, {})
200
+ for (const card of listing) {
201
+ expect(card.hasProductOptions).toBe('true')
202
+ expect(card.optionGroups).toEqual([])
203
+ expect(card.optionGroupsJson).toBe('[]')
204
+ }
205
+ })
206
+ })
@@ -0,0 +1,82 @@
1
+ import { generateEcommerceProductTransformationCode } from '../src/transformations/ecommerce-product'
2
+ import { generateSharedTransformationCode } from '../src/transformations/shared-utils'
3
+ import { buildProductTransformOptions } from '../src/transformations'
4
+ import type { UIDLEcommerceProductPages } from '@teleporthq/teleport-types'
5
+ import fixtures from './product-page-url-fixtures.json'
6
+
7
+ /**
8
+ * Custom product pages: with `ecommerceSettings.productPages` baked in, every
9
+ * product carries `productPageKey` (the page it opens on, '' = the standard
10
+ * one) and `productPageUrl` (its canonical address) — the product pages'
11
+ * guarded redirect and every product link read them. Without it the transform
12
+ * is exactly what it was. The addresses are pinned against the GUI's own
13
+ * builder by the SAME fixture file (`product-page-url-fixtures.json`, a copy of
14
+ * the GUI's `utils/product-pages/__tests__/fixtures/` one).
15
+ */
16
+
17
+ type BuildProduct = (record: unknown, options?: unknown) => Record<string, unknown>
18
+
19
+ const evalBuildProduct = (productPages?: UIDLEcommerceProductPages): BuildProduct => {
20
+ const code =
21
+ generateSharedTransformationCode() +
22
+ '\n' +
23
+ generateEcommerceProductTransformationCode({ productPages })
24
+ return new Function(code + '\nreturn buildEcommerceProduct;')() as BuildProduct
25
+ }
26
+
27
+ const PRODUCT = { id: '7', name: 'Mug', slug: 'mug', price: 12, currency: 'USD' }
28
+
29
+ describe('ecommerce product transform — product pages', () => {
30
+ it('adds nothing to a store without custom product pages', () => {
31
+ const code = generateEcommerceProductTransformationCode({})
32
+ expect(code).not.toContain('PRODUCT_PAGES')
33
+ expect(code).not.toContain('productPageUrl')
34
+ const product = evalBuildProduct()({ ...PRODUCT, product_page: 'personalise' })
35
+ expect(product).not.toHaveProperty('productPageKey')
36
+ expect(product).not.toHaveProperty('productPageUrl')
37
+ })
38
+
39
+ it("builds every product's page and address exactly as the GUI does", () => {
40
+ const build = evalBuildProduct(fixtures.config as UIDLEcommerceProductPages)
41
+ for (const fixture of fixtures.cases) {
42
+ const product = build({
43
+ ...PRODUCT,
44
+ ...fixture.values,
45
+ product_page: fixture.raw,
46
+ })
47
+ expect({
48
+ name: fixture.name,
49
+ key: product.productPageKey,
50
+ url: product.productPageUrl,
51
+ }).toEqual({
52
+ name: fixture.name,
53
+ key: fixture.key,
54
+ url: fixture.url,
55
+ })
56
+ }
57
+ })
58
+
59
+ it('reads the camel-cased column a non-SQL source hands back', () => {
60
+ const build = evalBuildProduct(fixtures.config as UIDLEcommerceProductPages)
61
+ const product = build({ ...PRODUCT, slug: 'postcard', productPage: 'personalise' })
62
+ expect(product.productPageUrl).toBe('/personalise/postcard')
63
+ })
64
+
65
+ it('bakes routes only — a page name never reaches the site', () => {
66
+ const code = generateEcommerceProductTransformationCode({
67
+ productPages: fixtures.config as UIDLEcommerceProductPages,
68
+ })
69
+ expect(code).not.toContain('Personalise')
70
+ // The helpers add no backtick: the transform is spliced into template literals.
71
+ const backticks = (text: string) => text.split('`').length
72
+ expect(backticks(code)).toBe(backticks(generateEcommerceProductTransformationCode({})))
73
+ })
74
+
75
+ it('takes the config from the exported e-commerce settings', () => {
76
+ const options = buildProductTransformOptions({
77
+ ecommerceSettings: { productPages: fixtures.config },
78
+ } as never)
79
+ expect(options.productPages).toEqual(fixtures.config)
80
+ expect(buildProductTransformOptions({} as never).productPages).toBeUndefined()
81
+ })
82
+ })