@teleporthq/teleport-plugin-next-data-source 0.42.34 → 0.43.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 (239) hide show
  1. package/__tests__/ecommerce-product-out-of-stock.test.ts +112 -0
  2. package/__tests__/fetchers.test.ts +0 -42
  3. package/__tests__/filter-utils.test.ts +149 -0
  4. package/__tests__/mocks.ts +0 -12
  5. package/__tests__/utils.test.ts +0 -2
  6. package/dist/cjs/array-mapper-registry.d.ts +2 -0
  7. package/dist/cjs/array-mapper-registry.d.ts.map +1 -1
  8. package/dist/cjs/array-mapper-registry.js +9 -1
  9. package/dist/cjs/array-mapper-registry.js.map +1 -1
  10. package/dist/cjs/count-fetchers.d.ts +2 -2
  11. package/dist/cjs/count-fetchers.d.ts.map +1 -1
  12. package/dist/cjs/count-fetchers.js +5 -5
  13. package/dist/cjs/count-fetchers.js.map +1 -1
  14. package/dist/cjs/data-source-fetchers.d.ts +2 -1
  15. package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
  16. package/dist/cjs/data-source-fetchers.js +11 -9
  17. package/dist/cjs/data-source-fetchers.js.map +1 -1
  18. package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
  19. package/dist/cjs/fetchers/airtable.js +1 -1
  20. package/dist/cjs/fetchers/airtable.js.map +1 -1
  21. package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
  22. package/dist/cjs/fetchers/clickhouse.js +1 -1
  23. package/dist/cjs/fetchers/clickhouse.js.map +1 -1
  24. package/dist/cjs/fetchers/csv-file.js +1 -1
  25. package/dist/cjs/fetchers/csv-file.js.map +1 -1
  26. package/dist/cjs/fetchers/firestore.js +1 -1
  27. package/dist/cjs/fetchers/firestore.js.map +1 -1
  28. package/dist/cjs/fetchers/google-sheets.js +1 -1
  29. package/dist/cjs/fetchers/google-sheets.js.map +1 -1
  30. package/dist/cjs/fetchers/index.d.ts +2 -1
  31. package/dist/cjs/fetchers/index.d.ts.map +1 -1
  32. package/dist/cjs/fetchers/index.js +8 -5
  33. package/dist/cjs/fetchers/index.js.map +1 -1
  34. package/dist/cjs/fetchers/javascript.js +1 -1
  35. package/dist/cjs/fetchers/javascript.js.map +1 -1
  36. package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
  37. package/dist/cjs/fetchers/mariadb.js +3 -3
  38. package/dist/cjs/fetchers/mariadb.js.map +1 -1
  39. package/dist/cjs/fetchers/mongodb.js +1 -1
  40. package/dist/cjs/fetchers/mongodb.js.map +1 -1
  41. package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
  42. package/dist/cjs/fetchers/mysql.js +2 -2
  43. package/dist/cjs/fetchers/mysql.js.map +1 -1
  44. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
  45. package/dist/cjs/fetchers/postgresql.js +2 -2
  46. package/dist/cjs/fetchers/postgresql.js.map +1 -1
  47. package/dist/cjs/fetchers/raw-query.d.ts +18 -0
  48. package/dist/cjs/fetchers/raw-query.d.ts.map +1 -0
  49. package/dist/cjs/fetchers/raw-query.js +70 -0
  50. package/dist/cjs/fetchers/raw-query.js.map +1 -0
  51. package/dist/cjs/fetchers/redis.js +1 -1
  52. package/dist/cjs/fetchers/redis.js.map +1 -1
  53. package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
  54. package/dist/cjs/fetchers/redshift.js +2 -2
  55. package/dist/cjs/fetchers/redshift.js.map +1 -1
  56. package/dist/cjs/fetchers/rest-api.js +1 -1
  57. package/dist/cjs/fetchers/rest-api.js.map +1 -1
  58. package/dist/cjs/fetchers/supabase.d.ts.map +1 -1
  59. package/dist/cjs/fetchers/supabase.js +62 -2
  60. package/dist/cjs/fetchers/supabase.js.map +1 -1
  61. package/dist/cjs/fetchers/teleport.d.ts +7 -0
  62. package/dist/cjs/fetchers/teleport.d.ts.map +1 -0
  63. package/dist/cjs/fetchers/teleport.js +63 -0
  64. package/dist/cjs/fetchers/teleport.js.map +1 -0
  65. package/dist/cjs/fetchers/turso.d.ts.map +1 -1
  66. package/dist/cjs/fetchers/turso.js +1 -1
  67. package/dist/cjs/fetchers/turso.js.map +1 -1
  68. package/dist/cjs/filter-utils.d.ts +13 -0
  69. package/dist/cjs/filter-utils.d.ts.map +1 -0
  70. package/dist/cjs/filter-utils.js +95 -0
  71. package/dist/cjs/filter-utils.js.map +1 -0
  72. package/dist/cjs/index.d.ts.map +1 -1
  73. package/dist/cjs/index.js +112 -9
  74. package/dist/cjs/index.js.map +1 -1
  75. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  76. package/dist/cjs/pagination-plugin.js +389 -128
  77. package/dist/cjs/pagination-plugin.js.map +1 -1
  78. package/dist/cjs/sort-utils.d.ts +10 -0
  79. package/dist/cjs/sort-utils.d.ts.map +1 -0
  80. package/dist/cjs/sort-utils.js +141 -0
  81. package/dist/cjs/sort-utils.js.map +1 -0
  82. package/dist/cjs/transformations/blog-post.d.ts +7 -0
  83. package/dist/cjs/transformations/blog-post.d.ts.map +1 -0
  84. package/dist/cjs/transformations/blog-post.js +13 -0
  85. package/dist/cjs/transformations/blog-post.js.map +1 -0
  86. package/dist/cjs/transformations/ecommerce-product.d.ts +7 -0
  87. package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -0
  88. package/dist/cjs/transformations/ecommerce-product.js +13 -0
  89. package/dist/cjs/transformations/ecommerce-product.js.map +1 -0
  90. package/dist/cjs/transformations/index.d.ts +26 -0
  91. package/dist/cjs/transformations/index.d.ts.map +1 -0
  92. package/dist/cjs/transformations/index.js +81 -0
  93. package/dist/cjs/transformations/index.js.map +1 -0
  94. package/dist/cjs/transformations/shared-utils.d.ts +7 -0
  95. package/dist/cjs/transformations/shared-utils.d.ts.map +1 -0
  96. package/dist/cjs/transformations/shared-utils.js +13 -0
  97. package/dist/cjs/transformations/shared-utils.js.map +1 -0
  98. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  99. package/dist/cjs/utils.d.ts +30 -1
  100. package/dist/cjs/utils.d.ts.map +1 -1
  101. package/dist/cjs/utils.js +173 -10
  102. package/dist/cjs/utils.js.map +1 -1
  103. package/dist/esm/array-mapper-registry.d.ts +2 -0
  104. package/dist/esm/array-mapper-registry.d.ts.map +1 -1
  105. package/dist/esm/array-mapper-registry.js +9 -1
  106. package/dist/esm/array-mapper-registry.js.map +1 -1
  107. package/dist/esm/count-fetchers.d.ts +2 -2
  108. package/dist/esm/count-fetchers.d.ts.map +1 -1
  109. package/dist/esm/count-fetchers.js +4 -4
  110. package/dist/esm/count-fetchers.js.map +1 -1
  111. package/dist/esm/data-source-fetchers.d.ts +2 -1
  112. package/dist/esm/data-source-fetchers.d.ts.map +1 -1
  113. package/dist/esm/data-source-fetchers.js +10 -9
  114. package/dist/esm/data-source-fetchers.js.map +1 -1
  115. package/dist/esm/fetchers/airtable.d.ts.map +1 -1
  116. package/dist/esm/fetchers/airtable.js +1 -1
  117. package/dist/esm/fetchers/airtable.js.map +1 -1
  118. package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
  119. package/dist/esm/fetchers/clickhouse.js +1 -1
  120. package/dist/esm/fetchers/clickhouse.js.map +1 -1
  121. package/dist/esm/fetchers/csv-file.js +1 -1
  122. package/dist/esm/fetchers/csv-file.js.map +1 -1
  123. package/dist/esm/fetchers/firestore.js +1 -1
  124. package/dist/esm/fetchers/firestore.js.map +1 -1
  125. package/dist/esm/fetchers/google-sheets.js +1 -1
  126. package/dist/esm/fetchers/google-sheets.js.map +1 -1
  127. package/dist/esm/fetchers/index.d.ts +2 -1
  128. package/dist/esm/fetchers/index.d.ts.map +1 -1
  129. package/dist/esm/fetchers/index.js +2 -1
  130. package/dist/esm/fetchers/index.js.map +1 -1
  131. package/dist/esm/fetchers/javascript.js +1 -1
  132. package/dist/esm/fetchers/javascript.js.map +1 -1
  133. package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
  134. package/dist/esm/fetchers/mariadb.js +4 -4
  135. package/dist/esm/fetchers/mariadb.js.map +1 -1
  136. package/dist/esm/fetchers/mongodb.js +1 -1
  137. package/dist/esm/fetchers/mongodb.js.map +1 -1
  138. package/dist/esm/fetchers/mysql.d.ts.map +1 -1
  139. package/dist/esm/fetchers/mysql.js +3 -3
  140. package/dist/esm/fetchers/mysql.js.map +1 -1
  141. package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
  142. package/dist/esm/fetchers/postgresql.js +3 -3
  143. package/dist/esm/fetchers/postgresql.js.map +1 -1
  144. package/dist/esm/fetchers/raw-query.d.ts +18 -0
  145. package/dist/esm/fetchers/raw-query.d.ts.map +1 -0
  146. package/dist/esm/fetchers/raw-query.js +65 -0
  147. package/dist/esm/fetchers/raw-query.js.map +1 -0
  148. package/dist/esm/fetchers/redis.js +1 -1
  149. package/dist/esm/fetchers/redis.js.map +1 -1
  150. package/dist/esm/fetchers/redshift.d.ts.map +1 -1
  151. package/dist/esm/fetchers/redshift.js +3 -3
  152. package/dist/esm/fetchers/redshift.js.map +1 -1
  153. package/dist/esm/fetchers/rest-api.js +1 -1
  154. package/dist/esm/fetchers/rest-api.js.map +1 -1
  155. package/dist/esm/fetchers/supabase.d.ts.map +1 -1
  156. package/dist/esm/fetchers/supabase.js +63 -3
  157. package/dist/esm/fetchers/supabase.js.map +1 -1
  158. package/dist/esm/fetchers/teleport.d.ts +7 -0
  159. package/dist/esm/fetchers/teleport.d.ts.map +1 -0
  160. package/dist/esm/fetchers/teleport.js +57 -0
  161. package/dist/esm/fetchers/teleport.js.map +1 -0
  162. package/dist/esm/fetchers/turso.d.ts.map +1 -1
  163. package/dist/esm/fetchers/turso.js +2 -2
  164. package/dist/esm/fetchers/turso.js.map +1 -1
  165. package/dist/esm/filter-utils.d.ts +13 -0
  166. package/dist/esm/filter-utils.d.ts.map +1 -0
  167. package/dist/esm/filter-utils.js +66 -0
  168. package/dist/esm/filter-utils.js.map +1 -0
  169. package/dist/esm/index.d.ts.map +1 -1
  170. package/dist/esm/index.js +113 -10
  171. package/dist/esm/index.js.map +1 -1
  172. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  173. package/dist/esm/pagination-plugin.js +389 -128
  174. package/dist/esm/pagination-plugin.js.map +1 -1
  175. package/dist/esm/sort-utils.d.ts +10 -0
  176. package/dist/esm/sort-utils.d.ts.map +1 -0
  177. package/dist/esm/sort-utils.js +113 -0
  178. package/dist/esm/sort-utils.js.map +1 -0
  179. package/dist/esm/transformations/blog-post.d.ts +7 -0
  180. package/dist/esm/transformations/blog-post.d.ts.map +1 -0
  181. package/dist/esm/transformations/blog-post.js +9 -0
  182. package/dist/esm/transformations/blog-post.js.map +1 -0
  183. package/dist/esm/transformations/ecommerce-product.d.ts +7 -0
  184. package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -0
  185. package/dist/esm/transformations/ecommerce-product.js +9 -0
  186. package/dist/esm/transformations/ecommerce-product.js.map +1 -0
  187. package/dist/esm/transformations/index.d.ts +26 -0
  188. package/dist/esm/transformations/index.d.ts.map +1 -0
  189. package/dist/esm/transformations/index.js +74 -0
  190. package/dist/esm/transformations/index.js.map +1 -0
  191. package/dist/esm/transformations/shared-utils.d.ts +7 -0
  192. package/dist/esm/transformations/shared-utils.d.ts.map +1 -0
  193. package/dist/esm/transformations/shared-utils.js +9 -0
  194. package/dist/esm/transformations/shared-utils.js.map +1 -0
  195. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  196. package/dist/esm/utils.d.ts +30 -1
  197. package/dist/esm/utils.d.ts.map +1 -1
  198. package/dist/esm/utils.js +170 -9
  199. package/dist/esm/utils.js.map +1 -1
  200. package/package.json +6 -5
  201. package/src/array-mapper-registry.ts +13 -0
  202. package/src/count-fetchers.ts +5 -5
  203. package/src/data-source-fetchers.ts +15 -11
  204. package/src/fetchers/airtable.ts +54 -8
  205. package/src/fetchers/clickhouse.ts +25 -19
  206. package/src/fetchers/csv-file.ts +2 -2
  207. package/src/fetchers/firestore.ts +2 -2
  208. package/src/fetchers/google-sheets.ts +2 -2
  209. package/src/fetchers/index.ts +6 -5
  210. package/src/fetchers/javascript.ts +2 -2
  211. package/src/fetchers/mariadb.ts +27 -12
  212. package/src/fetchers/mongodb.ts +2 -2
  213. package/src/fetchers/mysql.ts +27 -12
  214. package/src/fetchers/postgresql.ts +31 -18
  215. package/src/fetchers/raw-query.ts +178 -0
  216. package/src/fetchers/redis.ts +2 -2
  217. package/src/fetchers/redshift.ts +14 -10
  218. package/src/fetchers/rest-api.ts +2 -2
  219. package/src/fetchers/supabase.ts +97 -14
  220. package/src/fetchers/teleport.ts +485 -0
  221. package/src/fetchers/turso.ts +15 -7
  222. package/src/filter-utils.ts +111 -0
  223. package/src/index.ts +146 -6
  224. package/src/pagination-plugin.ts +547 -308
  225. package/src/sort-utils.ts +150 -0
  226. package/src/transformations/blog-post.ts +128 -0
  227. package/src/transformations/ecommerce-product.ts +173 -0
  228. package/src/transformations/index.ts +97 -0
  229. package/src/transformations/shared-utils.ts +271 -0
  230. package/src/utils.ts +227 -11
  231. package/dist/cjs/fetchers/static-collection.d.ts +0 -7
  232. package/dist/cjs/fetchers/static-collection.d.ts.map +0 -1
  233. package/dist/cjs/fetchers/static-collection.js +0 -25
  234. package/dist/cjs/fetchers/static-collection.js.map +0 -1
  235. package/dist/esm/fetchers/static-collection.d.ts +0 -7
  236. package/dist/esm/fetchers/static-collection.d.ts.map +0 -1
  237. package/dist/esm/fetchers/static-collection.js +0 -19
  238. package/dist/esm/fetchers/static-collection.js.map +0 -1
  239. package/src/fetchers/static-collection.ts +0 -231
package/src/index.ts CHANGED
@@ -3,12 +3,15 @@ import {
3
3
  ComponentPluginFactory,
4
4
  FileType,
5
5
  ChunkType,
6
+ UIDLComponentSEO,
7
+ UIDLDynamicReference,
6
8
  } from '@teleporthq/teleport-types'
7
9
  import { UIDLUtils, StringUtils } from '@teleporthq/teleport-shared'
8
10
  import {
9
11
  extractDataSourceIntoNextAPIFolder,
10
12
  extractDataSourceIntoGetStaticProps,
11
13
  sanitizeFileName,
14
+ hasUnresolvableDynamicParams,
12
15
  } from './utils'
13
16
  import { createNextArrayMapperPaginationPlugin } from './pagination-plugin'
14
17
  import * as types from '@babel/types'
@@ -580,15 +583,14 @@ export const createNextPagesDataSourcePlugin: ComponentPluginFactory<{}> = () =>
580
583
  return
581
584
  }
582
585
 
583
- // Check if resource has dynamic parameters
586
+ // Check if resource has dynamic parameters that can't be resolved server-side
587
+ const dynamicRouteAttr = uidl.outputOptions?.dynamicRouteAttribute
584
588
  // tslint:disable-next-line:no-any
585
589
  const hasResourceDynamicParams = dataSourceNode.content.resource?.params
586
- ? Object.values(dataSourceNode.content.resource.params).some(
587
- (param: any) => param.type === 'expr' || param.type === 'dynamic'
588
- )
590
+ ? hasUnresolvableDynamicParams(dataSourceNode.content.resource.params, dynamicRouteAttr)
589
591
  : false
590
592
 
591
- // If no dynamic params, extract to getStaticProps (server-side)
593
+ // If no dynamic params (or all are route-resolvable), extract to getStaticProps (server-side)
592
594
  // Otherwise, extract to API route (client-side)
593
595
  if (!hasResourceDynamicParams) {
594
596
  // extractDataSourceIntoGetStaticProps is called for every UIDL data-source node.
@@ -602,7 +604,9 @@ export const createNextPagesDataSourcePlugin: ComponentPluginFactory<{}> = () =>
602
604
  getStaticPropsChunk,
603
605
  chunks,
604
606
  options.extractedResources,
605
- dependencies
607
+ dependencies,
608
+ dynamicRouteAttr,
609
+ uidl.outputOptions?.folderPath
606
610
  )
607
611
 
608
612
  if (result.success && result.chunk) {
@@ -898,6 +902,14 @@ export const createNextPagesDataSourcePlugin: ComponentPluginFactory<{}> = () =>
898
902
  }
899
903
  }
900
904
 
905
+ // For pages with dynamicRouteAttribute and dynamic SEO references,
906
+ // add the SEO-referenced fields as direct page props from the fetched data.
907
+ // The head config plugin generates props?.metaTitle from refPath: ['metaTitle'],
908
+ // so we need those fields at the top level of props.
909
+ if (uidl.outputOptions?.dynamicRouteAttribute && getStaticPropsChunk && uidl.seo) {
910
+ addDynamicSeoPropsToGetStaticProps(uidl.seo, getStaticPropsChunk)
911
+ }
912
+
901
913
  const paginationPlugin = createNextArrayMapperPaginationPlugin()
902
914
  return paginationPlugin(structure)
903
915
  }
@@ -905,6 +917,134 @@ export const createNextPagesDataSourcePlugin: ComponentPluginFactory<{}> = () =>
905
917
  return nextPagesDataSourcePlugin
906
918
  }
907
919
 
920
+ /**
921
+ * Extracts dynamic SEO reference paths from UIDLComponentSEO and returns the
922
+ * refPath arrays for prop-based dynamic references.
923
+ */
924
+ function extractDynamicSeoRefPaths(seo: UIDLComponentSEO): string[][] {
925
+ const paths: string[][] = []
926
+
927
+ const extractFromValue = (value: unknown) => {
928
+ if (
929
+ typeof value === 'object' &&
930
+ value !== null &&
931
+ 'type' in value &&
932
+ (value as { type: string }).type === 'dynamic'
933
+ ) {
934
+ const dynRef = value as UIDLDynamicReference
935
+ if (dynRef.content.referenceType === 'prop' && dynRef.content.refPath?.length) {
936
+ paths.push(dynRef.content.refPath)
937
+ }
938
+ }
939
+ }
940
+
941
+ if (seo.title) {
942
+ extractFromValue(seo.title)
943
+ }
944
+
945
+ if (seo.metaTags) {
946
+ seo.metaTags.forEach((tag) => {
947
+ Object.values(tag).forEach(extractFromValue)
948
+ })
949
+ }
950
+
951
+ return paths
952
+ }
953
+
954
+ /**
955
+ * For pages with dynamic route attributes and dynamic SEO prop references,
956
+ * augments the getStaticProps return to include SEO fields extracted from
957
+ * the first fetched entity. This enables server-side SEO resolution.
958
+ */
959
+ function addDynamicSeoPropsToGetStaticProps(
960
+ seo: UIDLComponentSEO,
961
+ getStaticPropsChunk: { content: unknown; type: string }
962
+ ): void {
963
+ const seoRefPaths = extractDynamicSeoRefPaths(seo)
964
+ if (seoRefPaths.length === 0) {
965
+ return
966
+ }
967
+
968
+ try {
969
+ const exportDecl = getStaticPropsChunk.content as types.ExportNamedDeclaration
970
+ if (!exportDecl?.declaration || exportDecl.declaration.type !== 'FunctionDeclaration') {
971
+ return
972
+ }
973
+
974
+ const funcBody = exportDecl.declaration.body
975
+ const tryStmt = funcBody.body.find((s) => s.type === 'TryStatement') as types.TryStatement
976
+ if (!tryStmt) {
977
+ return
978
+ }
979
+
980
+ const returnStmt = tryStmt.block.body.find(
981
+ (s) => s.type === 'ReturnStatement'
982
+ ) as types.ReturnStatement
983
+ if (!returnStmt?.argument || returnStmt.argument.type !== 'ObjectExpression') {
984
+ return
985
+ }
986
+
987
+ const propsProperty = returnStmt.argument.properties.find(
988
+ (p) =>
989
+ p.type === 'ObjectProperty' &&
990
+ ((p.key as types.Identifier)?.name === 'props' ||
991
+ (p.key as types.StringLiteral)?.value === 'props')
992
+ ) as types.ObjectProperty | undefined
993
+ if (!propsProperty || propsProperty.value.type !== 'ObjectExpression') {
994
+ return
995
+ }
996
+
997
+ const propsObj = propsProperty.value as types.ObjectExpression
998
+
999
+ // Find the first data prop (non-spread, non-string-keyed) as the source entity.
1000
+ // Exclude spread elements and meta spreads that the data-source plugin adds.
1001
+ const firstDataProp = propsObj.properties.find(
1002
+ (p) => p.type === 'ObjectProperty' && (p.key as types.Identifier)?.name !== undefined
1003
+ ) as types.ObjectProperty | undefined
1004
+
1005
+ if (!firstDataProp) {
1006
+ return
1007
+ }
1008
+
1009
+ const addedSeoFields = new Set<string>()
1010
+
1011
+ for (const refPath of seoRefPaths) {
1012
+ const fieldName = refPath[refPath.length - 1]
1013
+ if (addedSeoFields.has(fieldName)) {
1014
+ continue
1015
+ }
1016
+ addedSeoFields.add(fieldName)
1017
+
1018
+ let memberExpr: types.Expression = types.cloneNode(
1019
+ firstDataProp.value as types.Expression,
1020
+ true
1021
+ )
1022
+
1023
+ // If the prop value is an array (no index access), get the first element
1024
+ const valueStr = JSON.stringify(firstDataProp.value)
1025
+ const hasIndexAccess = valueStr.includes('"computed":true')
1026
+ if (!hasIndexAccess) {
1027
+ memberExpr = types.optionalMemberExpression(memberExpr, types.numericLiteral(0), true, true)
1028
+ }
1029
+
1030
+ for (const segment of refPath) {
1031
+ memberExpr = types.optionalMemberExpression(
1032
+ memberExpr,
1033
+ types.identifier(segment),
1034
+ false,
1035
+ true
1036
+ )
1037
+ }
1038
+
1039
+ const seoValue = types.logicalExpression('??', memberExpr, types.stringLiteral(''))
1040
+
1041
+ propsObj.properties.push(types.objectProperty(types.identifier(fieldName), seoValue))
1042
+ }
1043
+ } catch {
1044
+ // If AST manipulation fails, skip SEO prop injection silently
1045
+ }
1046
+ }
1047
+
908
1048
  // Helper function to stringify complex params (sorts, filters) in DataProvider components
909
1049
  function stringifyComplexParamsInDataProviders(componentChunk: any): void {
910
1050
  if (!componentChunk || !componentChunk.content) {