@teleporthq/teleport-plugin-next-data-source 0.43.75 → 0.44.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 (136) hide show
  1. package/__tests__/_helpers/pagination-widget-fixture.ts +87 -4
  2. package/__tests__/booking-item-transform.test.ts +120 -0
  3. package/__tests__/browser-row-policy.test.ts +8 -2
  4. package/__tests__/fetchers.test.ts +1 -1
  5. package/__tests__/fixed-list-sort.test.ts +1 -1
  6. package/__tests__/infinite-scroll.test.ts +154 -5
  7. package/__tests__/list-sort-order.test.ts +61 -0
  8. package/__tests__/null-filter-where.test.ts +132 -0
  9. package/__tests__/pg-date-parser.test.ts +73 -0
  10. package/__tests__/plain-list-sort.test.ts +1 -1
  11. package/__tests__/product-price-sort.ts +11 -6
  12. package/__tests__/range-control.test.ts +159 -0
  13. package/__tests__/search-input-marker.test.ts +86 -0
  14. package/__tests__/search-url-sync.test.ts +34 -8
  15. package/__tests__/table-read-route-protected-tables.test.ts +33 -3
  16. package/dist/cjs/count-effect.d.ts +7 -1
  17. package/dist/cjs/count-effect.d.ts.map +1 -1
  18. package/dist/cjs/count-effect.js +15 -5
  19. package/dist/cjs/count-effect.js.map +1 -1
  20. package/dist/cjs/fetchers/pg-client-import.d.ts +12 -0
  21. package/dist/cjs/fetchers/pg-client-import.d.ts.map +1 -0
  22. package/dist/cjs/fetchers/pg-client-import.js +15 -0
  23. package/dist/cjs/fetchers/pg-client-import.js.map +1 -0
  24. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
  25. package/dist/cjs/fetchers/postgresql.js +3 -1
  26. package/dist/cjs/fetchers/postgresql.js.map +1 -1
  27. package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
  28. package/dist/cjs/fetchers/raw-query.js +2 -1
  29. package/dist/cjs/fetchers/raw-query.js.map +1 -1
  30. package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
  31. package/dist/cjs/fetchers/teleport.js +4 -2
  32. package/dist/cjs/fetchers/teleport.js.map +1 -1
  33. package/dist/cjs/infinite-scroll.d.ts +25 -2
  34. package/dist/cjs/infinite-scroll.d.ts.map +1 -1
  35. package/dist/cjs/infinite-scroll.js +144 -33
  36. package/dist/cjs/infinite-scroll.js.map +1 -1
  37. package/dist/cjs/list-sort-order.d.ts +14 -0
  38. package/dist/cjs/list-sort-order.d.ts.map +1 -0
  39. package/dist/cjs/list-sort-order.js +36 -0
  40. package/dist/cjs/list-sort-order.js.map +1 -0
  41. package/dist/cjs/pagination-controls.d.ts +22 -3
  42. package/dist/cjs/pagination-controls.d.ts.map +1 -1
  43. package/dist/cjs/pagination-controls.js +29 -3
  44. package/dist/cjs/pagination-controls.js.map +1 -1
  45. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  46. package/dist/cjs/pagination-plugin.js +137 -101
  47. package/dist/cjs/pagination-plugin.js.map +1 -1
  48. package/dist/cjs/range-control.d.ts +45 -0
  49. package/dist/cjs/range-control.d.ts.map +1 -0
  50. package/dist/cjs/range-control.js +165 -0
  51. package/dist/cjs/range-control.js.map +1 -0
  52. package/dist/cjs/row-order.d.ts +3 -2
  53. package/dist/cjs/row-order.d.ts.map +1 -1
  54. package/dist/cjs/row-order.js +5 -4
  55. package/dist/cjs/row-order.js.map +1 -1
  56. package/dist/cjs/search-input.d.ts +42 -0
  57. package/dist/cjs/search-input.d.ts.map +1 -0
  58. package/dist/cjs/search-input.js +158 -0
  59. package/dist/cjs/search-input.js.map +1 -0
  60. package/dist/cjs/transformations/booking-item.d.ts +24 -0
  61. package/dist/cjs/transformations/booking-item.d.ts.map +1 -0
  62. package/dist/cjs/transformations/booking-item.js +27 -0
  63. package/dist/cjs/transformations/booking-item.js.map +1 -0
  64. package/dist/cjs/transformations/index.d.ts +4 -2
  65. package/dist/cjs/transformations/index.d.ts.map +1 -1
  66. package/dist/cjs/transformations/index.js +14 -5
  67. package/dist/cjs/transformations/index.js.map +1 -1
  68. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  69. package/dist/esm/count-effect.d.ts +7 -1
  70. package/dist/esm/count-effect.d.ts.map +1 -1
  71. package/dist/esm/count-effect.js +15 -5
  72. package/dist/esm/count-effect.js.map +1 -1
  73. package/dist/esm/fetchers/pg-client-import.d.ts +12 -0
  74. package/dist/esm/fetchers/pg-client-import.d.ts.map +1 -0
  75. package/dist/esm/fetchers/pg-client-import.js +12 -0
  76. package/dist/esm/fetchers/pg-client-import.js.map +1 -0
  77. package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
  78. package/dist/esm/fetchers/postgresql.js +3 -1
  79. package/dist/esm/fetchers/postgresql.js.map +1 -1
  80. package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
  81. package/dist/esm/fetchers/raw-query.js +2 -1
  82. package/dist/esm/fetchers/raw-query.js.map +1 -1
  83. package/dist/esm/fetchers/teleport.d.ts.map +1 -1
  84. package/dist/esm/fetchers/teleport.js +4 -2
  85. package/dist/esm/fetchers/teleport.js.map +1 -1
  86. package/dist/esm/infinite-scroll.d.ts +25 -2
  87. package/dist/esm/infinite-scroll.d.ts.map +1 -1
  88. package/dist/esm/infinite-scroll.js +142 -32
  89. package/dist/esm/infinite-scroll.js.map +1 -1
  90. package/dist/esm/list-sort-order.d.ts +14 -0
  91. package/dist/esm/list-sort-order.d.ts.map +1 -0
  92. package/dist/esm/list-sort-order.js +32 -0
  93. package/dist/esm/list-sort-order.js.map +1 -0
  94. package/dist/esm/pagination-controls.d.ts +22 -3
  95. package/dist/esm/pagination-controls.d.ts.map +1 -1
  96. package/dist/esm/pagination-controls.js +28 -2
  97. package/dist/esm/pagination-controls.js.map +1 -1
  98. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  99. package/dist/esm/pagination-plugin.js +138 -102
  100. package/dist/esm/pagination-plugin.js.map +1 -1
  101. package/dist/esm/range-control.d.ts +45 -0
  102. package/dist/esm/range-control.d.ts.map +1 -0
  103. package/dist/esm/range-control.js +137 -0
  104. package/dist/esm/range-control.js.map +1 -0
  105. package/dist/esm/row-order.d.ts +3 -2
  106. package/dist/esm/row-order.d.ts.map +1 -1
  107. package/dist/esm/row-order.js +5 -4
  108. package/dist/esm/row-order.js.map +1 -1
  109. package/dist/esm/search-input.d.ts +42 -0
  110. package/dist/esm/search-input.d.ts.map +1 -0
  111. package/dist/esm/search-input.js +129 -0
  112. package/dist/esm/search-input.js.map +1 -0
  113. package/dist/esm/transformations/booking-item.d.ts +24 -0
  114. package/dist/esm/transformations/booking-item.d.ts.map +1 -0
  115. package/dist/esm/transformations/booking-item.js +23 -0
  116. package/dist/esm/transformations/booking-item.js.map +1 -0
  117. package/dist/esm/transformations/index.d.ts +4 -2
  118. package/dist/esm/transformations/index.d.ts.map +1 -1
  119. package/dist/esm/transformations/index.js +14 -5
  120. package/dist/esm/transformations/index.js.map +1 -1
  121. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  122. package/package.json +5 -5
  123. package/src/count-effect.ts +31 -8
  124. package/src/fetchers/pg-client-import.ts +16 -0
  125. package/src/fetchers/postgresql.ts +15 -9
  126. package/src/fetchers/raw-query.ts +2 -1
  127. package/src/fetchers/teleport.ts +11 -8
  128. package/src/infinite-scroll.ts +240 -79
  129. package/src/list-sort-order.ts +34 -0
  130. package/src/pagination-controls.ts +26 -3
  131. package/src/pagination-plugin.ts +163 -135
  132. package/src/range-control.ts +207 -0
  133. package/src/row-order.ts +5 -4
  134. package/src/search-input.ts +190 -0
  135. package/src/transformations/booking-item.ts +71 -0
  136. package/src/transformations/index.ts +25 -2
@@ -85,6 +85,8 @@ var loading_state_1 = require("./loading-state");
85
85
  var config_1 = require("./cache/config");
86
86
  var ast_1 = require("./cache/ast");
87
87
  var pagination_controls_1 = require("./pagination-controls");
88
+ var range_control_1 = require("./range-control");
89
+ var search_input_1 = require("./search-input");
88
90
  var count_effect_1 = require("./count-effect");
89
91
  var search_clear_1 = require("./search-clear");
90
92
  var numbered_pagination_1 = require("./numbered-pagination");
@@ -433,6 +435,7 @@ function buildStateRegistry(uidlNode) {
433
435
  index: index++,
434
436
  dataSourceIdentifier: parentDataSource.identifier,
435
437
  arrayMapperRenderProp: arrayMapperRenderProp,
438
+ nodeId: typeof content.nodeId === 'string' ? content.nodeId : undefined,
436
439
  resourceDefinition: {
437
440
  dataSourceId: parentDataSource.resourceDef.dataSourceId || '',
438
441
  tableName: parentDataSource.resourceDef.tableName || '',
@@ -528,6 +531,8 @@ function getStateVarsForUsage(usage) {
528
531
  setPageStateVar: "setDs_".concat(idx, "_page"),
529
532
  maxPagesStateVar: "ds_".concat(idx, "_maxPages"),
530
533
  setMaxPagesStateVar: "setDs_".concat(idx, "_maxPages"),
534
+ totalStateVar: "ds_".concat(idx, "_total"),
535
+ setTotalStateVar: "setDs_".concat(idx, "_total"),
531
536
  searchQueryVar: "ds_".concat(idx, "_searchQuery"),
532
537
  setSearchQueryVar: "setDs_".concat(idx, "_searchQuery"),
533
538
  debouncedSearchQueryVar: "ds_".concat(idx, "_debouncedQuery"),
@@ -605,19 +610,27 @@ function pushSearchUrlSyncEffects(effectStatements, usage, vars) {
605
610
  effectStatements.push(teleport_plugin_common_1.URLSearchParamSync.buildUrlWriteBackEffect(usage.searchUrlParamKey, buildDebouncedValueExpr(), buildDebouncedValueExpr(), undefined, adoptedRefVar));
606
611
  // read-back: `?<searchUrlParamKey>=` → the input AND the applied query at once
607
612
  // (`ds_N_adoptUrlSearch`), so an address is not a typed change: it skips the
608
- // debounce and keeps the page — `?searchKeyword=x&page=3` lands on page 3.
613
+ // debounce and keeps a page the address carries — `?searchKeyword=x&page=3`
614
+ // lands on page 3.
609
615
  effectStatements.push(teleport_plugin_common_1.URLSearchParamSync.buildUrlReadBackEffect(usage.searchUrlParamKey, adopterVar, undefined, adoptedRefVar));
610
616
  }
611
617
  // `const ds_N_adoptUrlSearch = (update) => { … }`: the setter the search's URL
612
618
  // read-back calls with its `(prev) => next` updater. It moves the input and the
613
- // applied (debounced) query together; the combined paginated state keeps its
614
- // page, which a typed change resets to 1 in the debounce effect.
619
+ // applied (debounced) query together. A list whose page is in the address
620
+ // keeps its page — the page sync sets it from the same address. Any other
621
+ // list starts the new query on page 1, as a typed change does in the debounce
622
+ // effect: an appending list has usually advanced past page 1 by the time the
623
+ // router is ready, and the query's first answer would otherwise be its page 2
624
+ // — empty for a query only a few rows match.
615
625
  function buildUrlSearchAdopter(adopterVar, usage, vars) {
616
626
  var update = types.identifier('update');
627
+ var pageReset = usageHasPageUrlSync(usage)
628
+ ? []
629
+ : [types.objectProperty(types.identifier('page'), types.numericLiteral(1))];
617
630
  var applyToDebounced = usage.category === 'paginated+search'
618
631
  ? // setDs_N_state((state) => {
619
632
  // const next = update(state.debouncedQuery)
620
- // return next === state.debouncedQuery ? state : { ...state, debouncedQuery: next }
633
+ // return next === state.debouncedQuery ? state : { ...state, page: 1, debouncedQuery: next }
621
634
  // })
622
635
  types.callExpression(types.identifier(vars.setCombinedStateVar), [
623
636
  types.arrowFunctionExpression([types.identifier('state')], types.blockStatement([
@@ -626,10 +639,11 @@ function buildUrlSearchAdopter(adopterVar, usage, vars) {
626
639
  types.memberExpression(types.identifier('state'), types.identifier('debouncedQuery')),
627
640
  ])),
628
641
  ]),
629
- types.returnStatement(types.conditionalExpression(types.binaryExpression('===', types.identifier('next'), types.memberExpression(types.identifier('state'), types.identifier('debouncedQuery'))), types.identifier('state'), types.objectExpression([
630
- types.spreadElement(types.identifier('state')),
642
+ types.returnStatement(types.conditionalExpression(types.binaryExpression('===', types.identifier('next'), types.memberExpression(types.identifier('state'), types.identifier('debouncedQuery'))), types.identifier('state'), types.objectExpression(__spreadArray(__spreadArray([
643
+ types.spreadElement(types.identifier('state'))
644
+ ], pageReset, true), [
631
645
  types.objectProperty(types.identifier('debouncedQuery'), types.identifier('next')),
632
- ]))),
646
+ ], false)))),
633
647
  ])),
634
648
  ])
635
649
  : types.callExpression(types.identifier(vars.setDebouncedSearchQueryVar), [update]);
@@ -792,7 +806,7 @@ function pushFilterSortPageResetEffect(stateDeclarations, effectStatements, usag
792
806
  // ==================== MAIN PLUGIN ====================
793
807
  var createNextArrayMapperPaginationPlugin = function () {
794
808
  var paginationPlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () {
795
- var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, registry, getStaticPropsChunk, isPage, localized, stateDeclarations, effectStatements, needsUseRouter, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, loadingStateDeclarations, usagesWithLoadingState, usageByProviderNode, cacheDeclarations, sharedOptions, cachedScopes, dataProvidersWithoutRepeaters, searchInputs, searchEnabledUsages, usageBySearchInput;
809
+ var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, rangeControls, hasRangeControl, registry, getStaticPropsChunk, isPage, localized, stateDeclarations, effectStatements, needsUseRouter, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, loadingStateDeclarations, usagesWithLoadingState, usageByProviderNode, cacheDeclarations, sharedOptions, cachedScopes, dataProvidersWithoutRepeaters, searchInputs, usageBySearchInput, _i, searchInputs_1, input, usage;
796
810
  var _a, _b;
797
811
  return __generator(this, function (_c) {
798
812
  uidl = structure.uidl, chunks = structure.chunks, dependencies = structure.dependencies, options = structure.options;
@@ -816,6 +830,8 @@ var createNextArrayMapperPaginationPlugin = function () {
816
830
  return [2 /*return*/, structure];
817
831
  }
818
832
  blockStatement = arrowFunction.body;
833
+ rangeControls = (0, range_control_1.findRangeControls)(blockStatement);
834
+ hasRangeControl = rangeControls.length > 0;
819
835
  registry = buildStateRegistry(uidl.node);
820
836
  if (registry.usages.length === 0) {
821
837
  return [2 /*return*/, structure];
@@ -984,11 +1000,15 @@ var createNextArrayMapperPaginationPlugin = function () {
984
1000
  (0, filter_utils_1.pushPropIdsAsDeps)(countEffectDeps, countEffectSeen, usage.filterPropIds);
985
1001
  // Same goes for URL-driven filters (already documented above).
986
1002
  pushUrlSearchParamMemoDeps(countEffectDeps, usage);
1003
+ if (hasRangeControl) {
1004
+ stateDeclarations.push(buildTotalDeclaration(vars));
1005
+ }
987
1006
  stateDeclarations.push((0, count_effect_1.buildCountSeqDeclaration)(vars.countSeqRefVar));
988
1007
  effectStatements.push((0, count_effect_1.buildCountFetchEffect)({
989
1008
  fileName: fileName,
990
1009
  perPage: usage.perPage,
991
1010
  setMaxPagesVar: vars.setMaxPagesStateVar,
1011
+ setTotalVar: hasRangeControl ? vars.setTotalStateVar : undefined,
992
1012
  urlParams: urlParams,
993
1013
  deps: countEffectDeps,
994
1014
  countSeqRefVar: vars.countSeqRefVar,
@@ -1030,7 +1050,9 @@ var createNextArrayMapperPaginationPlugin = function () {
1030
1050
  // "Next" button, but not to label a numbered strip or to know when an
1031
1051
  // infinite list has reached its end, so those two modes fetch a live
1032
1052
  // count as well.
1033
- var needsLiveCount = !isPage || usage.paginationMode === 'numbered' || usage.infiniteScroll;
1053
+ // A range text needs the live count too: the build-time count only
1054
+ // ever became a page count, never the total it shows.
1055
+ var needsLiveCount = !isPage || usage.paginationMode === 'numbered' || usage.infiniteScroll || hasRangeControl;
1034
1056
  if (needsLiveCount) {
1035
1057
  var fileName = (0, utils_1.generateSafeFileName)(usage.resourceDefinition.dataSourceType, usage.resourceDefinition.tableName, usage.resourceDefinition.dataSourceId);
1036
1058
  // ⛔ This list has no search input, so no `query` param — but it may
@@ -1052,11 +1074,15 @@ var createNextArrayMapperPaginationPlugin = function () {
1052
1074
  (0, filter_utils_1.pushStateIdsAsDeps)(countDeps, countDepsSeen, usage.filterStateIds);
1053
1075
  (0, filter_utils_1.pushPropIdsAsDeps)(countDeps, countDepsSeen, usage.filterPropIds);
1054
1076
  pushUrlSearchParamMemoDeps(countDeps, usage);
1077
+ if (hasRangeControl) {
1078
+ stateDeclarations.push(buildTotalDeclaration(vars));
1079
+ }
1055
1080
  stateDeclarations.push((0, count_effect_1.buildCountSeqDeclaration)(vars.countSeqRefVar));
1056
1081
  effectStatements.push((0, count_effect_1.buildCountFetchEffect)({
1057
1082
  fileName: fileName,
1058
1083
  perPage: usage.perPage,
1059
1084
  setMaxPagesVar: vars.setMaxPagesStateVar,
1085
+ setTotalVar: hasRangeControl ? vars.setTotalStateVar : undefined,
1060
1086
  urlParams: countUrlParams,
1061
1087
  deps: countDeps,
1062
1088
  countSeqRefVar: vars.countSeqRefVar,
@@ -1187,7 +1213,9 @@ var createNextArrayMapperPaginationPlugin = function () {
1187
1213
  // An auto-loading list needs something at the end of the rows for its
1188
1214
  // IntersectionObserver to watch.
1189
1215
  if (usage.infiniteScroll && usage.paginated && !usage.infiniteScrollLoadMore) {
1190
- insertSentinelAfterProvider(blockStatement, dp, (0, infinite_scroll_1.buildSentinelElement)((0, infinite_scroll_1.getInfiniteScrollVars)(usage.index)));
1216
+ insertSentinelAfterProvider(blockStatement, dp, function (parentTag) {
1217
+ return (0, infinite_scroll_1.buildSentinelElement)((0, infinite_scroll_1.getInfiniteScrollVars)(usage.index), parentTag);
1218
+ });
1191
1219
  }
1192
1220
  // Create API route for all categories (including 'plain' for components)
1193
1221
  ensureAPIRouteExists(options.extractedResources, usage, options.dataSources, (0, transformations_1.buildProductTransformOptions)(options));
@@ -1228,18 +1256,15 @@ var createNextArrayMapperPaginationPlugin = function () {
1228
1256
  dataProvidersWithoutRepeaters.forEach(function (dp) {
1229
1257
  stabilizeDataProviderWithoutRepeater(dp);
1230
1258
  });
1231
- searchInputs = findAllSearchInputsInJSX(blockStatement);
1232
- searchEnabledUsages = registry.usages.filter(function (u) { return u.searchEnabled; });
1233
- usageBySearchInput = new Map();
1234
- searchInputs.forEach(function (input, idx) {
1235
- if (idx >= searchEnabledUsages.length) {
1236
- return;
1259
+ searchInputs = (0, search_input_1.findAllSearchInputsInJSX)(blockStatement);
1260
+ usageBySearchInput = (0, search_input_1.pairSearchInputs)(searchInputs, registry.usages);
1261
+ for (_i = 0, searchInputs_1 = searchInputs; _i < searchInputs_1.length; _i++) {
1262
+ input = searchInputs_1[_i];
1263
+ usage = usageBySearchInput.get(input.node);
1264
+ if (usage) {
1265
+ (0, search_input_1.wireSearchInput)(input, getStateVarsForUsage(usage));
1237
1266
  }
1238
- var usage = searchEnabledUsages[idx];
1239
- var vars = getStateVarsForUsage(usage);
1240
- wireSearchInput(input.node, vars);
1241
- usageBySearchInput.set(input.node, usage);
1242
- });
1267
+ }
1243
1268
  // STEP 4.5: Wire the clear buttons, each to the input it shares a parent with.
1244
1269
  (0, search_clear_1.findAllSearchClearButtonsInJSX)(blockStatement).forEach(function (button) {
1245
1270
  var input = (0, search_clear_1.findSiblingSearchInput)(button, searchInputs.map(function (entry) { return entry.node; }));
@@ -1266,6 +1291,24 @@ var createNextArrayMapperPaginationPlugin = function () {
1266
1291
  }
1267
1292
  wirePaginationWidget(node, usage, getStateVarsForUsage(usage), usagesWithLoadingState.has(usage.index));
1268
1293
  });
1294
+ // STEP 5b: Wire the range texts that name their mapper.
1295
+ //
1296
+ // A `range` control carrying `data-tq-pagination-for` belongs to the mapper
1297
+ // whose editor node id it names, wherever it sits — a template's footer or
1298
+ // toolbar is laid out by the author, not inside a pagination container, so
1299
+ // the container-ownership rule above cannot reach it. One without the
1300
+ // attribute was wired by its container in STEP 5; one naming a mapper this
1301
+ // component does not render stays as drawn.
1302
+ rangeControls.forEach(function (control) {
1303
+ var ownerNodeId = (0, pagination_controls_1.readJSXAttributeString)(control, pagination_controls_1.PAGINATION_FOR_ATTR);
1304
+ if (!ownerNodeId) {
1305
+ return;
1306
+ }
1307
+ var owner = registry.usages.find(function (usage) { return usage.nodeId === ownerNodeId; });
1308
+ if (owner) {
1309
+ wireRangeControlFor(control, owner, getStateVarsForUsage(owner));
1310
+ }
1311
+ });
1269
1312
  // STEP 6: Update getStaticProps if this is a page
1270
1313
  if (isPage) {
1271
1314
  updateGetStaticProps(chunks, registry, dependencies, (_b = uidl.outputOptions) === null || _b === void 0 ? void 0 : _b.folderPath, localized);
@@ -1284,39 +1327,59 @@ exports.createNextArrayMapperPaginationPlugin = createNextArrayMapperPaginationP
1284
1327
  * It has to be a sibling: the provider's own child is a render-prop function,
1285
1328
  * and anything placed inside it would be re-created on every fetch — the
1286
1329
  * observer would then be watching an element that is no longer in the document.
1330
+ * The sentinel is built for the element it lands in (`buildSentinelElement`):
1331
+ * a fragment is transparent, a component's children may go anywhere.
1287
1332
  *
1288
1333
  * Returns false when the provider has no element parent to splice into (a
1289
1334
  * provider returned directly from the component), leaving the list on its
1290
1335
  * count-driven `hasMore` rather than injecting a stray node.
1291
1336
  */
1292
- function insertSentinelAfterProvider(blockStatement, provider, sentinel) {
1337
+ function insertSentinelAfterProvider(blockStatement, provider, buildSentinel) {
1293
1338
  var inserted = false;
1294
- var traverse = function (node) {
1339
+ var elementTagOf = function (node, enclosing) {
1340
+ var _a;
1341
+ if ((node === null || node === void 0 ? void 0 : node.type) === 'JSXFragment') {
1342
+ return enclosing;
1343
+ }
1344
+ if ((node === null || node === void 0 ? void 0 : node.type) !== 'JSXElement') {
1345
+ return enclosing;
1346
+ }
1347
+ var name = (_a = node.openingElement) === null || _a === void 0 ? void 0 : _a.name;
1348
+ if ((name === null || name === void 0 ? void 0 : name.type) !== 'JSXIdentifier') {
1349
+ return null;
1350
+ }
1351
+ if (name.name === 'Fragment') {
1352
+ return enclosing;
1353
+ }
1354
+ return /^[a-z]/.test(name.name) ? name.name : null;
1355
+ };
1356
+ var traverse = function (node, enclosing) {
1295
1357
  if (!node || inserted) {
1296
1358
  return;
1297
1359
  }
1360
+ var tag = elementTagOf(node, enclosing);
1298
1361
  if (Array.isArray(node.children)) {
1299
1362
  var at = node.children.indexOf(provider);
1300
1363
  if (at !== -1) {
1301
- node.children.splice(at + 1, 0, sentinel);
1364
+ node.children.splice(at + 1, 0, buildSentinel(tag));
1302
1365
  inserted = true;
1303
1366
  return;
1304
1367
  }
1305
- node.children.forEach(traverse);
1368
+ node.children.forEach(function (child) { return traverse(child, tag); });
1306
1369
  }
1307
1370
  if (node.body) {
1308
1371
  if (Array.isArray(node.body)) {
1309
- node.body.forEach(traverse);
1372
+ node.body.forEach(function (child) { return traverse(child, tag); });
1310
1373
  }
1311
1374
  else {
1312
- traverse(node.body);
1375
+ traverse(node.body, tag);
1313
1376
  }
1314
1377
  }
1315
1378
  if (node.consequent) {
1316
- traverse(node.consequent);
1379
+ traverse(node.consequent, tag);
1317
1380
  }
1318
1381
  if (node.alternate) {
1319
- traverse(node.alternate);
1382
+ traverse(node.alternate, tag);
1320
1383
  }
1321
1384
  // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
1322
1385
  // panel, a gated section: the provider sits on the RIGHT of a logical
@@ -1324,22 +1387,22 @@ function insertSentinelAfterProvider(blockStatement, provider, sentinel) {
1324
1387
  // an argument. Left unvisited, the products list under the first tab of
1325
1388
  // a shop was never paginated or seeded, and rendered nothing.
1326
1389
  if (node.left) {
1327
- traverse(node.left);
1390
+ traverse(node.left, tag);
1328
1391
  }
1329
1392
  if (node.right) {
1330
- traverse(node.right);
1393
+ traverse(node.right, tag);
1331
1394
  }
1332
1395
  if (node.test) {
1333
- traverse(node.test);
1396
+ traverse(node.test, tag);
1334
1397
  }
1335
1398
  if (node.expression) {
1336
- traverse(node.expression);
1399
+ traverse(node.expression, tag);
1337
1400
  }
1338
1401
  if (node.argument) {
1339
- traverse(node.argument);
1402
+ traverse(node.argument, tag);
1340
1403
  }
1341
1404
  };
1342
- traverse(blockStatement);
1405
+ traverse(blockStatement, null);
1343
1406
  return inserted;
1344
1407
  }
1345
1408
  function findAllDataProvidersInJSX(blockStatement) {
@@ -1434,61 +1497,6 @@ function findArrayMapperRenderPropInDataProvider(dataProvider) {
1434
1497
  }
1435
1498
  return renderItemAttr.value.expression.params[0].name;
1436
1499
  }
1437
- function findAllSearchInputsInJSX(blockStatement) {
1438
- var results = [];
1439
- var traverse = function (node) {
1440
- var _a, _b, _c, _d, _e, _f;
1441
- if (!node) {
1442
- return;
1443
- }
1444
- if (node.type === 'JSXElement' && ((_b = (_a = node.openingElement) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.name) === 'input') {
1445
- var classAttr = (_c = node.openingElement.attributes) === null || _c === void 0 ? void 0 : _c.find(function (attr) { return attr.type === 'JSXAttribute' && attr.name.name === 'className'; });
1446
- var className = ((_d = classAttr === null || classAttr === void 0 ? void 0 : classAttr.value) === null || _d === void 0 ? void 0 : _d.value) || ((_f = (_e = classAttr === null || classAttr === void 0 ? void 0 : classAttr.value) === null || _e === void 0 ? void 0 : _e.expression) === null || _f === void 0 ? void 0 : _f.value) || '';
1447
- if (className.includes('search-input')) {
1448
- results.push({ node: node, className: className });
1449
- }
1450
- }
1451
- if (node.children && Array.isArray(node.children)) {
1452
- node.children.forEach(function (c) { return traverse(c); });
1453
- }
1454
- if (node.body) {
1455
- if (Array.isArray(node.body)) {
1456
- node.body.forEach(function (s) { return traverse(s); });
1457
- }
1458
- else {
1459
- traverse(node.body);
1460
- }
1461
- }
1462
- if (node.consequent) {
1463
- traverse(node.consequent);
1464
- }
1465
- if (node.alternate) {
1466
- traverse(node.alternate);
1467
- }
1468
- // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
1469
- // panel, a gated section: the provider sits on the RIGHT of a logical
1470
- // expression (or in a ternary's `test`), which is not a child, a body or
1471
- // an argument. Left unvisited, the products list under the first tab of
1472
- // a shop was never paginated or seeded, and rendered nothing.
1473
- if (node.left) {
1474
- traverse(node.left);
1475
- }
1476
- if (node.right) {
1477
- traverse(node.right);
1478
- }
1479
- if (node.test) {
1480
- traverse(node.test);
1481
- }
1482
- if (node.expression) {
1483
- traverse(node.expression);
1484
- }
1485
- if (node.argument) {
1486
- traverse(node.argument);
1487
- }
1488
- };
1489
- traverse(blockStatement);
1490
- return results;
1491
- }
1492
1500
  /**
1493
1501
  * Every pagination widget in the JSX, paired with the mapper it belongs to.
1494
1502
  *
@@ -1933,21 +1941,18 @@ accumulate) {
1933
1941
  ? (0, ast_1.buildCacheStoreThen)(cache)
1934
1942
  : uncachedTail;
1935
1943
  var networkChain = types.callExpression(types.memberExpression(parsedJson, types.identifier('then')), [responseTail]);
1944
+ // An appending list records the request it now stands on before making it
1945
+ // (see `buildAccumulatingRequestMark`).
1936
1946
  var arrow = cache
1937
1947
  ? types.arrowFunctionExpression([types.identifier('params')], (0, ast_1.buildCachedFetchBody)(networkChain, cache))
1938
- : types.arrowFunctionExpression([types.identifier('params')], networkChain);
1948
+ : accumulate
1949
+ ? types.arrowFunctionExpression([types.identifier('params')], types.blockStatement([
1950
+ (0, infinite_scroll_1.buildAccumulatingRequestMark)((0, infinite_scroll_1.getInfiniteScrollVars)(accumulate.usage.index)),
1951
+ types.returnStatement(networkChain),
1952
+ ]))
1953
+ : types.arrowFunctionExpression([types.identifier('params')], networkChain);
1939
1954
  return types.jsxAttribute(types.jsxIdentifier('fetchData'), types.jsxExpressionContainer(types.callExpression(types.identifier('useCallback'), [arrow, types.arrayExpression([])])));
1940
1955
  }
1941
- function wireSearchInput(inputNode, vars) {
1942
- // Remove existing onChange and value
1943
- inputNode.openingElement.attributes = inputNode.openingElement.attributes.filter(function (attr) { var _a; return !['onChange', 'value'].includes((_a = attr.name) === null || _a === void 0 ? void 0 : _a.name); });
1944
- // Add onChange
1945
- inputNode.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('onChange'), types.jsxExpressionContainer(types.arrowFunctionExpression([types.identifier('e')], types.callExpression(types.identifier(vars.setSearchQueryVar), [
1946
- types.memberExpression(types.memberExpression(types.identifier('e'), types.identifier('target')), types.identifier('value')),
1947
- ])))));
1948
- // Add value
1949
- inputNode.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('value'), types.jsxExpressionContainer(types.identifier(vars.searchQueryVar))));
1950
- }
1951
1956
  /**
1952
1957
  * The `fetchData` attribute for one paginated usage.
1953
1958
  *
@@ -1963,6 +1968,32 @@ function buildFetchDataForUsage(fileName, usage) {
1963
1968
  }
1964
1969
  return createFetchDataAttribute(fileName, ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined);
1965
1970
  }
1971
+ /** `const [ds_N_total, setDs_N_total] = useState(null)` — declared only beside a range control. */
1972
+ function buildTotalDeclaration(vars) {
1973
+ return types.variableDeclaration('const', [
1974
+ types.variableDeclarator(types.arrayPattern([
1975
+ types.identifier(vars.totalStateVar),
1976
+ types.identifier(vars.setTotalStateVar),
1977
+ ]), types.callExpression(types.identifier('useState'), [types.nullLiteral()])),
1978
+ ]);
1979
+ }
1980
+ /**
1981
+ * Fills one `range` text with the live row range of `usage` (see
1982
+ * `range-control.ts`). A mapper that does not page has no count to show, so
1983
+ * its range text is left as drawn.
1984
+ */
1985
+ function wireRangeControlFor(control, usage, vars) {
1986
+ if (!usage.paginated) {
1987
+ return;
1988
+ }
1989
+ var currentPageExpr = getPageAccessors(usage, vars).currentPageExpr;
1990
+ (0, range_control_1.wireRangeControl)(control, {
1991
+ totalVar: vars.totalStateVar,
1992
+ currentPageExpr: currentPageExpr(),
1993
+ perPage: usage.perPage,
1994
+ infiniteScroll: usage.infiniteScroll,
1995
+ });
1996
+ }
1966
1997
  /**
1967
1998
  * Wires the authored Load More button of an appending list.
1968
1999
  *
@@ -2002,6 +2033,11 @@ function wireLoadMoreButton(paginationNode, usage, vars, hasLoadingState) {
2002
2033
  * buttons beside its Load More.
2003
2034
  */
2004
2035
  function wirePaginationWidget(paginationNode, usage, vars, hasLoadingState) {
2036
+ // A range text inside the container takes the container's owner, like the
2037
+ // buttons beside it; one naming its own mapper is wired in STEP 5b instead.
2038
+ (0, pagination_controls_1.findAllPaginationControls)(paginationNode, 'range')
2039
+ .filter(function (control) { return !(0, pagination_controls_1.readJSXAttributeString)(control, pagination_controls_1.PAGINATION_FOR_ATTR); })
2040
+ .forEach(function (control) { return wireRangeControlFor(control, usage, vars); });
2005
2041
  if (usage.infiniteScroll) {
2006
2042
  // Only the Load More button survives; page controls make no sense for a
2007
2043
  // list the visitor never leaves.