@teleporthq/teleport-plugin-next-data-source 0.42.9 → 0.42.10

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 (160) hide show
  1. package/__tests__/csv-header-detection.test.ts +212 -0
  2. package/__tests__/validation.test.ts +33 -2
  3. package/dist/cjs/data-source-fetchers.d.ts +2 -2
  4. package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
  5. package/dist/cjs/data-source-fetchers.js +30 -7
  6. package/dist/cjs/data-source-fetchers.js.map +1 -1
  7. package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
  8. package/dist/cjs/fetchers/airtable.js +1 -1
  9. package/dist/cjs/fetchers/airtable.js.map +1 -1
  10. package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
  11. package/dist/cjs/fetchers/clickhouse.js +1 -1
  12. package/dist/cjs/fetchers/clickhouse.js.map +1 -1
  13. package/dist/cjs/fetchers/csv-file.d.ts.map +1 -1
  14. package/dist/cjs/fetchers/csv-file.js +22 -3
  15. package/dist/cjs/fetchers/csv-file.js.map +1 -1
  16. package/dist/cjs/fetchers/firestore.d.ts.map +1 -1
  17. package/dist/cjs/fetchers/firestore.js +1 -1
  18. package/dist/cjs/fetchers/firestore.js.map +1 -1
  19. package/dist/cjs/fetchers/google-sheets.d.ts.map +1 -1
  20. package/dist/cjs/fetchers/google-sheets.js +6 -1
  21. package/dist/cjs/fetchers/google-sheets.js.map +1 -1
  22. package/dist/cjs/fetchers/javascript.d.ts.map +1 -1
  23. package/dist/cjs/fetchers/javascript.js +1 -1
  24. package/dist/cjs/fetchers/javascript.js.map +1 -1
  25. package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
  26. package/dist/cjs/fetchers/mariadb.js +3 -3
  27. package/dist/cjs/fetchers/mariadb.js.map +1 -1
  28. package/dist/cjs/fetchers/mongodb.d.ts +1 -1
  29. package/dist/cjs/fetchers/mongodb.d.ts.map +1 -1
  30. package/dist/cjs/fetchers/mongodb.js +11 -3
  31. package/dist/cjs/fetchers/mongodb.js.map +1 -1
  32. package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
  33. package/dist/cjs/fetchers/mysql.js +2 -2
  34. package/dist/cjs/fetchers/mysql.js.map +1 -1
  35. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
  36. package/dist/cjs/fetchers/postgresql.js +3 -3
  37. package/dist/cjs/fetchers/postgresql.js.map +1 -1
  38. package/dist/cjs/fetchers/redis.d.ts.map +1 -1
  39. package/dist/cjs/fetchers/redis.js +1 -1
  40. package/dist/cjs/fetchers/redis.js.map +1 -1
  41. package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
  42. package/dist/cjs/fetchers/redshift.js +2 -2
  43. package/dist/cjs/fetchers/redshift.js.map +1 -1
  44. package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
  45. package/dist/cjs/fetchers/rest-api.js +2 -2
  46. package/dist/cjs/fetchers/rest-api.js.map +1 -1
  47. package/dist/cjs/fetchers/static-collection.d.ts.map +1 -1
  48. package/dist/cjs/fetchers/static-collection.js +1 -1
  49. package/dist/cjs/fetchers/static-collection.js.map +1 -1
  50. package/dist/cjs/fetchers/supabase.d.ts.map +1 -1
  51. package/dist/cjs/fetchers/supabase.js +2 -2
  52. package/dist/cjs/fetchers/supabase.js.map +1 -1
  53. package/dist/cjs/fetchers/turso.d.ts.map +1 -1
  54. package/dist/cjs/fetchers/turso.js +1 -1
  55. package/dist/cjs/fetchers/turso.js.map +1 -1
  56. package/dist/cjs/fetchers/utils/header-detection.d.ts +2 -0
  57. package/dist/cjs/fetchers/utils/header-detection.d.ts.map +1 -0
  58. package/dist/cjs/fetchers/utils/header-detection.js +8 -0
  59. package/dist/cjs/fetchers/utils/header-detection.js.map +1 -0
  60. package/dist/cjs/index.d.ts.map +1 -1
  61. package/dist/cjs/index.js +168 -4
  62. package/dist/cjs/index.js.map +1 -1
  63. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  64. package/dist/cjs/pagination-plugin.js +320 -65
  65. package/dist/cjs/pagination-plugin.js.map +1 -1
  66. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  67. package/dist/cjs/utils.d.ts +2 -0
  68. package/dist/cjs/utils.d.ts.map +1 -1
  69. package/dist/cjs/utils.js +214 -46
  70. package/dist/cjs/utils.js.map +1 -1
  71. package/dist/esm/data-source-fetchers.d.ts +2 -2
  72. package/dist/esm/data-source-fetchers.d.ts.map +1 -1
  73. package/dist/esm/data-source-fetchers.js +29 -6
  74. package/dist/esm/data-source-fetchers.js.map +1 -1
  75. package/dist/esm/fetchers/airtable.d.ts.map +1 -1
  76. package/dist/esm/fetchers/airtable.js +2 -2
  77. package/dist/esm/fetchers/airtable.js.map +1 -1
  78. package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
  79. package/dist/esm/fetchers/clickhouse.js +2 -2
  80. package/dist/esm/fetchers/clickhouse.js.map +1 -1
  81. package/dist/esm/fetchers/csv-file.d.ts.map +1 -1
  82. package/dist/esm/fetchers/csv-file.js +23 -4
  83. package/dist/esm/fetchers/csv-file.js.map +1 -1
  84. package/dist/esm/fetchers/firestore.d.ts.map +1 -1
  85. package/dist/esm/fetchers/firestore.js +2 -2
  86. package/dist/esm/fetchers/firestore.js.map +1 -1
  87. package/dist/esm/fetchers/google-sheets.d.ts.map +1 -1
  88. package/dist/esm/fetchers/google-sheets.js +6 -1
  89. package/dist/esm/fetchers/google-sheets.js.map +1 -1
  90. package/dist/esm/fetchers/javascript.d.ts.map +1 -1
  91. package/dist/esm/fetchers/javascript.js +2 -2
  92. package/dist/esm/fetchers/javascript.js.map +1 -1
  93. package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
  94. package/dist/esm/fetchers/mariadb.js +4 -4
  95. package/dist/esm/fetchers/mariadb.js.map +1 -1
  96. package/dist/esm/fetchers/mongodb.d.ts +1 -1
  97. package/dist/esm/fetchers/mongodb.d.ts.map +1 -1
  98. package/dist/esm/fetchers/mongodb.js +12 -4
  99. package/dist/esm/fetchers/mongodb.js.map +1 -1
  100. package/dist/esm/fetchers/mysql.d.ts.map +1 -1
  101. package/dist/esm/fetchers/mysql.js +3 -3
  102. package/dist/esm/fetchers/mysql.js.map +1 -1
  103. package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
  104. package/dist/esm/fetchers/postgresql.js +4 -4
  105. package/dist/esm/fetchers/postgresql.js.map +1 -1
  106. package/dist/esm/fetchers/redis.d.ts.map +1 -1
  107. package/dist/esm/fetchers/redis.js +2 -2
  108. package/dist/esm/fetchers/redis.js.map +1 -1
  109. package/dist/esm/fetchers/redshift.d.ts.map +1 -1
  110. package/dist/esm/fetchers/redshift.js +3 -3
  111. package/dist/esm/fetchers/redshift.js.map +1 -1
  112. package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
  113. package/dist/esm/fetchers/rest-api.js +3 -3
  114. package/dist/esm/fetchers/rest-api.js.map +1 -1
  115. package/dist/esm/fetchers/static-collection.d.ts.map +1 -1
  116. package/dist/esm/fetchers/static-collection.js +2 -2
  117. package/dist/esm/fetchers/static-collection.js.map +1 -1
  118. package/dist/esm/fetchers/supabase.d.ts.map +1 -1
  119. package/dist/esm/fetchers/supabase.js +3 -3
  120. package/dist/esm/fetchers/supabase.js.map +1 -1
  121. package/dist/esm/fetchers/turso.d.ts.map +1 -1
  122. package/dist/esm/fetchers/turso.js +2 -2
  123. package/dist/esm/fetchers/turso.js.map +1 -1
  124. package/dist/esm/fetchers/utils/header-detection.d.ts +2 -0
  125. package/dist/esm/fetchers/utils/header-detection.d.ts.map +1 -0
  126. package/dist/esm/fetchers/utils/header-detection.js +4 -0
  127. package/dist/esm/fetchers/utils/header-detection.js.map +1 -0
  128. package/dist/esm/index.d.ts.map +1 -1
  129. package/dist/esm/index.js +169 -5
  130. package/dist/esm/index.js.map +1 -1
  131. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  132. package/dist/esm/pagination-plugin.js +320 -65
  133. package/dist/esm/pagination-plugin.js.map +1 -1
  134. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  135. package/dist/esm/utils.d.ts +2 -0
  136. package/dist/esm/utils.d.ts.map +1 -1
  137. package/dist/esm/utils.js +211 -45
  138. package/dist/esm/utils.js.map +1 -1
  139. package/package.json +2 -2
  140. package/src/data-source-fetchers.ts +29 -13
  141. package/src/fetchers/airtable.ts +78 -30
  142. package/src/fetchers/clickhouse.ts +85 -18
  143. package/src/fetchers/csv-file.ts +254 -29
  144. package/src/fetchers/firestore.ts +62 -12
  145. package/src/fetchers/google-sheets.ts +147 -30
  146. package/src/fetchers/javascript.ts +102 -23
  147. package/src/fetchers/mariadb.ts +82 -25
  148. package/src/fetchers/mongodb.ts +153 -36
  149. package/src/fetchers/mysql.ts +83 -25
  150. package/src/fetchers/postgresql.ts +86 -26
  151. package/src/fetchers/redis.ts +40 -4
  152. package/src/fetchers/redshift.ts +84 -17
  153. package/src/fetchers/rest-api.ts +101 -24
  154. package/src/fetchers/static-collection.ts +96 -18
  155. package/src/fetchers/supabase.ts +175 -53
  156. package/src/fetchers/turso.ts +84 -17
  157. package/src/fetchers/utils/header-detection.ts +200 -0
  158. package/src/index.ts +248 -2
  159. package/src/pagination-plugin.ts +708 -191
  160. package/src/utils.ts +344 -38
package/src/utils.ts CHANGED
@@ -440,10 +440,10 @@ export default dataSourceModule.handler
440
440
  return
441
441
  }
442
442
 
443
- // Generate fetcher code with BOTH fetchData and handler
443
+ // Generate fetcher code for API route (exports just the handler)
444
444
  let fetcherCode: string
445
445
  try {
446
- fetcherCode = generateDataSourceFetcherWithCore(dataSource, tableName || '')
446
+ fetcherCode = generateDataSourceFetcherWithCore(dataSource, tableName || '', true)
447
447
  } catch (error) {
448
448
  return
449
449
  }
@@ -510,6 +510,19 @@ export const replaceSecretReference = (
510
510
  }
511
511
  }
512
512
 
513
+ export const generateSafeJSONParseCode = (): string => {
514
+ return `const safeJSONParse = (value) => {
515
+ if (!value) return value
516
+ if (typeof value === 'object') return value
517
+ try {
518
+ return JSON.parse(value)
519
+ } catch (e) {
520
+ console.warn('Failed to parse JSON:', e)
521
+ return value
522
+ }
523
+ }`
524
+ }
525
+
513
526
  export const generateDateFormatterCode = (): string => {
514
527
  return `const formatDateValue = (date) => {
515
528
  const options = {
@@ -540,6 +553,110 @@ const dateReplacer = (key, value) => {
540
553
  }`
541
554
  }
542
555
 
556
+ export const generateSortFilterHelperCode = (): string => {
557
+ return `function getNestedValue(obj, path) {
558
+ if (!obj || typeof obj !== 'object') return undefined
559
+ const keys = path.split('.')
560
+ let value = obj
561
+ for (const key of keys) {
562
+ if (value && typeof value === 'object' && key in value) {
563
+ value = value[key]
564
+ } else {
565
+ return undefined
566
+ }
567
+ }
568
+ return value
569
+ }
570
+
571
+ function compareValues(value, target, operand) {
572
+ if (value === null || value === undefined || target === null || target === undefined) {
573
+ switch (operand) {
574
+ case '=': return value == target
575
+ case '!=': return value != target
576
+ default: return false
577
+ }
578
+ }
579
+
580
+ if (typeof value === 'number' && typeof target === 'number') {
581
+ switch (operand) {
582
+ case '=': return value === target
583
+ case '!=': return value !== target
584
+ case '>': return value > target
585
+ case '>=': return value >= target
586
+ case '<': return value < target
587
+ case '<=': return value <= target
588
+ default: return true
589
+ }
590
+ }
591
+
592
+ if (typeof value === 'string' && typeof target === 'string') {
593
+ switch (operand) {
594
+ case '=': return value === target
595
+ case '!=': return value !== target
596
+ case '>': return value > target
597
+ case '>=': return value >= target
598
+ case '<': return value < target
599
+ case '<=': return value <= target
600
+ default: return true
601
+ }
602
+ }
603
+
604
+ const valueDate = value instanceof Date ? value : (typeof value === 'string' ? new Date(value) : null)
605
+ const targetDate = target instanceof Date ? target : (typeof target === 'string' ? new Date(target) : null)
606
+ if (valueDate && targetDate && !isNaN(valueDate.getTime()) && !isNaN(targetDate.getTime())) {
607
+ const valueTime = valueDate.getTime()
608
+ const targetTime = targetDate.getTime()
609
+ switch (operand) {
610
+ case '=': return valueTime === targetTime
611
+ case '!=': return valueTime !== targetTime
612
+ case '>': return valueTime > targetTime
613
+ case '>=': return valueTime >= targetTime
614
+ case '<': return valueTime < targetTime
615
+ case '<=': return valueTime <= targetTime
616
+ default: return true
617
+ }
618
+ }
619
+
620
+ if (Array.isArray(value) && Array.isArray(target)) {
621
+ switch (operand) {
622
+ case '=': return JSON.stringify(value) === JSON.stringify(target)
623
+ case '!=': return JSON.stringify(value) !== JSON.stringify(target)
624
+ case '>': return value.length > target.length
625
+ case '>=': return value.length >= target.length
626
+ case '<': return value.length < target.length
627
+ case '<=': return value.length <= target.length
628
+ default: return true
629
+ }
630
+ }
631
+
632
+ if (Array.isArray(value) && !Array.isArray(target)) {
633
+ switch (operand) {
634
+ case '=': return value.includes(target)
635
+ case '!=': return !value.includes(target)
636
+ default: return false
637
+ }
638
+ }
639
+
640
+ if (typeof value === 'object' && typeof target === 'object') {
641
+ switch (operand) {
642
+ case '=': return JSON.stringify(value) === JSON.stringify(target)
643
+ case '!=': return JSON.stringify(value) !== JSON.stringify(target)
644
+ default: return false
645
+ }
646
+ }
647
+
648
+ switch (operand) {
649
+ case '=': return value == target
650
+ case '!=': return value != target
651
+ case '>': return Number(value) > Number(target)
652
+ case '>=': return Number(value) >= Number(target)
653
+ case '<': return Number(value) < Number(target)
654
+ case '<=': return Number(value) <= Number(target)
655
+ default: return true
656
+ }
657
+ }`
658
+ }
659
+
543
660
  export const sanitizeNumericParam = (value: unknown, defaultValue: number = 0): number => {
544
661
  if (typeof value === 'number' && !isNaN(value) && isFinite(value)) {
545
662
  return Math.max(0, Math.floor(value))
@@ -632,19 +749,42 @@ export const extractDataSourceIntoGetStaticProps = (
632
749
  return { success: false }
633
750
  }
634
751
 
635
- // Generate prop key first
752
+ // Generate prop key using renderPropIdentifier and resource ID for uniqueness
753
+ // This ensures each DataProvider instance gets its own fetch and initialData even when
754
+ // multiple instances share the same renderPropIdentifier but have different params (sorts, filters)
755
+ const renderPropIdentifier = node.content?.renderPropIdentifier
756
+ // tslint:disable-next-line:no-any
757
+ const resourceId = (node.content?.resource as any)?.id
758
+
759
+ // Always generate a unique base key from dataSourceId and tableName
636
760
  const sanitizedDsName = StringUtils.dashCaseToCamelCase(
637
761
  sanitizeFileName(dataSource.name || dataSourceId)
638
762
  )
639
763
  const sanitizedTableName = StringUtils.dashCaseToCamelCase(
640
764
  sanitizeFileName(tableName || 'data')
641
765
  )
642
- const propKey = `${sanitizedDsName}_${sanitizedTableName}_data`
766
+ const baseKey = `${sanitizedDsName}_${sanitizedTableName}_data`
643
767
 
644
- // Find ALL JSX nodes matching this dataSourceId AND tableName and add initialData to ALL of them
768
+ let propKey: string
769
+ if (renderPropIdentifier && resourceId) {
770
+ // Include resource ID to differentiate between same renderProp but different params
771
+ const sanitizedResourceId = StringUtils.dashCaseToCamelCase(
772
+ sanitizeFileName(resourceId).replace(/^TQ_/, '')
773
+ )
774
+ propKey = `${renderPropIdentifier}_${sanitizedResourceId}`
775
+ } else {
776
+ // Use base key for uniqueness (even if renderPropIdentifier exists, base key ensures uniqueness across different data sources)
777
+ propKey = baseKey
778
+ }
779
+
780
+ // Find matching JSX nodes for this data source
781
+ // Strategy depends on whether we have a unique resource ID:
782
+ // - With resource ID: each UIDL node updates ONE JSX element (unique data per element)
783
+ // - Without resource ID: one UIDL node can update ALL matching JSX elements (shared data)
784
+ const hasUniqueResourceId = !!resourceId
645
785
  const matchingJsxNodes: types.JSXElement[] = []
646
786
 
647
- // Helper function to recursively traverse the AST and find all matching JSXElements
787
+ // Helper function to recursively traverse the AST and find matching JSX elements
648
788
  const traverseAST = (astNode: any) => {
649
789
  if (!astNode || typeof astNode !== 'object') {
650
790
  return
@@ -660,7 +800,25 @@ export const extractDataSourceIntoGetStaticProps = (
660
800
  (attr as types.JSXAttribute).name.name === 'resourceDefinition'
661
801
  ) as types.JSXAttribute | undefined
662
802
 
803
+ const nameAttr = attrs.find(
804
+ (attr) =>
805
+ (attr as any).type === 'JSXAttribute' &&
806
+ (attr as types.JSXAttribute).name.name === 'name'
807
+ ) as types.JSXAttribute | undefined
808
+
809
+ const paramsAttr = attrs.find(
810
+ (attr) =>
811
+ (attr as any).type === 'JSXAttribute' &&
812
+ (attr as types.JSXAttribute).name.name === 'params'
813
+ ) as types.JSXAttribute | undefined
814
+
815
+ // Check if this node already has initialData - if so, skip it
816
+ const hasInitialData = attrs.some(
817
+ (attr) => (attr as types.JSXAttribute).name?.name === 'initialData'
818
+ )
819
+
663
820
  if (
821
+ !hasInitialData &&
664
822
  resourceDefAttr &&
665
823
  resourceDefAttr.value &&
666
824
  resourceDefAttr.value.type === 'JSXExpressionContainer'
@@ -673,27 +831,131 @@ export const extractDataSourceIntoGetStaticProps = (
673
831
  // tslint:disable-next-line:no-any
674
832
  const idValue = (idProp as any)?.value?.value
675
833
 
676
- // Also check tableName to ensure we're matching the right data source
677
834
  // tslint:disable-next-line:no-any
678
835
  const tableNameProp = props.find((p: any) => p.key?.value === 'tableName')
679
836
  // tslint:disable-next-line:no-any
680
837
  const tableNameValue = (tableNameProp as any)?.value?.value
681
838
 
682
- if (idValue === dataSourceId && tableNameValue === tableName) {
839
+ // Match by name attribute to ensure we get the right renderPropIdentifier
840
+ let nameMatches = true
841
+ if (renderPropIdentifier && nameAttr && nameAttr.value) {
842
+ nameMatches = false
843
+ if (nameAttr.value.type === 'StringLiteral') {
844
+ nameMatches = nameAttr.value.value === renderPropIdentifier
845
+ } else if (nameAttr.value.type === 'JSXExpressionContainer') {
846
+ const nameExpr = nameAttr.value.expression
847
+ if (nameExpr.type === 'StringLiteral') {
848
+ nameMatches = nameExpr.value === renderPropIdentifier
849
+ }
850
+ }
851
+ }
852
+
853
+ // Check if params match (for nodes with the same renderPropIdentifier but different params)
854
+ // We only compare simple numeric params that affect data fetching (limit, page, perPage)
855
+ // Other params like queryColumns are handled by pagination plugin and don't affect matching
856
+ // tslint:disable-next-line:no-any
857
+ const nodeResourceParams = (node.content as any).resource?.params || {}
858
+ let paramsMatch = true
859
+
860
+ // Extract numeric params from JSX that affect data fetching
861
+ const jsxLimitParams: Record<string, number | undefined> = {}
862
+ let jsxHasParams = false
863
+
864
+ if (
865
+ paramsAttr &&
866
+ paramsAttr.value &&
867
+ paramsAttr.value.type === 'JSXExpressionContainer'
868
+ ) {
869
+ jsxHasParams = true
870
+ const paramsExpr = paramsAttr.value.expression
871
+ let objExpr: types.ObjectExpression | null = null
872
+
873
+ // Handle useMemo wrapper
874
+ if (
875
+ paramsExpr.type === 'CallExpression' &&
876
+ (paramsExpr.callee as any).name === 'useMemo'
877
+ ) {
878
+ const memoArgs = paramsExpr.arguments
879
+ if (memoArgs.length > 0 && memoArgs[0].type === 'ArrowFunctionExpression') {
880
+ const arrowFunc = memoArgs[0] as types.ArrowFunctionExpression
881
+ if (arrowFunc.body.type === 'ObjectExpression') {
882
+ objExpr = arrowFunc.body as types.ObjectExpression
883
+ }
884
+ }
885
+ } else if (paramsExpr.type === 'ObjectExpression') {
886
+ // Direct object expression (before useMemo wrapping)
887
+ objExpr = paramsExpr as types.ObjectExpression
888
+ }
889
+
890
+ if (objExpr) {
891
+ objExpr.properties.forEach((prop: any) => {
892
+ if (prop.type === 'ObjectProperty') {
893
+ let key: string | undefined
894
+ if (prop.key.type === 'Identifier') {
895
+ key = prop.key.name
896
+ } else if (prop.key.type === 'StringLiteral') {
897
+ key = prop.key.value
898
+ }
899
+
900
+ // Only extract limit param - this is what differentiates data fetches
901
+ if (key === 'limit' && prop.value.type === 'NumericLiteral') {
902
+ jsxLimitParams[key] = prop.value.value
903
+ }
904
+ }
905
+ })
906
+ }
907
+ }
908
+
909
+ // Extract limit from resource params
910
+ const resourceLimit =
911
+ nodeResourceParams.limit?.type === 'static'
912
+ ? nodeResourceParams.limit.content
913
+ : undefined
914
+
915
+ // Compare limit params - this determines if data fetches are equivalent
916
+ const jsxLimit = jsxLimitParams.limit
917
+ if (jsxLimit !== undefined || resourceLimit !== undefined) {
918
+ // If either has limit, both must have the same limit value
919
+ paramsMatch = jsxLimit === resourceLimit
920
+ } else if (!jsxHasParams && Object.keys(nodeResourceParams).length > 0) {
921
+ // JSX has no params but resource has params - check if resource only has non-limit params
922
+ // If resource has limit, no match; otherwise match
923
+ paramsMatch = resourceLimit === undefined
924
+ } else if (jsxHasParams && Object.keys(nodeResourceParams).length === 0) {
925
+ // JSX has params but resource doesn't - check if JSX only has non-limit params
926
+ // If JSX has limit, no match; otherwise match
927
+ paramsMatch = jsxLimit === undefined
928
+ }
929
+ // else: both don't have params or both have equivalent limit - match
930
+
931
+ // Collect matching nodes that don't have initialData yet and have matching params
932
+ if (
933
+ idValue === dataSourceId &&
934
+ tableNameValue === tableName &&
935
+ nameMatches &&
936
+ paramsMatch
937
+ ) {
683
938
  matchingJsxNodes.push(jsxElement)
939
+ // If we have a unique resource ID, stop after finding the first match
940
+ // Otherwise, collect all matching nodes
941
+ if (hasUniqueResourceId) {
942
+ return
943
+ }
684
944
  }
685
945
  }
686
946
  }
687
947
  }
688
948
 
689
- // Recursively traverse all properties
690
- for (const key in astNode) {
691
- if (astNode.hasOwnProperty(key)) {
692
- const value = astNode[key]
693
- if (Array.isArray(value)) {
694
- value.forEach((item) => traverseAST(item))
695
- } else if (typeof value === 'object') {
696
- traverseAST(value)
949
+ // Recursively traverse all properties (unless we already found a match with unique resource ID)
950
+ if (!hasUniqueResourceId || matchingJsxNodes.length === 0) {
951
+ for (const key in astNode) {
952
+ if (astNode.hasOwnProperty(key)) {
953
+ const value = astNode[key]
954
+ if (Array.isArray(value)) {
955
+ value.forEach((item) => traverseAST(item))
956
+ } else if (typeof value === 'object') {
957
+ traverseAST(value)
958
+ }
697
959
  }
698
960
  }
699
961
  }
@@ -702,12 +964,13 @@ export const extractDataSourceIntoGetStaticProps = (
702
964
  // Traverse the entire component AST content
703
965
  traverseAST(componentChunk.content)
704
966
 
705
- if (matchingJsxNodes.length === 0) {
706
- return { success: false }
707
- }
967
+ // Even if all JSX nodes already have initialData, we should still add the fetch to getStaticProps
968
+ // This handles cases where multiple data-source-list nodes share the same renderPropIdentifier
969
+ // but have different params (e.g., one with limit, one without)
970
+ const nodesToUpdate: types.JSXElement[] = matchingJsxNodes
708
971
 
709
- // Update ALL matching JSX nodes with initialData
710
- for (const jsxNode of matchingJsxNodes) {
972
+ // Update all target JSX nodes with initialData (only those that don't already have it)
973
+ for (const jsxNode of nodesToUpdate) {
711
974
  // For SSR/SSG with initialData, rename 'children' to 'renderSuccess'
712
975
  const childrenAttrIndex = jsxNode.openingElement.attributes.findIndex(
713
976
  (attr) => (attr as types.JSXAttribute).name?.name === 'children'
@@ -747,6 +1010,7 @@ export const extractDataSourceIntoGetStaticProps = (
747
1010
  )
748
1011
  jsxNode.openingElement.attributes.push(persistDataAttr)
749
1012
  }
1013
+ // End of target node processing
750
1014
 
751
1015
  // Generate safe file name for the fetcher
752
1016
  const fileName = generateSafeFileName(dataSourceType, tableName || 'data', dataSourceId)
@@ -797,23 +1061,64 @@ export const extractDataSourceIntoGetStaticProps = (
797
1061
  let astValue: any
798
1062
 
799
1063
  if (value.content === null || value.content === undefined) {
800
- astValue = types.nullLiteral()
1064
+ // Skip null/undefined values entirely
1065
+ return
801
1066
  } else if (Array.isArray(value.content)) {
802
- // Handle array values (like queryColumns)
803
- astValue = types.arrayExpression(
804
- value.content.map((item: any) => {
805
- if (typeof item === 'string') {
806
- return types.stringLiteral(item)
807
- }
808
- if (typeof item === 'number') {
809
- return types.numericLiteral(item)
810
- }
811
- if (typeof item === 'boolean') {
812
- return types.booleanLiteral(item)
813
- }
814
- return types.nullLiteral()
815
- })
1067
+ // Filter out null/undefined values from the array
1068
+ const validItems = value.content.filter(
1069
+ (item: any) => item !== null && item !== undefined
816
1070
  )
1071
+
1072
+ // Skip the array entirely if it's empty or only had null values
1073
+ if (validItems.length === 0) {
1074
+ return
1075
+ }
1076
+
1077
+ // For sorts, filters, and queryColumns, stringify the array for consistency with API handler
1078
+ if (key === 'sorts' || key === 'filters' || key === 'queryColumns') {
1079
+ const arrayExpr = types.arrayExpression(
1080
+ validItems.map((item: any) => {
1081
+ if (typeof item === 'string') {
1082
+ return types.stringLiteral(item)
1083
+ }
1084
+ if (typeof item === 'number') {
1085
+ return types.numericLiteral(item)
1086
+ }
1087
+ if (typeof item === 'boolean') {
1088
+ return types.booleanLiteral(item)
1089
+ }
1090
+ if (typeof item === 'object' && item !== null) {
1091
+ return ASTUtils.objectToObjectExpression(item)
1092
+ }
1093
+ return types.nullLiteral()
1094
+ })
1095
+ )
1096
+ // Wrap in JSON.stringify()
1097
+ astValue = types.callExpression(
1098
+ types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
1099
+ [arrayExpr]
1100
+ )
1101
+ } else {
1102
+ // Handle other array values normally
1103
+ astValue = types.arrayExpression(
1104
+ validItems.map((item: any) => {
1105
+ if (typeof item === 'string') {
1106
+ return types.stringLiteral(item)
1107
+ }
1108
+ if (typeof item === 'number') {
1109
+ return types.numericLiteral(item)
1110
+ }
1111
+ if (typeof item === 'boolean') {
1112
+ return types.booleanLiteral(item)
1113
+ }
1114
+ if (typeof item === 'object' && item !== null) {
1115
+ // Handle object items (like sort/filter objects)
1116
+ return ASTUtils.objectToObjectExpression(item)
1117
+ }
1118
+ return types.nullLiteral()
1119
+ })
1120
+ )
1121
+ }
817
1122
  } else if (typeof value.content === 'string') {
818
1123
  astValue = types.stringLiteral(value.content)
819
1124
  } else if (typeof value.content === 'number') {
@@ -821,7 +1126,8 @@ export const extractDataSourceIntoGetStaticProps = (
821
1126
  } else if (typeof value.content === 'boolean') {
822
1127
  astValue = types.booleanLiteral(value.content)
823
1128
  } else {
824
- astValue = types.nullLiteral()
1129
+ // Skip other null-like values
1130
+ return
825
1131
  }
826
1132
 
827
1133
  paramsProperties.push(types.objectProperty(types.stringLiteral(key), astValue))
@@ -932,7 +1238,7 @@ export const extractDataSourceIntoGetStaticProps = (
932
1238
  const propsValue = propsObject.value as types.ObjectExpression
933
1239
 
934
1240
  // Check if propKey already exists in the parallel fetch metadata
935
- const parallelFetchMeta = getStaticPropsChunk.meta?.parallelFetch as
1241
+ const parallelFetchMeta = getStaticPropsChunk.meta?.parallelFetchData as
936
1242
  | ParallelFetchMeta
937
1243
  | undefined
938
1244
  const existingInFetchMeta = parallelFetchMeta?.names.includes(propKey)