@teleporthq/teleport-plugin-next-data-source 0.44.0 → 0.44.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.
Files changed (94) hide show
  1. package/__tests__/_helpers/first-page-runtime.ts +184 -0
  2. package/__tests__/_helpers/pagination-widget-fixture.ts +67 -1
  3. package/__tests__/browser-row-policy.test.ts +202 -2
  4. package/__tests__/data-source-request-locale.test.ts +4 -3
  5. package/__tests__/fetchers.test.ts +6 -2
  6. package/__tests__/infinite-scroll.test.ts +210 -5
  7. package/__tests__/page-data-visitor-read.test.ts +196 -0
  8. package/__tests__/plain-list-sort.test.ts +1 -1
  9. package/__tests__/plain-list-starting-sort.test.ts +323 -0
  10. package/__tests__/refetch-loading-state.test.ts +158 -2
  11. package/__tests__/starting-sort-first-page.test.ts +285 -0
  12. package/dist/cjs/cache/ast.d.ts +2 -0
  13. package/dist/cjs/cache/ast.d.ts.map +1 -1
  14. package/dist/cjs/cache/ast.js +5 -2
  15. package/dist/cjs/cache/ast.js.map +1 -1
  16. package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
  17. package/dist/cjs/data-source-fetchers.js +10 -2
  18. package/dist/cjs/data-source-fetchers.js.map +1 -1
  19. package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
  20. package/dist/cjs/fetchers/teleport.js +1 -1
  21. package/dist/cjs/fetchers/teleport.js.map +1 -1
  22. package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -1
  23. package/dist/cjs/fetchers/utils/browser-row-policy.js +5 -1
  24. package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -1
  25. package/dist/cjs/index.d.ts.map +1 -1
  26. package/dist/cjs/index.js +14 -2
  27. package/dist/cjs/index.js.map +1 -1
  28. package/dist/cjs/infinite-scroll.d.ts +64 -2
  29. package/dist/cjs/infinite-scroll.d.ts.map +1 -1
  30. package/dist/cjs/infinite-scroll.js +102 -3
  31. package/dist/cjs/infinite-scroll.js.map +1 -1
  32. package/dist/cjs/loading-state.d.ts +23 -2
  33. package/dist/cjs/loading-state.d.ts.map +1 -1
  34. package/dist/cjs/loading-state.js +50 -15
  35. package/dist/cjs/loading-state.js.map +1 -1
  36. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  37. package/dist/cjs/pagination-plugin.js +200 -64
  38. package/dist/cjs/pagination-plugin.js.map +1 -1
  39. package/dist/cjs/sort-utils.d.ts +53 -1
  40. package/dist/cjs/sort-utils.d.ts.map +1 -1
  41. package/dist/cjs/sort-utils.js +105 -13
  42. package/dist/cjs/sort-utils.js.map +1 -1
  43. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  44. package/dist/cjs/utils.d.ts +3 -2
  45. package/dist/cjs/utils.d.ts.map +1 -1
  46. package/dist/cjs/utils.js +48 -18
  47. package/dist/cjs/utils.js.map +1 -1
  48. package/dist/esm/cache/ast.d.ts +2 -0
  49. package/dist/esm/cache/ast.d.ts.map +1 -1
  50. package/dist/esm/cache/ast.js +3 -1
  51. package/dist/esm/cache/ast.js.map +1 -1
  52. package/dist/esm/data-source-fetchers.d.ts.map +1 -1
  53. package/dist/esm/data-source-fetchers.js +10 -2
  54. package/dist/esm/data-source-fetchers.js.map +1 -1
  55. package/dist/esm/fetchers/teleport.d.ts.map +1 -1
  56. package/dist/esm/fetchers/teleport.js +1 -1
  57. package/dist/esm/fetchers/teleport.js.map +1 -1
  58. package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -1
  59. package/dist/esm/fetchers/utils/browser-row-policy.js +6 -2
  60. package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -1
  61. package/dist/esm/index.d.ts.map +1 -1
  62. package/dist/esm/index.js +14 -2
  63. package/dist/esm/index.js.map +1 -1
  64. package/dist/esm/infinite-scroll.d.ts +64 -2
  65. package/dist/esm/infinite-scroll.d.ts.map +1 -1
  66. package/dist/esm/infinite-scroll.js +97 -2
  67. package/dist/esm/infinite-scroll.js.map +1 -1
  68. package/dist/esm/loading-state.d.ts +23 -2
  69. package/dist/esm/loading-state.d.ts.map +1 -1
  70. package/dist/esm/loading-state.js +50 -15
  71. package/dist/esm/loading-state.js.map +1 -1
  72. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  73. package/dist/esm/pagination-plugin.js +204 -68
  74. package/dist/esm/pagination-plugin.js.map +1 -1
  75. package/dist/esm/sort-utils.d.ts +53 -1
  76. package/dist/esm/sort-utils.d.ts.map +1 -1
  77. package/dist/esm/sort-utils.js +100 -12
  78. package/dist/esm/sort-utils.js.map +1 -1
  79. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  80. package/dist/esm/utils.d.ts +3 -2
  81. package/dist/esm/utils.d.ts.map +1 -1
  82. package/dist/esm/utils.js +49 -19
  83. package/dist/esm/utils.js.map +1 -1
  84. package/package.json +4 -4
  85. package/src/cache/ast.ts +4 -1
  86. package/src/data-source-fetchers.ts +33 -14
  87. package/src/fetchers/teleport.ts +8 -2
  88. package/src/fetchers/utils/browser-row-policy.ts +79 -5
  89. package/src/index.ts +8 -2
  90. package/src/infinite-scroll.ts +182 -4
  91. package/src/loading-state.ts +85 -13
  92. package/src/pagination-plugin.ts +369 -83
  93. package/src/sort-utils.ts +164 -21
  94. package/src/utils.ts +55 -18
package/src/sort-utils.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import * as types from '@babel/types'
2
2
  import { ASTUtils } from '@teleporthq/teleport-plugin-common'
3
- import { UIDLExpressionValue, UIDLStaticValue } from '@teleporthq/teleport-types'
3
+ import { JSIdentifiers } from '@teleporthq/teleport-shared'
4
+ import {
5
+ UIDLExpressionValue,
6
+ UIDLStateDefinition,
7
+ UIDLStaticValue,
8
+ } from '@teleporthq/teleport-types'
4
9
 
5
10
  export interface DynamicSortAST {
6
11
  field: types.Expression
@@ -18,22 +23,26 @@ const SKIP_IDENTIFIERS = new Set([
18
23
  'console',
19
24
  ])
20
25
 
21
- const collectIdentifiers = (node: types.Node | null | undefined, acc: Set<string>): void => {
26
+ const collectIdentifiers = (
27
+ node: types.Node | null | undefined,
28
+ acc: Set<string>,
29
+ skip: ReadonlySet<string> = SKIP_IDENTIFIERS
30
+ ): void => {
22
31
  if (!node || typeof node !== 'object') {
23
32
  return
24
33
  }
25
34
 
26
35
  if (node.type === 'Identifier') {
27
- if (!SKIP_IDENTIFIERS.has(node.name)) {
36
+ if (!skip.has(node.name)) {
28
37
  acc.add(node.name)
29
38
  }
30
39
  return
31
40
  }
32
41
 
33
42
  if (node.type === 'MemberExpression') {
34
- collectIdentifiers(node.object, acc)
43
+ collectIdentifiers(node.object, acc, skip)
35
44
  if (node.computed) {
36
- collectIdentifiers(node.property, acc)
45
+ collectIdentifiers(node.property, acc, skip)
37
46
  }
38
47
  return
39
48
  }
@@ -43,9 +52,9 @@ const collectIdentifiers = (node: types.Node | null | undefined, acc: Set<string
43
52
  // tslint:disable-next-line:no-any
44
53
  const child = (node as any)[key]
45
54
  if (Array.isArray(child)) {
46
- child.forEach((c) => collectIdentifiers(c, acc))
55
+ child.forEach((c) => collectIdentifiers(c, acc, skip))
47
56
  } else if (child && typeof child === 'object' && typeof child.type === 'string') {
48
- collectIdentifiers(child, acc)
57
+ collectIdentifiers(child, acc, skip)
49
58
  }
50
59
  }
51
60
  }
@@ -111,24 +120,158 @@ export const extractDynamicSort = (
111
120
  }
112
121
  }
113
122
 
114
- // Build the AST for `sorts: JSON.stringify([{ field: <fieldExpr>, order: <orderExpr> }])`
115
- const buildDynamicSortsProperty = (dynamicSort: DynamicSortAST): types.ObjectProperty => {
116
- return types.objectProperty(
117
- types.identifier('sorts'),
118
- types.callExpression(
119
- types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
120
- [
121
- types.arrayExpression([
122
- types.objectExpression([
123
- types.objectProperty(types.identifier('field'), types.cloneNode(dynamicSort.field)),
124
- types.objectProperty(types.identifier('order'), types.cloneNode(dynamicSort.order)),
125
- ]),
123
+ // `JSON.stringify([{ field: <fieldExpr>, order: <orderExpr> }])`
124
+ const buildDynamicSortsValue = (dynamicSort: DynamicSortAST): types.CallExpression =>
125
+ types.callExpression(
126
+ types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
127
+ [
128
+ types.arrayExpression([
129
+ types.objectExpression([
130
+ types.objectProperty(types.identifier('field'), types.cloneNode(dynamicSort.field, true)),
131
+ types.objectProperty(types.identifier('order'), types.cloneNode(dynamicSort.order, true)),
126
132
  ]),
127
- ]
128
- )
133
+ ]),
134
+ ]
129
135
  )
136
+
137
+ // Build the AST for `sorts: JSON.stringify([{ field: <fieldExpr>, order: <orderExpr> }])`
138
+ const buildDynamicSortsProperty = (dynamicSort: DynamicSortAST): types.ObjectProperty =>
139
+ types.objectProperty(types.identifier('sorts'), buildDynamicSortsValue(dynamicSort))
140
+
141
+ /**
142
+ * A page state a state-bound sort reads, at the value the visitor starts with.
143
+ */
144
+ export interface SortStateDefault {
145
+ /** The state's binding in the component: the name the sort expressions read. */
146
+ binding: string
147
+ value: string | number | boolean
130
148
  }
131
149
 
150
+ // Names a sort expression may read besides its states: they mean the same on
151
+ // the server and in the browser.
152
+ const CONSTANT_IDENTIFIERS: ReadonlySet<string> = new Set(['undefined', 'NaN', 'Infinity'])
153
+
154
+ const isStartingValue = (value: unknown): value is SortStateDefault['value'] =>
155
+ typeof value === 'string' ||
156
+ typeof value === 'boolean' ||
157
+ (typeof value === 'number' && Number.isFinite(value))
158
+
159
+ /**
160
+ * The states a state-bound sort reads, at their defaults: the sort a visitor
161
+ * starts with, which the build can fetch the first page by.
162
+ *
163
+ * Only when every name the column and the direction read is a state of this
164
+ * component starting at a fixed value. Anything else they read — a prop, the
165
+ * router, `window` — or a state the page seeds from its data leaves the build
166
+ * unable to know the starting sort: `undefined`, and the list fetches its first
167
+ * page in the browser. An empty list is a sort that reads nothing.
168
+ */
169
+ export const readSortStateDefaults = (
170
+ dynamicSort: DynamicSortAST,
171
+ stateDefinitions: Record<string, UIDLStateDefinition> | undefined
172
+ ): SortStateDefault[] | undefined => {
173
+ const read = new Set<string>()
174
+ collectIdentifiers(dynamicSort.field, read, CONSTANT_IDENTIFIERS)
175
+ collectIdentifiers(dynamicSort.order, read, CONSTANT_IDENTIFIERS)
176
+
177
+ // A state's binding is its sanitised name, as the component declares it.
178
+ const stateByBinding = new Map<string, UIDLStateDefinition>()
179
+ Object.entries(stateDefinitions ?? {}).forEach(([key, definition]) => {
180
+ stateByBinding.set(JSIdentifiers.createSafeJSIdentifier(key), definition)
181
+ })
182
+
183
+ const defaults: SortStateDefault[] = []
184
+ for (const binding of Array.from(read)) {
185
+ const definition = stateByBinding.get(binding)
186
+ if (
187
+ !definition ||
188
+ definition.dataSourceBinding ||
189
+ definition.query ||
190
+ !isStartingValue(definition.defaultValue)
191
+ ) {
192
+ return undefined
193
+ }
194
+ defaults.push({ binding, value: definition.defaultValue })
195
+ }
196
+ return defaults
197
+ }
198
+
199
+ /**
200
+ * A list repeater's own sort, read the one way for every part of the plugin
201
+ * that fetches the list's rows — the build's fetch for a page and the
202
+ * browser's: a column the editor fixed (`fixed`), or one bound to state
203
+ * (`dynamic`) with, when the build can know them, the values a visitor starts
204
+ * with (`starting`, see `readSortStateDefaults`). Empty without a sort.
205
+ */
206
+ export interface RepeaterSort {
207
+ fixed?: { field: string; order: 'asc' | 'desc' }
208
+ dynamic?: DynamicSortAST
209
+ starting?: SortStateDefault[]
210
+ }
211
+
212
+ export const readRepeaterSort = (
213
+ sort: UIDLStaticValue | UIDLExpressionValue | undefined,
214
+ sortDirection: UIDLStaticValue | UIDLExpressionValue | undefined,
215
+ stateDefinitions: Record<string, UIDLStateDefinition> | undefined
216
+ ): RepeaterSort => {
217
+ if (!sort) {
218
+ return {}
219
+ }
220
+ const fixed = readFixedSort(sort, sortDirection)
221
+ if (fixed) {
222
+ return { fixed }
223
+ }
224
+ const dynamic = extractDynamicSort(sort, sortDirection)
225
+ return dynamic ? { dynamic, starting: readSortStateDefaults(dynamic, stateDefinitions) } : {}
226
+ }
227
+
228
+ /**
229
+ * The `sorts` param of the build-time fetch: the browser's own expression,
230
+ * evaluated with every state it reads at its default —
231
+ *
232
+ * ((sortBy) => JSON.stringify([{ field: sortBy.split("-")?.[0] || '', order: … }]))("name-asc")
233
+ *
234
+ * — so it is the very string the browser sends while the sort is untouched, and
235
+ * the data source orders the first page exactly as it orders every later one
236
+ * (the same columns, empty values last, the row's id breaking ties).
237
+ */
238
+ export const buildStartingSortsValue = (
239
+ dynamicSort: DynamicSortAST,
240
+ defaults: SortStateDefault[]
241
+ ): types.Expression => {
242
+ const sorts = buildDynamicSortsValue(dynamicSort)
243
+ if (defaults.length === 0) {
244
+ return sorts
245
+ }
246
+ return types.callExpression(
247
+ types.arrowFunctionExpression(
248
+ defaults.map((state) => types.identifier(state.binding)),
249
+ sorts
250
+ ),
251
+ defaults.map((state) => types.valueToNode(state.value) as types.Expression)
252
+ )
253
+ }
254
+
255
+ /**
256
+ * `sortBy === "name-asc"`: the sort is still the one the visitor starts with,
257
+ * so the rows fetched at build time are its first page. `null` for a sort that
258
+ * reads nothing, which never moves.
259
+ */
260
+ export const buildSortAtStartGuard = (defaults: SortStateDefault[]): types.Expression | null =>
261
+ defaults
262
+ .map(
263
+ (state): types.Expression =>
264
+ types.binaryExpression(
265
+ '===',
266
+ types.identifier(state.binding),
267
+ types.valueToNode(state.value) as types.Expression
268
+ )
269
+ )
270
+ .reduce<types.Expression | null>(
271
+ (all, guard) => (all ? types.logicalExpression('&&', all, guard) : guard),
272
+ null
273
+ )
274
+
132
275
  // Build the AST for the legacy static sorts array form.
133
276
  // tslint:disable-next-line:no-any
134
277
  const buildLegacySortsProperty = (sorts: any[]): types.ObjectProperty => {
package/src/utils.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  UIDLDataSource,
8
8
  DataSourceType,
9
9
  ChunkType,
10
+ UIDLStateDefinition,
10
11
  } from '@teleporthq/teleport-types'
11
12
  import * as types from '@babel/types'
12
13
  import { ASTUtils } from '@teleporthq/teleport-plugin-common'
@@ -15,7 +16,7 @@ import { generateDataSourceFetcherWithCore } from './data-source-fetchers'
15
16
  import type { EntityTransformOptions } from './transformations'
16
17
  import { DATA_SOURCE_ISR_REVALIDATE_SECONDS } from './isr'
17
18
  import { buildServerLocaleParam, hasLocaleParam, buildClientLocaleParam } from './request-locale'
18
- import { readFixedSort } from './sort-utils'
19
+ import { buildStartingSortsValue, readRepeaterSort, type RepeaterSort } from './sort-utils'
19
20
 
20
21
  const VALID_DATA_SOURCE_TYPES: DataSourceType[] = [
21
22
  'rest-api',
@@ -1180,16 +1181,16 @@ const isSortKeyed = (properties: types.ObjectProperty[]): boolean =>
1180
1181
  (property.key.type === 'Identifier' && property.key.name === 'sorts')
1181
1182
  )
1182
1183
 
1183
- /** The fixed sort of the first list repeater inside a data-source node, if it has one. */
1184
+ /** The first list repeater inside a data-source node: the list its rows are fetched for. */
1184
1185
  // tslint:disable-next-line:no-any
1185
- const findRepeaterFixedSort = (node: any): { field: string; order: 'asc' | 'desc' } | undefined => {
1186
+ const findListRepeater = (node: any): Record<string, any> | undefined => {
1186
1187
  // tslint:disable-next-line:no-any
1187
- const visit = (current: any): { field: string; order: 'asc' | 'desc' } | null | undefined => {
1188
+ const visit = (current: any): Record<string, any> | undefined => {
1188
1189
  if (!current || typeof current !== 'object') {
1189
1190
  return undefined
1190
1191
  }
1191
1192
  if (current.type === 'cms-list-repeater') {
1192
- return readFixedSort(current.content?.sort, current.content?.sortDirection) ?? null
1193
+ return current.content ?? {}
1193
1194
  }
1194
1195
  const content = current.content
1195
1196
  const children = [
@@ -1204,9 +1205,28 @@ const findRepeaterFixedSort = (node: any): { field: string; order: 'asc' | 'desc
1204
1205
  }
1205
1206
  return undefined
1206
1207
  }
1207
- return visit(node) ?? undefined
1208
+ return visit(node)
1208
1209
  }
1209
1210
 
1211
+ /**
1212
+ * The order a list's page fetch carries, in what tells it apart from another
1213
+ * list's fetch with the same params (`claimParamsPropKey`): the column the
1214
+ * editor fixed, or a sort the visitor picks at the values they start with.
1215
+ */
1216
+ const prefetchOrderOf = (
1217
+ // tslint:disable-next-line:no-any
1218
+ repeater: Record<string, any> | undefined,
1219
+ sort: RepeaterSort
1220
+ ): unknown =>
1221
+ sort.fixed ??
1222
+ (sort.dynamic && sort.starting
1223
+ ? {
1224
+ sort: repeater?.sort,
1225
+ sortDirection: repeater?.sortDirection ?? null,
1226
+ starting: sort.starting,
1227
+ }
1228
+ : undefined)
1229
+
1210
1230
  export const extractDataSourceIntoGetStaticProps = (
1211
1231
  node: UIDLDataSourceItemNode | UIDLDataSourceListNode,
1212
1232
  dataSources: Record<string, UIDLDataSource>,
@@ -1217,7 +1237,13 @@ export const extractDataSourceIntoGetStaticProps = (
1217
1237
  dependencies: Record<string, any>,
1218
1238
  dynamicRouteAttr?: string,
1219
1239
  folderPath?: string[],
1220
- transformOptions: EntityTransformOptions = {}
1240
+ transformOptions: EntityTransformOptions = {},
1241
+ // What the read hands the module after its params: `{ as: 'visitor' }` on
1242
+ // every page but an admin page only trusted readers open (see
1243
+ // `RouteUtils.pageDataReadArguments`).
1244
+ readArguments: () => types.Expression[] = () => [],
1245
+ // The page's states: a list sorted by one is fetched by the value it starts at.
1246
+ stateDefinitions?: Record<string, UIDLStateDefinition>
1221
1247
  ): { success: boolean; chunk?: any } => {
1222
1248
  try {
1223
1249
  // Validate node content
@@ -1281,11 +1307,13 @@ export const extractDataSourceIntoGetStaticProps = (
1281
1307
  }
1282
1308
  // tslint:disable-next-line:no-any
1283
1309
  const nodeResource = (node.content as any).resource
1310
+ const repeater = findListRepeater(node)
1311
+ const repeaterSort = readRepeaterSort(repeater?.sort, repeater?.sortDirection, stateDefinitions)
1284
1312
  const propKey = claimParamsPropKey(
1285
1313
  componentChunk,
1286
1314
  resourcePropKey,
1287
1315
  nodeResource,
1288
- findRepeaterFixedSort(node)
1316
+ prefetchOrderOf(repeater, repeaterSort)
1289
1317
  )
1290
1318
 
1291
1319
  // Find matching JSX nodes for this data source
@@ -1732,11 +1760,13 @@ export const extractDataSourceIntoGetStaticProps = (
1732
1760
  }
1733
1761
  })
1734
1762
 
1735
- // The sort the list's repeater fixed, when the resource names none — the
1763
+ // The sort the list's repeater carries, when the resource names none — the
1736
1764
  // rows the page is pre-rendered with come in the order the list shows them
1737
- // (the browser keeps them: the provider skips its first fetch).
1738
- const repeaterSort = isSortKeyed(paramsProperties) ? undefined : findRepeaterFixedSort(node)
1739
- if (repeaterSort) {
1765
+ // (the browser keeps them: the provider skips its first fetch). A sort the
1766
+ // visitor picks is fetched as they start with it, by the browser's own sort
1767
+ // expression (`buildStartingSortsValue`), and the list keeps the rows only
1768
+ // while the sort stays there (`seedPlainListWhileSortStarts`).
1769
+ if (!isSortKeyed(paramsProperties) && repeaterSort.fixed) {
1740
1770
  paramsProperties.push(
1741
1771
  types.objectProperty(
1742
1772
  types.stringLiteral('sorts'),
@@ -1747,11 +1777,11 @@ export const extractDataSourceIntoGetStaticProps = (
1747
1777
  types.objectExpression([
1748
1778
  types.objectProperty(
1749
1779
  types.identifier('field'),
1750
- types.stringLiteral(repeaterSort.field)
1780
+ types.stringLiteral(repeaterSort.fixed.field)
1751
1781
  ),
1752
1782
  types.objectProperty(
1753
1783
  types.identifier('order'),
1754
- types.stringLiteral(repeaterSort.order)
1784
+ types.stringLiteral(repeaterSort.fixed.order)
1755
1785
  ),
1756
1786
  ]),
1757
1787
  ]),
@@ -1759,6 +1789,13 @@ export const extractDataSourceIntoGetStaticProps = (
1759
1789
  )
1760
1790
  )
1761
1791
  )
1792
+ } else if (!isSortKeyed(paramsProperties) && repeaterSort.dynamic && repeaterSort.starting) {
1793
+ paramsProperties.push(
1794
+ types.objectProperty(
1795
+ types.stringLiteral('sorts'),
1796
+ buildStartingSortsValue(repeaterSort.dynamic, repeaterSort.starting)
1797
+ )
1798
+ )
1762
1799
  }
1763
1800
 
1764
1801
  // getStaticProps runs once per language: the prefetch is for that language.
@@ -1768,7 +1805,7 @@ export const extractDataSourceIntoGetStaticProps = (
1768
1805
 
1769
1806
  const fetchCallExpression = types.callExpression(
1770
1807
  types.memberExpression(types.identifier(fetcherImportName), types.identifier('fetchData')),
1771
- [types.objectExpression(paramsProperties)]
1808
+ [types.objectExpression(paramsProperties), ...readArguments()]
1772
1809
  )
1773
1810
 
1774
1811
  const safeFetchExpression = createSafeFetchExpression(fetchCallExpression, propKey)
@@ -1925,12 +1962,12 @@ const claimParamsPropKey = (
1925
1962
  componentChunk: ChunkDefinition,
1926
1963
  resourcePropKey: string,
1927
1964
  resource: { params?: Record<string, unknown> } | undefined,
1928
- // The list's own fixed sort rides on its prefetch too (see the params below).
1929
- repeaterSort?: { field: string; order: string }
1965
+ // The list's own sort rides on its prefetch too (`prefetchOrderOf`).
1966
+ prefetchOrder?: unknown
1930
1967
  ): string => {
1931
1968
  const meta = componentChunk.meta as { dataSourcePropKeyParams?: Record<string, string> }
1932
1969
  const claimed = meta.dataSourcePropKeyParams ?? (meta.dataSourcePropKeyParams = {})
1933
- const signature = JSON.stringify([resource?.params ?? {}, repeaterSort ?? null])
1970
+ const signature = JSON.stringify([resource?.params ?? {}, prefetchOrder ?? null])
1934
1971
 
1935
1972
  let propKey = resourcePropKey
1936
1973
  for (let suffix = 2; claimed[propKey] !== undefined && claimed[propKey] !== signature; suffix++) {