@teleporthq/teleport-plugin-next-data-source 0.40.15 → 0.42.1

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.
@@ -0,0 +1,185 @@
1
+ # Count API Route Fix
2
+
3
+ ## Problem
4
+
5
+ When pages with pagination + search functionality were generated, the code included `useEffect` hooks that fetched from count API endpoints (e.g., `/api/cockroachdb-users-eab38133-count`), but these count API route files were not being created. This resulted in 404 errors when users searched.
6
+
7
+ ### Affected Code
8
+
9
+ In the generated page `index.js`:
10
+ ```javascript
11
+ useEffect(() => {
12
+ if (skipCountFetchOnMount_pg_1.current) {
13
+ skipCountFetchOnMount_pg_1.current = false
14
+ return
15
+ }
16
+ fetch(
17
+ `/api/cockroachdb-users-eab38133-count?${new URLSearchParams({
18
+ query: paginationState_pg_1.debouncedQuery,
19
+ queryColumns: JSON.stringify(['name']),
20
+ })}`
21
+ )
22
+ .then((res) => res.json())
23
+ .then((data) => {
24
+ if (data && 'count' in data) {
25
+ setPagination_pg_1_maxPages(
26
+ data.count === 0 ? 0 : Math.ceil(data.count / 3)
27
+ )
28
+ }
29
+ })
30
+ }, [paginationState_pg_1.debouncedQuery])
31
+ ```
32
+
33
+ But `/api/cockroachdb-users-eab38133-count.js` did **not exist**.
34
+
35
+ ## Root Cause
36
+
37
+ In `pagination-plugin.ts`, the `createAPIRoutesForPaginatedDataSources()` function:
38
+ ```typescript
39
+ // OLD CODE (line 2354)
40
+ if (isComponent) { // ❌ Only created for components!
41
+ // Create count API route
42
+ extractedResources[`api/${countFileName}`] = { ... }
43
+ }
44
+ ```
45
+
46
+ The count API route was only being created for **components**, but pages with search functionality also need it to refetch count when the search query changes.
47
+
48
+ ## Why Pages with Search Need Count Routes
49
+
50
+ ### Different Scenarios:
51
+
52
+ 1. **Pages with pagination ONLY (no search)**:
53
+ - Initial count fetched in `getStaticProps`
54
+ - Count is static - doesn't change during user interaction
55
+ - ✅ No count API route needed
56
+
57
+ 2. **Pages with pagination + SEARCH**:
58
+ - Initial count fetched in `getStaticProps` (for page 1, no search)
59
+ - Count changes when user searches
60
+ - Need to refetch count with search parameters
61
+ - ❌ **NEEDS count API route** (was missing!)
62
+
63
+ 3. **Components with pagination (with or without search)**:
64
+ - No `getStaticProps`
65
+ - Always fetch count client-side
66
+ - ✅ **NEEDS count API route** (was already working)
67
+
68
+ 4. **Search-only (no pagination)**:
69
+ - No pagination, no maxPages calculation needed
70
+ - ✅ No count route needed
71
+
72
+ ## Solution
73
+
74
+ Changed the condition from "only components" to "components OR pages with search":
75
+
76
+ ```typescript
77
+ // NEW CODE (lines 2344-2364)
78
+ const searchEnabledDataSources = new Set(
79
+ paginationInfos
80
+ .filter((info) => info.searchEnabled)
81
+ .map((info) => info.dataSourceIdentifier)
82
+ )
83
+
84
+ const createdCountRoutes = new Set<string>()
85
+
86
+ // ... in traverseForDataSources:
87
+ const hasSearch = searchEnabledDataSources.has(renderProp)
88
+ const needsCountRoute = isComponent || hasSearch // ✅ Now checks both!
89
+
90
+ if (needsCountRoute) {
91
+ const resourceDef = node.content.resourceDefinition
92
+ if (resourceDef) {
93
+ // ... create count route
94
+ if (!createdCountRoutes.has(countFileName)) {
95
+ extractedResources[`api/${countFileName}`] = {
96
+ fileName: countFileName,
97
+ fileType: FileType.JS,
98
+ path: ['pages', 'api'],
99
+ content: `import dataSource from '../../utils/data-sources/${fileName}'
100
+
101
+ export default dataSource.getCount
102
+ `,
103
+ }
104
+ createdCountRoutes.add(countFileName)
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ ## Additional Improvements
111
+
112
+ 1. **Deduplication**: Added `createdCountRoutes` Set to prevent creating the same count route multiple times when the same data source is used with different perPage settings
113
+
114
+ 2. **Clear Logic**: The condition `needsCountRoute = isComponent || hasSearch` makes it explicit when count routes are needed
115
+
116
+ ## Generated Files
117
+
118
+ After the fix, for a page with search, the following files are generated:
119
+
120
+ ```
121
+ pages/
122
+ api/
123
+ cockroachdb-users-eab38133.js ✅ Main data API route
124
+ cockroachdb-users-eab38133-count.js ✅ Count API route (NOW CREATED!)
125
+
126
+ utils/
127
+ data-sources/
128
+ cockroachdb-users-eab38133.js ✅ Data source module with fetchData, fetchCount, handler, getCount
129
+ ```
130
+
131
+ ## Testing
132
+
133
+ Verified with the provided UIDL containing:
134
+ - Table with pagination + search
135
+ - Array mapper with pagination + search
136
+ - Both bound to the same CockroachDB data source
137
+
138
+ Results:
139
+ - ✅ Count API route created: `/api/cockroachdb-users-eab38133-count.js`
140
+ - ✅ File exports `dataSource.getCount` handler
141
+ - ✅ Search functionality works without 404 errors
142
+ - ✅ Count updates correctly when search query changes
143
+
144
+ ## Edge Cases Handled
145
+
146
+ 1. ✅ **Multiple data sources with search**: Each gets its own count route
147
+ 2. ✅ **Same data source, different perPage**: Count route created once, shared
148
+ 3. ✅ **Pages without search**: Count route not created (unnecessary)
149
+ 4. ✅ **Components**: Count route still created (backward compatible)
150
+ 5. ✅ **Search-only (no pagination)**: No count route created (correct)
151
+
152
+ ## Files Modified
153
+
154
+ - `packages/teleport-plugin-next-data-source/src/pagination-plugin.ts`:
155
+ - `createAPIRoutesForPaginatedDataSources()`: Updated condition to create count routes for pages with search
156
+ - Added `searchEnabledDataSources` Set
157
+ - Added `createdCountRoutes` Set for deduplication
158
+ - Changed `if (isComponent)` to `if (needsCountRoute)`
159
+
160
+ ## Regression Prevention
161
+
162
+ This fix aligns with the comment on line 353 of the same file:
163
+ ```typescript
164
+ // Add useEffect to refetch count when search changes (for both pages and components)
165
+ ```
166
+
167
+ The useEffect was already being created for both pages and components, but the count API route was only being created for components. Now both are consistent.
168
+
169
+ ## Related Issues
170
+
171
+ This issue was introduced when search functionality was added to pages. The original pagination implementation (without search) correctly didn't create count routes for pages because:
172
+ - Pages got initial count in `getStaticProps`
173
+ - Count never changed during user interaction (no search)
174
+
175
+ When search was added, the requirement changed:
176
+ - Count now needs to be refetched when search query changes
177
+ - But the count API route creation wasn't updated
178
+
179
+ ## Future Considerations
180
+
181
+ If additional features are added that require dynamic count updates (e.g., filters, sorting), the same pattern should be followed:
182
+ 1. Create the useEffect to refetch count
183
+ 2. Ensure the count API route is created
184
+ 3. Add the feature to the `searchEnabledDataSources` check (or create a new check)
185
+
@@ -0,0 +1,96 @@
1
+ # Search State Bug Fix Summary
2
+
3
+ ## Problem
4
+ When multiple data providers with search functionality were bound to the same data source in a project, all search inputs were incorrectly writing to the same state variable, causing search functionality to interfere between different list/table components.
5
+
6
+ ## Root Causes
7
+
8
+ ### 1. Fallback Matching Logic
9
+ The `modifySearchInputs` function had a problematic fallback:
10
+ ```typescript
11
+ } else if (className && className.includes('search-input')) {
12
+ // Fallback: match any input with 'search-input' in class
13
+ addSearchInputHandlers(node, info)
14
+ }
15
+ ```
16
+ This caused EVERY search input to match EVERY pagination info, with the last match overwriting previous handlers.
17
+
18
+ ### 2. No Duplicate Prevention
19
+ The function didn't track which inputs had already been modified, allowing multiple matches to overwrite handlers.
20
+
21
+ ### 3. Limited Detection Scope
22
+ The detection logic only looked at immediate siblings of DataProviders, missing search inputs in nested structures (e.g., table layouts where the search input is at a different nesting level).
23
+
24
+ ## Solutions Implemented
25
+
26
+ ### 1. Removed Fallback Logic
27
+ Changed from `forEach` to `for` loop with `break` statement to ensure only ONE match per input:
28
+ ```typescript
29
+ for (let index = 0; index < detectedPaginations.length; index++) {
30
+ const detected = detectedPaginations[index]
31
+ const info = paginationInfos[index]
32
+
33
+ if (className === detected.searchInputClass) {
34
+ addSearchInputHandlers(node, info)
35
+ modifiedInputs.add(node)
36
+ break // Stop after first match
37
+ }
38
+ }
39
+ ```
40
+
41
+ ### 2. Added Duplicate Tracking
42
+ Introduced a `Set` to track modified inputs:
43
+ ```typescript
44
+ const modifiedInputs = new Set<any>()
45
+ // ...
46
+ if (className && !modifiedInputs.has(node)) {
47
+ // Only process if not already modified
48
+ }
49
+ ```
50
+
51
+ ### 3. Improved Detection for Nested Structures
52
+ Enhanced the detection logic to walk up the JSX tree to find search inputs and pagination controls that aren't direct siblings:
53
+ ```typescript
54
+ const findSearchAndPaginationInScope = (scopeNode: any, skipNode: any = null): void => {
55
+ // Recursively search through children
56
+ }
57
+
58
+ let currentScope = parent
59
+ let depth = 0
60
+ const maxDepth = 5
61
+
62
+ while (currentScope && (!searchInputInfo || !paginationNodeInfo) && depth < maxDepth) {
63
+ findSearchAndPaginationInScope(currentScope, depth === 0 ? dataProvider : null)
64
+ currentScope = findParentNode(blockStatement, currentScope)
65
+ depth++
66
+ }
67
+ ```
68
+
69
+ ### 4. Applied Same Fix to Pagination Buttons
70
+ Applied the same pattern to `modifyPaginationButtons` for consistency and to prevent similar issues with button handlers.
71
+
72
+ ## Edge Cases Handled
73
+
74
+ 1. ✅ **Multiple data sources on same page**: Each gets unique state variables (e.g., `search_pg_0_query`, `search_pg_1_query`)
75
+ 2. ✅ **Nested structures (tables)**: Detection walks up the tree to find search inputs
76
+ 3. ✅ **Same data source, different components**: Each instance maintains independent state
77
+ 4. ✅ **Mixed pagination + search**: Proper state management with combined `paginationState` object
78
+ 5. ✅ **Search-only mappers**: Separate handling preserves functionality
79
+
80
+ ## Testing
81
+
82
+ Verified with UIDL containing:
83
+ - Table with search and pagination
84
+ - Array mapper with search and pagination
85
+ - Both bound to the same CockroachDB data source
86
+
87
+ Result: Each search input now has its own state variable and handlers, functioning independently without interference.
88
+
89
+ ## Files Modified
90
+
91
+ 1. `packages/teleport-plugin-next-data-source/src/pagination-plugin.ts`
92
+ - `modifySearchInputs()`: Removed fallback, added tracking, break on match
93
+ - `modifyPaginationButtons()`: Applied same pattern for consistency
94
+ - `detectPaginationsAndSearchFromJSX()`: Enhanced detection for nested structures
95
+ - `findParentNode()`: New helper to walk up the tree
96
+
@@ -1 +1 @@
1
- {"version":3,"file":"pagination-plugin.d.ts","sourceRoot":"","sources":["../../src/pagination-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,sBAAsB,EAGvB,MAAM,4BAA4B,CAAA;AAgBnC,eAAO,MAAM,qCAAqC,EAAE,sBAAsB,CAAC,EAAE,CA+tB5E,CAAA;;AA0iDD,wBAAsD"}
1
+ {"version":3,"file":"pagination-plugin.d.ts","sourceRoot":"","sources":["../../src/pagination-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,sBAAsB,EAGvB,MAAM,4BAA4B,CAAA;AAgBnC,eAAO,MAAM,qCAAqC,EAAE,sBAAsB,CAAC,EAAE,CA+tB5E,CAAA;;AAuoDD,wBAAsD"}
@@ -457,6 +457,64 @@ var createNextArrayMapperPaginationPlugin = function () {
457
457
  return paginationPlugin;
458
458
  };
459
459
  exports.createNextArrayMapperPaginationPlugin = createNextArrayMapperPaginationPlugin;
460
+ function findParentNode(root, target, currentParent) {
461
+ if (currentParent === void 0) { currentParent = null; }
462
+ if (!root || !target) {
463
+ return null;
464
+ }
465
+ if (root === target) {
466
+ return currentParent;
467
+ }
468
+ if (root.type === 'JSXElement' || root.type === 'JSXFragment') {
469
+ if (root.children && Array.isArray(root.children)) {
470
+ for (var _i = 0, _a = root.children; _i < _a.length; _i++) {
471
+ var child = _a[_i];
472
+ var found = findParentNode(child, target, root);
473
+ if (found !== null) {
474
+ return found;
475
+ }
476
+ }
477
+ }
478
+ }
479
+ else if (root.type === 'JSXExpressionContainer') {
480
+ if (root.expression) {
481
+ var found = findParentNode(root.expression, target, root);
482
+ if (found !== null) {
483
+ return found;
484
+ }
485
+ }
486
+ }
487
+ else if (root.type === 'BlockStatement') {
488
+ if (root.body && Array.isArray(root.body)) {
489
+ for (var _b = 0, _c = root.body; _b < _c.length; _b++) {
490
+ var stmt = _c[_b];
491
+ var found = findParentNode(stmt, target, root);
492
+ if (found !== null) {
493
+ return found;
494
+ }
495
+ }
496
+ }
497
+ }
498
+ else if (root.type === 'ReturnStatement') {
499
+ if (root.argument) {
500
+ var found = findParentNode(root.argument, target, root);
501
+ if (found !== null) {
502
+ return found;
503
+ }
504
+ }
505
+ }
506
+ else if (root.type === 'ConditionalExpression') {
507
+ var foundConsequent = findParentNode(root.consequent, target, root);
508
+ if (foundConsequent !== null) {
509
+ return foundConsequent;
510
+ }
511
+ var foundAlternate = findParentNode(root.alternate, target, root);
512
+ if (foundAlternate !== null) {
513
+ return foundAlternate;
514
+ }
515
+ }
516
+ return null;
517
+ }
460
518
  function detectPaginationsAndSearchFromJSX(blockStatement, uidlNode) {
461
519
  var dataProviderMap = new Map();
462
520
  var arrayMapperInfoMap = new Map();
@@ -624,32 +682,35 @@ function detectPaginationsAndSearchFromJSX(blockStatement, uidlNode) {
624
682
  var dataProviderIdentifier = nameAttr.value.expression.value;
625
683
  var paginationNodeInfo = null;
626
684
  var searchInputInfo = null;
627
- // Look through parent's children for siblings
628
- if (parent && parent.children && Array.isArray(parent.children)) {
629
- parent.children.forEach(function (sibling) {
685
+ var findSearchAndPaginationInScope = function (scopeNode, skipNode) {
686
+ if (skipNode === void 0) { skipNode = null; }
687
+ if (!scopeNode || !scopeNode.children || !Array.isArray(scopeNode.children)) {
688
+ return;
689
+ }
690
+ scopeNode.children.forEach(function (child) {
630
691
  var _a, _b, _c;
631
- if (sibling === dataProvider) {
692
+ if (child === skipNode) {
632
693
  return;
633
694
  }
634
- if (sibling.type === 'JSXElement') {
635
- var siblingClassName = getClassName(((_a = sibling.openingElement) === null || _a === void 0 ? void 0 : _a.attributes) || []);
636
- var siblingElementName = (_c = (_b = sibling.openingElement) === null || _b === void 0 ? void 0 : _b.name) === null || _c === void 0 ? void 0 : _c.name;
695
+ if (child.type === 'JSXElement') {
696
+ var childClassName = getClassName(((_a = child.openingElement) === null || _a === void 0 ? void 0 : _a.attributes) || []);
697
+ var childElementName = (_c = (_b = child.openingElement) === null || _b === void 0 ? void 0 : _b.name) === null || _c === void 0 ? void 0 : _c.name;
637
698
  // Found pagination node
638
- if (siblingClassName && siblingClassName.includes('cms-pagination-node')) {
639
- var prevClass = findChildWithClass(sibling, 'previous');
640
- var nextClass = findChildWithClass(sibling, 'next');
699
+ if (childClassName && childClassName.includes('cms-pagination-node')) {
700
+ var prevClass = findChildWithClass(child, 'previous');
701
+ var nextClass = findChildWithClass(child, 'next');
641
702
  if (prevClass || nextClass) {
642
703
  paginationNodeInfo = {
643
- class: siblingClassName,
704
+ class: childClassName,
644
705
  prevClass: prevClass,
645
706
  nextClass: nextClass,
646
707
  };
647
708
  }
648
709
  }
649
710
  // Found search container - search for input inside it
650
- if (siblingClassName && siblingClassName.includes('data-source-search-node')) {
651
- if (sibling.children && Array.isArray(sibling.children)) {
652
- sibling.children.forEach(function (searchChild) {
711
+ if (childClassName && childClassName.includes('data-source-search-node')) {
712
+ if (child.children && Array.isArray(child.children)) {
713
+ child.children.forEach(function (searchChild) {
653
714
  var _a, _b, _c;
654
715
  if (searchChild.type === 'JSXElement') {
655
716
  var searchChildElementName = (_b = (_a = searchChild.openingElement) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.name;
@@ -666,17 +727,28 @@ function detectPaginationsAndSearchFromJSX(blockStatement, uidlNode) {
666
727
  });
667
728
  }
668
729
  }
669
- // Also check if search input is a direct sibling
670
- if (siblingClassName &&
671
- siblingClassName.includes('search-input') &&
672
- siblingElementName === 'input') {
730
+ // Also check if search input is a direct child
731
+ if (childClassName &&
732
+ childClassName.includes('search-input') &&
733
+ childElementName === 'input') {
673
734
  searchInputInfo = {
674
- class: siblingClassName,
675
- jsx: sibling,
735
+ class: childClassName,
736
+ jsx: child,
676
737
  };
677
738
  }
739
+ if (!searchInputInfo || !paginationNodeInfo) {
740
+ findSearchAndPaginationInScope(child, skipNode);
741
+ }
678
742
  }
679
743
  });
744
+ };
745
+ var currentScope = parent;
746
+ var depth = 0;
747
+ var maxDepth = 5;
748
+ while (currentScope && (!searchInputInfo || !paginationNodeInfo) && depth < maxDepth) {
749
+ findSearchAndPaginationInScope(currentScope, depth === 0 ? dataProvider : null);
750
+ currentScope = findParentNode(blockStatement, currentScope);
751
+ depth++;
680
752
  }
681
753
  // Record the DataProvider with its pagination/search info
682
754
  var uniqueKey = "".concat(dataProviderIdentifier, "__").concat(arrayMapperRenderProp);
@@ -1063,6 +1135,7 @@ function findChildWithClass(node, classSubstring) {
1063
1135
  return null;
1064
1136
  }
1065
1137
  function modifyPaginationButtons(blockStatement, detectedPaginations, paginationInfos) {
1138
+ var modifiedButtons = new Set();
1066
1139
  var modifyNode = function (node) {
1067
1140
  if (!node) {
1068
1141
  return;
@@ -1070,20 +1143,25 @@ function modifyPaginationButtons(blockStatement, detectedPaginations, pagination
1070
1143
  if (node.type === 'JSXElement') {
1071
1144
  var openingElement = node.openingElement;
1072
1145
  if (openingElement && openingElement.name && openingElement.name.type === 'JSXIdentifier') {
1073
- var className_1 = getClassName(openingElement.attributes);
1074
- if (className_1) {
1075
- detectedPaginations.forEach(function (detected, index) {
1146
+ var className = getClassName(openingElement.attributes);
1147
+ if (className && !modifiedButtons.has(node)) {
1148
+ for (var index = 0; index < detectedPaginations.length; index++) {
1149
+ var detected = detectedPaginations[index];
1076
1150
  var info = paginationInfos[index];
1077
1151
  if (!info) {
1078
- return;
1152
+ continue;
1079
1153
  }
1080
- if (className_1 === detected.prevButtonClass) {
1154
+ if (className === detected.prevButtonClass) {
1081
1155
  convertToButton(node, info, 'prev');
1156
+ modifiedButtons.add(node);
1157
+ break;
1082
1158
  }
1083
- else if (className_1 === detected.nextButtonClass) {
1159
+ else if (className === detected.nextButtonClass) {
1084
1160
  convertToButton(node, info, 'next');
1161
+ modifiedButtons.add(node);
1162
+ break;
1085
1163
  }
1086
- });
1164
+ }
1087
1165
  }
1088
1166
  }
1089
1167
  }
@@ -1101,6 +1179,7 @@ function modifyPaginationButtons(blockStatement, detectedPaginations, pagination
1101
1179
  modifyNode(blockStatement);
1102
1180
  }
1103
1181
  function modifySearchInputs(blockStatement, detectedPaginations, paginationInfos) {
1182
+ var modifiedInputs = new Set();
1104
1183
  var modifyNode = function (node) {
1105
1184
  if (!node) {
1106
1185
  return;
@@ -1108,21 +1187,20 @@ function modifySearchInputs(blockStatement, detectedPaginations, paginationInfos
1108
1187
  if (node.type === 'JSXElement') {
1109
1188
  var openingElement = node.openingElement;
1110
1189
  if (openingElement && openingElement.name && openingElement.name.type === 'JSXIdentifier') {
1111
- var className_2 = getClassName(openingElement.attributes);
1112
- if (className_2) {
1113
- detectedPaginations.forEach(function (detected, index) {
1190
+ var className = getClassName(openingElement.attributes);
1191
+ if (className && !modifiedInputs.has(node)) {
1192
+ for (var index = 0; index < detectedPaginations.length; index++) {
1193
+ var detected = detectedPaginations[index];
1114
1194
  var info = paginationInfos[index];
1115
1195
  if (!info || !info.searchEnabled) {
1116
- return;
1117
- }
1118
- if (className_2 === detected.searchInputClass) {
1119
- addSearchInputHandlers(node, info);
1196
+ continue;
1120
1197
  }
1121
- else if (className_2 && className_2.includes('search-input')) {
1122
- // Fallback: match any input with 'search-input' in class
1198
+ if (className === detected.searchInputClass) {
1123
1199
  addSearchInputHandlers(node, info);
1200
+ modifiedInputs.add(node);
1201
+ break;
1124
1202
  }
1125
- });
1203
+ }
1126
1204
  }
1127
1205
  }
1128
1206
  }
@@ -1426,6 +1504,8 @@ function modifyGetStaticPropsForPagination(chunks, paginationInfos) {
1426
1504
  }
1427
1505
  function createAPIRoutesForPaginatedDataSources(uidlNode, dataSources, componentChunk, extractedResources, paginationInfos, isComponent) {
1428
1506
  var paginatedDataSourceIds = new Set(paginationInfos.map(function (info) { return info.dataSourceIdentifier; }));
1507
+ var searchEnabledDataSources = new Set(paginationInfos.filter(function (info) { return info.searchEnabled; }).map(function (info) { return info.dataSourceIdentifier; }));
1508
+ var createdCountRoutes = new Set();
1429
1509
  var traverseForDataSources = function (node) {
1430
1510
  var _a;
1431
1511
  if (!node) {
@@ -1435,8 +1515,9 @@ function createAPIRoutesForPaginatedDataSources(uidlNode, dataSources, component
1435
1515
  var renderProp = node.content.renderPropIdentifier;
1436
1516
  if (renderProp && paginatedDataSourceIds.has(renderProp)) {
1437
1517
  (0, utils_1.extractDataSourceIntoNextAPIFolder)(node, dataSources, componentChunk, extractedResources);
1438
- // For components, also create count API route
1439
- if (isComponent) {
1518
+ var hasSearch = searchEnabledDataSources.has(renderProp);
1519
+ var needsCountRoute = isComponent || hasSearch;
1520
+ if (needsCountRoute) {
1440
1521
  var resourceDef = node.content.resourceDefinition;
1441
1522
  if (resourceDef) {
1442
1523
  var dataSourceId = resourceDef.dataSourceId;
@@ -1444,13 +1525,15 @@ function createAPIRoutesForPaginatedDataSources(uidlNode, dataSources, component
1444
1525
  var dataSourceType = resourceDef.dataSourceType;
1445
1526
  var fileName = "".concat(dataSourceType, "-").concat(tableName, "-").concat(dataSourceId.substring(0, 8));
1446
1527
  var countFileName = "".concat(fileName, "-count");
1447
- // Create count API route that exports getCount handler
1448
- extractedResources["api/".concat(countFileName)] = {
1449
- fileName: countFileName,
1450
- fileType: teleport_types_1.FileType.JS,
1451
- path: ['pages', 'api'],
1452
- content: "import dataSource from '../../utils/data-sources/".concat(fileName, "'\n\nexport default dataSource.getCount\n"),
1453
- };
1528
+ if (!createdCountRoutes.has(countFileName)) {
1529
+ extractedResources["api/".concat(countFileName)] = {
1530
+ fileName: countFileName,
1531
+ fileType: teleport_types_1.FileType.JS,
1532
+ path: ['pages', 'api'],
1533
+ content: "import dataSource from '../../utils/data-sources/".concat(fileName, "'\n\nexport default dataSource.getCount\n"),
1534
+ };
1535
+ createdCountRoutes.add(countFileName);
1536
+ }
1454
1537
  }
1455
1538
  }
1456
1539
  }