@teleporthq/teleport-plugin-next-data-source 0.43.69 → 0.43.71
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.
- package/__tests__/rest-list-edge-cases.test.ts +211 -0
- package/__tests__/shared-resource-prefetch.test.ts +111 -0
- package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
- package/dist/cjs/fetchers/rest-api.js +25 -1
- package/dist/cjs/fetchers/rest-api.js.map +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/infinite-scroll.d.ts +7 -1
- package/dist/cjs/infinite-scroll.d.ts.map +1 -1
- package/dist/cjs/infinite-scroll.js +57 -8
- package/dist/cjs/infinite-scroll.js.map +1 -1
- package/dist/cjs/items-path.d.ts.map +1 -1
- package/dist/cjs/items-path.js +4 -3
- package/dist/cjs/items-path.js.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +5 -2
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/repeater-page-size.d.ts +16 -0
- package/dist/cjs/repeater-page-size.d.ts.map +1 -0
- package/dist/cjs/repeater-page-size.js +27 -0
- package/dist/cjs/repeater-page-size.js.map +1 -0
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +31 -4
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
- package/dist/esm/fetchers/rest-api.js +25 -1
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/infinite-scroll.d.ts +7 -1
- package/dist/esm/infinite-scroll.d.ts.map +1 -1
- package/dist/esm/infinite-scroll.js +57 -8
- package/dist/esm/infinite-scroll.js.map +1 -1
- package/dist/esm/items-path.d.ts.map +1 -1
- package/dist/esm/items-path.js +4 -3
- package/dist/esm/items-path.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +5 -2
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/repeater-page-size.d.ts +16 -0
- package/dist/esm/repeater-page-size.d.ts.map +1 -0
- package/dist/esm/repeater-page-size.js +23 -0
- package/dist/esm/repeater-page-size.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +31 -4
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/fetchers/rest-api.ts +27 -1
- package/src/index.ts +3 -2
- package/src/infinite-scroll.ts +107 -43
- package/src/items-path.ts +3 -2
- package/src/pagination-plugin.ts +6 -2
- package/src/repeater-page-size.ts +24 -0
- package/src/utils.ts +54 -6
package/src/fetchers/rest-api.ts
CHANGED
|
@@ -78,12 +78,38 @@ interface RESTAPIConfig {
|
|
|
78
78
|
url?: string
|
|
79
79
|
method?: string
|
|
80
80
|
headers?: Record<string, string>
|
|
81
|
+
queryParams?: Record<string, string>
|
|
81
82
|
authorization?: Authorization
|
|
82
83
|
bodyType?: string
|
|
83
84
|
body?: string
|
|
84
85
|
cache?: RESTAPICacheConfig
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
/**
|
|
89
|
+
* The URL with the data source's query params appended, built the way the
|
|
90
|
+
* editor's own fetch builds it (`RestApiService.constructUrl` in the
|
|
91
|
+
* data-fetcher worker: `searchParams.append` per param).
|
|
92
|
+
*
|
|
93
|
+
* The params used to be dropped here, so the published site asked the API a
|
|
94
|
+
* different question than the editor did — `?per_page=10` or `?category=x`
|
|
95
|
+
* configured on the source, and every row of the unfiltered response came back.
|
|
96
|
+
*/
|
|
97
|
+
const buildRequestURL = (
|
|
98
|
+
url: string | undefined,
|
|
99
|
+
queryParams?: Record<string, string>
|
|
100
|
+
): string | undefined => {
|
|
101
|
+
const params = Object.entries(queryParams || {}).filter(
|
|
102
|
+
([key, value]) => key.trim() !== '' && typeof value === 'string'
|
|
103
|
+
)
|
|
104
|
+
if (!url || params.length === 0) {
|
|
105
|
+
return url
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const requestURL = new URL(url)
|
|
109
|
+
params.forEach(([key, value]) => requestURL.searchParams.append(key, value))
|
|
110
|
+
return requestURL.toString()
|
|
111
|
+
}
|
|
112
|
+
|
|
87
113
|
export const generateRESTAPIFetcher = (config: Record<string, unknown>): string => {
|
|
88
114
|
const restConfig = config as RESTAPIConfig
|
|
89
115
|
const authCode = generateAuthCode(restConfig.authorization || {})
|
|
@@ -110,7 +136,7 @@ export default async function handler(req, res) {
|
|
|
110
136
|
try {
|
|
111
137
|
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset, collectionPath, itemsPath } = req.query
|
|
112
138
|
|
|
113
|
-
const url = ${JSON.stringify(restConfig.url)}
|
|
139
|
+
const url = ${JSON.stringify(buildRequestURL(restConfig.url, restConfig.queryParams))}
|
|
114
140
|
const method = ${JSON.stringify(restConfig.method || 'GET')}
|
|
115
141
|
|
|
116
142
|
const headers = ${JSON.stringify(restConfig.headers || {})}
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
hasUnresolvableDynamicParams,
|
|
16
16
|
} from './utils'
|
|
17
17
|
import { createNextArrayMapperPaginationPlugin } from './pagination-plugin'
|
|
18
|
+
import { resolveRepeaterPerPage } from './repeater-page-size'
|
|
18
19
|
import { buildProductTransformOptions } from './transformations'
|
|
19
20
|
import { DATA_SOURCE_ISR_REVALIDATE_SECONDS } from './isr'
|
|
20
21
|
import { ensureRouterDeclaration } from './request-locale'
|
|
@@ -416,8 +417,8 @@ function extractPaginationConfigEarly(uidlNode: any, resources: any): Pagination
|
|
|
416
417
|
const searchEnabled = node.content?.searchEnabled
|
|
417
418
|
const searchDebounce = node.content?.searchDebounce
|
|
418
419
|
|
|
419
|
-
if (paginated &&
|
|
420
|
-
perPageMap.set(renderProp, perPage)
|
|
420
|
+
if (paginated && renderProp) {
|
|
421
|
+
perPageMap.set(renderProp, resolveRepeaterPerPage(perPage))
|
|
421
422
|
}
|
|
422
423
|
|
|
423
424
|
if (searchEnabled && renderProp) {
|
package/src/infinite-scroll.ts
CHANGED
|
@@ -109,20 +109,124 @@ export const buildInfiniteScrollDeclarations = (
|
|
|
109
109
|
return declarations
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
const isIndexSegment = (segment: string): boolean => /^\d+$/.test(segment)
|
|
113
|
+
|
|
114
|
+
const readSegment = (object: types.Expression, segment: string): types.OptionalMemberExpression =>
|
|
115
|
+
isIndexSegment(segment)
|
|
116
|
+
? types.optionalMemberExpression(object, types.numericLiteral(Number(segment)), true, true)
|
|
117
|
+
: types.isValidIdentifier(segment)
|
|
118
|
+
? types.optionalMemberExpression(object, types.identifier(segment), false, true)
|
|
119
|
+
: types.optionalMemberExpression(object, types.stringLiteral(segment), true, true)
|
|
120
|
+
|
|
121
|
+
/** `payload?.data?.items` for `['data', 'items']`. */
|
|
122
|
+
const readAtPath = (payload: types.Expression, path: string[]): types.Expression =>
|
|
123
|
+
path.reduce<types.Expression>((object, segment) => readSegment(object, segment), payload)
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* A copy of `node` with `value` at `path`, every level around it kept:
|
|
127
|
+
* `{ ...payload, data: { ...payload?.data, items: value } }`. An index segment
|
|
128
|
+
* copies the list it indexes instead of spreading it into an object.
|
|
129
|
+
*/
|
|
130
|
+
const writeAtPath = (
|
|
131
|
+
node: types.Expression,
|
|
132
|
+
path: string[],
|
|
133
|
+
value: types.Expression
|
|
134
|
+
): types.Expression => {
|
|
135
|
+
if (path.length === 0) {
|
|
136
|
+
return value
|
|
137
|
+
}
|
|
138
|
+
const [segment, ...rest] = path
|
|
139
|
+
const child = writeAtPath(readSegment(types.cloneNode(node, true), segment), rest, value)
|
|
140
|
+
if (isIndexSegment(segment)) {
|
|
141
|
+
return types.callExpression(
|
|
142
|
+
types.memberExpression(types.identifier('Object'), types.identifier('assign')),
|
|
143
|
+
[
|
|
144
|
+
types.arrayExpression([
|
|
145
|
+
types.spreadElement(
|
|
146
|
+
types.logicalExpression('||', types.cloneNode(node, true), types.arrayExpression([]))
|
|
147
|
+
),
|
|
148
|
+
]),
|
|
149
|
+
types.objectExpression([
|
|
150
|
+
types.objectProperty(types.numericLiteral(Number(segment)), child),
|
|
151
|
+
]),
|
|
152
|
+
]
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
return types.objectExpression([
|
|
156
|
+
types.spreadElement(types.cloneNode(node, true)),
|
|
157
|
+
types.objectProperty(
|
|
158
|
+
types.isValidIdentifier(segment) ? types.identifier(segment) : types.stringLiteral(segment),
|
|
159
|
+
child
|
|
160
|
+
),
|
|
161
|
+
])
|
|
162
|
+
}
|
|
163
|
+
|
|
112
164
|
/**
|
|
113
165
|
* The `.then(...)` tail that accumulates a page and reports whether more exist.
|
|
114
166
|
*
|
|
115
167
|
* Returned as the resolved value of `fetchData`, so `DataProvider` stores the
|
|
116
168
|
* whole accumulated list as its data.
|
|
169
|
+
*
|
|
170
|
+
* `itemsPath` is where the rows sit inside a payload that wraps them
|
|
171
|
+
* (`{ items: [...], total }`): the rows are read there and the accumulated list
|
|
172
|
+
* is put back there, so the page keeps reading it through the same path. The
|
|
173
|
+
* wrapper itself counted as one "row" before, which ended the list after its
|
|
174
|
+
* first page and rendered nothing.
|
|
117
175
|
*/
|
|
118
176
|
export const buildAccumulatingResponseHandler = (
|
|
119
177
|
vars: InfiniteScrollVars,
|
|
120
|
-
perPage: number
|
|
178
|
+
perPage: number,
|
|
179
|
+
itemsPath?: string[]
|
|
121
180
|
): types.ArrowFunctionExpression => {
|
|
122
181
|
const accumCurrent = (): types.MemberExpression =>
|
|
123
182
|
types.memberExpression(types.identifier(vars.accumRefVar), types.identifier('current'))
|
|
124
183
|
const accumPages = (): types.MemberExpression =>
|
|
125
184
|
types.memberExpression(accumCurrent(), types.identifier('pages'))
|
|
185
|
+
const payload = (): types.OptionalMemberExpression =>
|
|
186
|
+
types.optionalMemberExpression(
|
|
187
|
+
types.identifier('response'),
|
|
188
|
+
types.identifier('data'),
|
|
189
|
+
false,
|
|
190
|
+
true
|
|
191
|
+
)
|
|
192
|
+
const rowsPath = itemsPath?.length ? itemsPath : undefined
|
|
193
|
+
|
|
194
|
+
const accumulatedRows = types.callExpression(
|
|
195
|
+
types.memberExpression(
|
|
196
|
+
types.callExpression(
|
|
197
|
+
types.memberExpression(
|
|
198
|
+
types.callExpression(
|
|
199
|
+
types.memberExpression(
|
|
200
|
+
types.callExpression(
|
|
201
|
+
types.memberExpression(types.identifier('Object'), types.identifier('keys')),
|
|
202
|
+
[accumPages()]
|
|
203
|
+
),
|
|
204
|
+
types.identifier('map')
|
|
205
|
+
),
|
|
206
|
+
[types.identifier('Number')]
|
|
207
|
+
),
|
|
208
|
+
types.identifier('sort')
|
|
209
|
+
),
|
|
210
|
+
[
|
|
211
|
+
types.arrowFunctionExpression(
|
|
212
|
+
[types.identifier('a'), types.identifier('b')],
|
|
213
|
+
types.binaryExpression('-', types.identifier('a'), types.identifier('b'))
|
|
214
|
+
),
|
|
215
|
+
]
|
|
216
|
+
),
|
|
217
|
+
types.identifier('reduce')
|
|
218
|
+
),
|
|
219
|
+
[
|
|
220
|
+
types.arrowFunctionExpression(
|
|
221
|
+
[types.identifier('acc'), types.identifier('p')],
|
|
222
|
+
types.callExpression(
|
|
223
|
+
types.memberExpression(types.identifier('acc'), types.identifier('concat')),
|
|
224
|
+
[types.memberExpression(accumPages(), types.identifier('p'), true)]
|
|
225
|
+
)
|
|
226
|
+
),
|
|
227
|
+
types.arrayExpression([]),
|
|
228
|
+
]
|
|
229
|
+
)
|
|
126
230
|
|
|
127
231
|
return types.arrowFunctionExpression(
|
|
128
232
|
[types.identifier('response')],
|
|
@@ -132,12 +236,7 @@ export const buildAccumulatingResponseHandler = (
|
|
|
132
236
|
types.identifier('rows'),
|
|
133
237
|
types.logicalExpression(
|
|
134
238
|
'||',
|
|
135
|
-
|
|
136
|
-
types.identifier('response'),
|
|
137
|
-
types.identifier('data'),
|
|
138
|
-
false,
|
|
139
|
-
true
|
|
140
|
-
),
|
|
239
|
+
rowsPath ? readAtPath(payload(), rowsPath) : payload(),
|
|
141
240
|
types.arrayExpression([])
|
|
142
241
|
)
|
|
143
242
|
),
|
|
@@ -211,42 +310,7 @@ export const buildAccumulatingResponseHandler = (
|
|
|
211
310
|
// page 10 does not land between 1 and 2. This is what DataProvider stores
|
|
212
311
|
// as its data, and so what the list renders.
|
|
213
312
|
types.returnStatement(
|
|
214
|
-
|
|
215
|
-
types.memberExpression(
|
|
216
|
-
types.callExpression(
|
|
217
|
-
types.memberExpression(
|
|
218
|
-
types.callExpression(
|
|
219
|
-
types.memberExpression(
|
|
220
|
-
types.callExpression(
|
|
221
|
-
types.memberExpression(types.identifier('Object'), types.identifier('keys')),
|
|
222
|
-
[accumPages()]
|
|
223
|
-
),
|
|
224
|
-
types.identifier('map')
|
|
225
|
-
),
|
|
226
|
-
[types.identifier('Number')]
|
|
227
|
-
),
|
|
228
|
-
types.identifier('sort')
|
|
229
|
-
),
|
|
230
|
-
[
|
|
231
|
-
types.arrowFunctionExpression(
|
|
232
|
-
[types.identifier('a'), types.identifier('b')],
|
|
233
|
-
types.binaryExpression('-', types.identifier('a'), types.identifier('b'))
|
|
234
|
-
),
|
|
235
|
-
]
|
|
236
|
-
),
|
|
237
|
-
types.identifier('reduce')
|
|
238
|
-
),
|
|
239
|
-
[
|
|
240
|
-
types.arrowFunctionExpression(
|
|
241
|
-
[types.identifier('acc'), types.identifier('p')],
|
|
242
|
-
types.callExpression(
|
|
243
|
-
types.memberExpression(types.identifier('acc'), types.identifier('concat')),
|
|
244
|
-
[types.memberExpression(accumPages(), types.identifier('p'), true)]
|
|
245
|
-
)
|
|
246
|
-
),
|
|
247
|
-
types.arrayExpression([]),
|
|
248
|
-
]
|
|
249
|
-
)
|
|
313
|
+
rowsPath ? writeAtPath(payload(), rowsPath, accumulatedRows) : accumulatedRows
|
|
250
314
|
),
|
|
251
315
|
])
|
|
252
316
|
)
|
package/src/items-path.ts
CHANGED
|
@@ -16,7 +16,7 @@ import * as types from '@babel/types'
|
|
|
16
16
|
* render prop — an expression this cannot read is left exactly as before.
|
|
17
17
|
*/
|
|
18
18
|
const SEGMENT_RE =
|
|
19
|
-
/^(?:\?\.([A-Za-z_$][\w$]*)|\.([A-Za-z_$][\w$]*)|(?:\?\.)?\[\s*(?:"([^"]*)"|'([^']*)')\s*\])/
|
|
19
|
+
/^(?:\?\.([A-Za-z_$][\w$]*)|\.([A-Za-z_$][\w$]*)|(?:\?\.)?\[\s*(?:"([^"]*)"|'([^']*)'|(\d+))\s*\])/
|
|
20
20
|
|
|
21
21
|
const PATH_AWARE_SOURCE_TYPES = new Set(['rest-api', 'javascript'])
|
|
22
22
|
|
|
@@ -47,7 +47,8 @@ export const extractItemsPath = (
|
|
|
47
47
|
if (!match) {
|
|
48
48
|
return undefined
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
// An index (`?.[0]`) is kept as its digits; the handler reads it off a list.
|
|
51
|
+
path.push(match[1] ?? match[2] ?? match[3] ?? match[4] ?? match[5])
|
|
51
52
|
rest = rest.slice(match[0].length)
|
|
52
53
|
}
|
|
53
54
|
|
package/src/pagination-plugin.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { buildProductTransformOptions, type EntityTransformOptions } from './tra
|
|
|
14
14
|
import { appendSortsParam, DynamicSortAST, extractDynamicSort } from './sort-utils'
|
|
15
15
|
import { appendFiltersParam, pushStateIdsAsDeps, pushPropIdsAsDeps } from './filter-utils'
|
|
16
16
|
import { appendItemsPathParam, extractItemsPath } from './items-path'
|
|
17
|
+
import { resolveRepeaterPerPage } from './repeater-page-size'
|
|
17
18
|
import {
|
|
18
19
|
applyLoadingStateToDataProvider,
|
|
19
20
|
buildLoadingStateDeclarations,
|
|
@@ -606,7 +607,9 @@ function buildStateRegistry(uidlNode: any): StateRegistry {
|
|
|
606
607
|
|
|
607
608
|
// For paginated mappers, use perPage from cms-list-repeater
|
|
608
609
|
// For plain mappers, use limit from data-source-list resource params
|
|
609
|
-
const effectivePerPage = content.paginated
|
|
610
|
+
const effectivePerPage = content.paginated
|
|
611
|
+
? resolveRepeaterPerPage(content.perPage)
|
|
612
|
+
: limit || content.perPage
|
|
610
613
|
|
|
611
614
|
const searchDefaultValue = parseSearchDefaultValue(content.searchDefaultValue)
|
|
612
615
|
|
|
@@ -2952,7 +2955,8 @@ function createFetchDataAttribute(
|
|
|
2952
2955
|
const responseTail = accumulate
|
|
2953
2956
|
? buildAccumulatingResponseHandler(
|
|
2954
2957
|
getInfiniteScrollVars(accumulate.usage.index),
|
|
2955
|
-
accumulate.usage.perPage
|
|
2958
|
+
accumulate.usage.perPage,
|
|
2959
|
+
accumulate.usage.itemsPath
|
|
2956
2960
|
)
|
|
2957
2961
|
: cache
|
|
2958
2962
|
? buildCacheStoreThen(cache)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rows per page a paginated list gets when its UIDL names none: the editor's
|
|
3
|
+
* own default (`DEFAULT_ARRAY_MAPPER_PER_PAGE`), which is also what the canvas
|
|
4
|
+
* pages by.
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_REPEATER_PER_PAGE = 10
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The page size of a paginated `cms-list-repeater`: its `perPage` when that is
|
|
10
|
+
* a whole positive number, else the editor's default.
|
|
11
|
+
*
|
|
12
|
+
* ⛔ A paginated list without a usable page size used to take the whole project
|
|
13
|
+
* down with it ("Property value expected type of number but got undefined"),
|
|
14
|
+
* and a NaN one paged nothing — while the canvas showed ten rows per page.
|
|
15
|
+
*/
|
|
16
|
+
export const resolveRepeaterPerPage = (perPage: unknown): number => {
|
|
17
|
+
const parsed =
|
|
18
|
+
typeof perPage === 'number'
|
|
19
|
+
? perPage
|
|
20
|
+
: typeof perPage === 'string'
|
|
21
|
+
? parseInt(perPage, 10)
|
|
22
|
+
: NaN
|
|
23
|
+
return Number.isFinite(parsed) && parsed >= 1 ? Math.floor(parsed) : DEFAULT_REPEATER_PER_PAGE
|
|
24
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -644,7 +644,15 @@ export const generateCollectionPathHelperCode = (): string => {
|
|
|
644
644
|
const readPayloadPath = (payload, path) => {
|
|
645
645
|
let current = payload
|
|
646
646
|
for (const segment of path) {
|
|
647
|
-
if (
|
|
647
|
+
if (Array.isArray(current)) {
|
|
648
|
+
const index = Number(segment)
|
|
649
|
+
if (!Number.isInteger(index) || index < 0 || index >= current.length) {
|
|
650
|
+
return undefined
|
|
651
|
+
}
|
|
652
|
+
current = current[index]
|
|
653
|
+
continue
|
|
654
|
+
}
|
|
655
|
+
if (!current || typeof current !== 'object' || !(segment in current)) {
|
|
648
656
|
return undefined
|
|
649
657
|
}
|
|
650
658
|
current = current[segment]
|
|
@@ -668,8 +676,16 @@ const openItemsEnvelope = (payload, rawPath) => {
|
|
|
668
676
|
if (!path) return null
|
|
669
677
|
const items = readPayloadPath(payload, path)
|
|
670
678
|
if (!Array.isArray(items)) return null
|
|
671
|
-
const rebuild = (node, depth, next) =>
|
|
672
|
-
depth === path.length
|
|
679
|
+
const rebuild = (node, depth, next) => {
|
|
680
|
+
if (depth === path.length) return next
|
|
681
|
+
const child = rebuild(node[path[depth]], depth + 1, next)
|
|
682
|
+
if (Array.isArray(node)) {
|
|
683
|
+
const copy = node.slice()
|
|
684
|
+
copy[Number(path[depth])] = child
|
|
685
|
+
return copy
|
|
686
|
+
}
|
|
687
|
+
return { ...node, [path[depth]]: child }
|
|
688
|
+
}
|
|
673
689
|
return { items, close: (next) => rebuild(payload, 0, next) }
|
|
674
690
|
}`
|
|
675
691
|
}
|
|
@@ -1217,17 +1233,20 @@ export const extractDataSourceIntoGetStaticProps = (
|
|
|
1217
1233
|
)
|
|
1218
1234
|
const baseKey = `${sanitizedDsName}_${sanitizedTableName}_data`
|
|
1219
1235
|
|
|
1220
|
-
let
|
|
1236
|
+
let resourcePropKey: string
|
|
1221
1237
|
if (renderPropIdentifier && resourceId) {
|
|
1222
1238
|
// Include resource ID to differentiate between same renderProp but different params
|
|
1223
1239
|
const sanitizedResourceId = StringUtils.dashCaseToCamelCase(
|
|
1224
1240
|
sanitizeFileName(resourceId).replace(/^TQ_/, '')
|
|
1225
1241
|
)
|
|
1226
|
-
|
|
1242
|
+
resourcePropKey = `${renderPropIdentifier}_${sanitizedResourceId}`
|
|
1227
1243
|
} else {
|
|
1228
1244
|
// Use base key for uniqueness (even if renderPropIdentifier exists, base key ensures uniqueness across different data sources)
|
|
1229
|
-
|
|
1245
|
+
resourcePropKey = baseKey
|
|
1230
1246
|
}
|
|
1247
|
+
// tslint:disable-next-line:no-any
|
|
1248
|
+
const nodeResource = (node.content as any).resource
|
|
1249
|
+
const propKey = claimParamsPropKey(componentChunk, resourcePropKey, nodeResource)
|
|
1231
1250
|
|
|
1232
1251
|
// Find matching JSX nodes for this data source
|
|
1233
1252
|
// Strategy depends on whether we have a unique resource ID:
|
|
@@ -1821,6 +1840,35 @@ interface ParallelFetchMeta {
|
|
|
1821
1840
|
declaration?: types.VariableDeclaration
|
|
1822
1841
|
}
|
|
1823
1842
|
|
|
1843
|
+
/**
|
|
1844
|
+
* The getStaticProps prop a provider's prefetch lands in: `resourcePropKey`,
|
|
1845
|
+
* suffixed when a provider with DIFFERENT params already holds it on this page.
|
|
1846
|
+
*
|
|
1847
|
+
* Every provider bound to the same table carries the same resource id
|
|
1848
|
+
* (`TQ_<source><table>`) and its own params, so the resource-derived key alone
|
|
1849
|
+
* gave a list limited to 10 rows and a text bound to the same source ONE
|
|
1850
|
+
* prefetch — whichever ran first. The other rendered its rows (`initialData`
|
|
1851
|
+
* skips the provider's own first fetch): every row in the limited list, or ten
|
|
1852
|
+
* where the text read the fifteenth. Providers with equal params still share
|
|
1853
|
+
* one key, and one fetch.
|
|
1854
|
+
*/
|
|
1855
|
+
const claimParamsPropKey = (
|
|
1856
|
+
componentChunk: ChunkDefinition,
|
|
1857
|
+
resourcePropKey: string,
|
|
1858
|
+
resource: { params?: Record<string, unknown> } | undefined
|
|
1859
|
+
): string => {
|
|
1860
|
+
const meta = componentChunk.meta as { dataSourcePropKeyParams?: Record<string, string> }
|
|
1861
|
+
const claimed = meta.dataSourcePropKeyParams ?? (meta.dataSourcePropKeyParams = {})
|
|
1862
|
+
const signature = JSON.stringify(resource?.params ?? {})
|
|
1863
|
+
|
|
1864
|
+
let propKey = resourcePropKey
|
|
1865
|
+
for (let suffix = 2; claimed[propKey] !== undefined && claimed[propKey] !== signature; suffix++) {
|
|
1866
|
+
propKey = `${resourcePropKey}_${suffix}`
|
|
1867
|
+
}
|
|
1868
|
+
claimed[propKey] = signature
|
|
1869
|
+
return propKey
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1824
1872
|
const createSafeFetchExpression = (
|
|
1825
1873
|
fetchCallExpression: types.CallExpression,
|
|
1826
1874
|
label: string
|