@teleporthq/teleport-plugin-next-data-source 0.42.4 → 0.42.6
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/dist/cjs/array-mapper-pagination.d.ts +5 -0
- package/dist/cjs/array-mapper-pagination.d.ts.map +1 -1
- package/dist/cjs/array-mapper-pagination.js.map +1 -1
- package/dist/cjs/array-mapper-registry.d.ts +84 -0
- package/dist/cjs/array-mapper-registry.d.ts.map +1 -0
- package/dist/cjs/array-mapper-registry.js +291 -0
- package/dist/cjs/array-mapper-registry.js.map +1 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +36 -14
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts +1 -3
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +908 -1351
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/array-mapper-pagination.d.ts +5 -0
- package/dist/esm/array-mapper-pagination.d.ts.map +1 -1
- package/dist/esm/array-mapper-pagination.js.map +1 -1
- package/dist/esm/array-mapper-registry.d.ts +84 -0
- package/dist/esm/array-mapper-registry.d.ts.map +1 -0
- package/dist/esm/array-mapper-registry.js +287 -0
- package/dist/esm/array-mapper-registry.js.map +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +36 -14
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts +1 -3
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +909 -1352
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/array-mapper-pagination.ts +5 -0
- package/src/array-mapper-registry.ts +408 -0
- package/src/index.ts +24 -1
- package/src/pagination-plugin.ts +1860 -2132
package/src/pagination-plugin.ts
CHANGED
|
@@ -5,20 +5,232 @@ import {
|
|
|
5
5
|
FileType,
|
|
6
6
|
} from '@teleporthq/teleport-types'
|
|
7
7
|
import * as types from '@babel/types'
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
import { StringUtils } from '@teleporthq/teleport-shared'
|
|
9
|
+
import { generateSafeFileName } from './utils'
|
|
10
|
+
|
|
11
|
+
// ==================== UIDL-FIRST STATE MANAGEMENT ====================
|
|
12
|
+
// This module uses a UIDL-first approach: we scan the UIDL FIRST to identify
|
|
13
|
+
// ALL data source usages and assign unique state IDs BEFORE any JSX processing.
|
|
14
|
+
// This ensures consistent state mapping across DataProviders, search inputs, and pagination buttons.
|
|
15
|
+
|
|
16
|
+
interface DataSourceUsage {
|
|
17
|
+
// Unique sequential index for this usage (0, 1, 2, ...)
|
|
18
|
+
index: number
|
|
19
|
+
// The data-source-list renderPropIdentifier (e.g., "dsadsa3_users_data")
|
|
15
20
|
dataSourceIdentifier: string
|
|
16
|
-
|
|
17
|
-
arrayMapperRenderProp
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
// The cms-list-repeater renderPropIdentifier (e.g., "context_1i871")
|
|
22
|
+
arrayMapperRenderProp: string
|
|
23
|
+
// Resource definition from UIDL
|
|
24
|
+
resourceDefinition: {
|
|
25
|
+
dataSourceId: string
|
|
26
|
+
tableName: string
|
|
27
|
+
dataSourceType: string
|
|
28
|
+
}
|
|
29
|
+
// Pagination config
|
|
30
|
+
paginated: boolean
|
|
31
|
+
perPage: number
|
|
32
|
+
// Search config
|
|
33
|
+
searchEnabled: boolean
|
|
34
|
+
searchDebounce: number
|
|
35
|
+
// Query columns from resource params
|
|
36
|
+
queryColumns: string[]
|
|
37
|
+
// Computed category
|
|
38
|
+
category: 'paginated+search' | 'paginated-only' | 'search-only' | 'plain'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface StateRegistry {
|
|
42
|
+
usages: DataSourceUsage[]
|
|
43
|
+
// Map from dataSourceIdentifier to all usages with that identifier
|
|
44
|
+
byDataSourceId: Map<string, DataSourceUsage[]>
|
|
45
|
+
// Map from arrayMapperRenderProp to usage
|
|
46
|
+
byArrayMapperRenderProp: Map<string, DataSourceUsage>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Scan UIDL to find all data source usages and build a registry
|
|
50
|
+
function buildStateRegistry(uidlNode: any): StateRegistry {
|
|
51
|
+
const usages: DataSourceUsage[] = []
|
|
52
|
+
const byDataSourceId = new Map<string, DataSourceUsage[]>()
|
|
53
|
+
const byArrayMapperRenderProp = new Map<string, DataSourceUsage>()
|
|
54
|
+
let index = 0
|
|
55
|
+
|
|
56
|
+
const traverse = (
|
|
57
|
+
node: any,
|
|
58
|
+
parentDataSource?: { identifier: string; resourceDef: any; resourceParams: any }
|
|
59
|
+
): void => {
|
|
60
|
+
if (!node || typeof node !== 'object') {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Found a data-source-list (DataProvider)
|
|
65
|
+
if (node.type === 'data-source-list' && node.content?.renderPropIdentifier) {
|
|
66
|
+
const dsIdentifier = node.content.renderPropIdentifier
|
|
67
|
+
const resourceDef = node.content.resourceDefinition || {}
|
|
68
|
+
const resourceParams = node.content.resource?.params || {}
|
|
69
|
+
|
|
70
|
+
// Look for cms-list-repeater inside this data-source-list
|
|
71
|
+
const newParent = {
|
|
72
|
+
identifier: dsIdentifier,
|
|
73
|
+
resourceDef,
|
|
74
|
+
resourceParams,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Traverse into success/error/loading nodes
|
|
78
|
+
if (node.content.nodes?.success) {
|
|
79
|
+
traverse(node.content.nodes.success, newParent)
|
|
80
|
+
}
|
|
81
|
+
if (node.content.nodes?.error) {
|
|
82
|
+
traverse(node.content.nodes.error, newParent)
|
|
83
|
+
}
|
|
84
|
+
if (node.content.nodes?.loading) {
|
|
85
|
+
traverse(node.content.nodes.loading, newParent)
|
|
86
|
+
}
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Found a cms-list-repeater (Repeater with pagination/search config)
|
|
91
|
+
const isCmsListRepeater =
|
|
92
|
+
node.type === 'cms-list-repeater' ||
|
|
93
|
+
(node.type === 'element' && node.content?.elementType === 'cms-list-repeater')
|
|
94
|
+
|
|
95
|
+
if (isCmsListRepeater && parentDataSource) {
|
|
96
|
+
const content = node.content || node
|
|
97
|
+
const arrayMapperRenderProp = content.renderPropIdentifier
|
|
98
|
+
|
|
99
|
+
if (arrayMapperRenderProp) {
|
|
100
|
+
// Extract query columns from parent's resource params
|
|
101
|
+
let queryColumns: string[] = []
|
|
102
|
+
if (parentDataSource.resourceParams?.queryColumns?.content) {
|
|
103
|
+
queryColumns = parentDataSource.resourceParams.queryColumns.content
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Extract limit from parent's resource params (for plain array mappers)
|
|
107
|
+
let limit = 0
|
|
108
|
+
if (parentDataSource.resourceParams?.limit?.content) {
|
|
109
|
+
limit = parentDataSource.resourceParams.limit.content
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// For paginated mappers, use perPage from cms-list-repeater
|
|
113
|
+
// For plain mappers, use limit from data-source-list resource params
|
|
114
|
+
const effectivePerPage = content.paginated ? content.perPage : limit || content.perPage
|
|
115
|
+
|
|
116
|
+
const usage: DataSourceUsage = {
|
|
117
|
+
index: index++,
|
|
118
|
+
dataSourceIdentifier: parentDataSource.identifier,
|
|
119
|
+
arrayMapperRenderProp,
|
|
120
|
+
resourceDefinition: {
|
|
121
|
+
dataSourceId: parentDataSource.resourceDef.dataSourceId || '',
|
|
122
|
+
tableName: parentDataSource.resourceDef.tableName || '',
|
|
123
|
+
dataSourceType: parentDataSource.resourceDef.dataSourceType || '',
|
|
124
|
+
},
|
|
125
|
+
paginated: !!content.paginated,
|
|
126
|
+
perPage: effectivePerPage,
|
|
127
|
+
searchEnabled: !!content.searchEnabled,
|
|
128
|
+
searchDebounce: content.searchDebounce || 300,
|
|
129
|
+
queryColumns,
|
|
130
|
+
category: 'plain',
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Determine category
|
|
134
|
+
if (usage.paginated && usage.searchEnabled) {
|
|
135
|
+
usage.category = 'paginated+search'
|
|
136
|
+
} else if (usage.paginated) {
|
|
137
|
+
usage.category = 'paginated-only'
|
|
138
|
+
} else if (usage.searchEnabled) {
|
|
139
|
+
usage.category = 'search-only'
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
usages.push(usage)
|
|
143
|
+
|
|
144
|
+
// Add to maps
|
|
145
|
+
if (!byDataSourceId.has(usage.dataSourceIdentifier)) {
|
|
146
|
+
byDataSourceId.set(usage.dataSourceIdentifier, [])
|
|
147
|
+
}
|
|
148
|
+
byDataSourceId.get(usage.dataSourceIdentifier)!.push(usage)
|
|
149
|
+
byArrayMapperRenderProp.set(arrayMapperRenderProp, usage)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Continue traversing inside the repeater
|
|
153
|
+
if (content.nodes?.list) {
|
|
154
|
+
traverse(content.nodes.list, parentDataSource)
|
|
155
|
+
}
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Recurse into children
|
|
160
|
+
if (node.content?.children && Array.isArray(node.content.children)) {
|
|
161
|
+
for (const child of node.content.children) {
|
|
162
|
+
traverse(child, parentDataSource)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (node.content?.node) {
|
|
166
|
+
traverse(node.content.node, parentDataSource)
|
|
167
|
+
}
|
|
168
|
+
if (node.content?.nodes) {
|
|
169
|
+
if (node.content.nodes.success) {
|
|
170
|
+
traverse(node.content.nodes.success, parentDataSource)
|
|
171
|
+
}
|
|
172
|
+
if (node.content.nodes.error) {
|
|
173
|
+
traverse(node.content.nodes.error, parentDataSource)
|
|
174
|
+
}
|
|
175
|
+
if (node.content.nodes.loading) {
|
|
176
|
+
traverse(node.content.nodes.loading, parentDataSource)
|
|
177
|
+
}
|
|
178
|
+
if (node.content.nodes.list) {
|
|
179
|
+
traverse(node.content.nodes.list, parentDataSource)
|
|
180
|
+
}
|
|
181
|
+
if (node.content.nodes.empty) {
|
|
182
|
+
traverse(node.content.nodes.empty, parentDataSource)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (Array.isArray(node.children)) {
|
|
186
|
+
for (const child of node.children) {
|
|
187
|
+
traverse(child, parentDataSource)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
traverse(uidlNode)
|
|
193
|
+
|
|
194
|
+
return { usages, byDataSourceId, byArrayMapperRenderProp }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Generate state variable names for a usage
|
|
198
|
+
function getStateVarsForUsage(usage: DataSourceUsage): {
|
|
199
|
+
pageStateVar: string
|
|
200
|
+
setPageStateVar: string
|
|
201
|
+
maxPagesStateVar: string
|
|
202
|
+
setMaxPagesStateVar: string
|
|
203
|
+
searchQueryVar: string
|
|
204
|
+
setSearchQueryVar: string
|
|
205
|
+
debouncedSearchQueryVar: string
|
|
206
|
+
setDebouncedSearchQueryVar: string
|
|
207
|
+
combinedStateVar: string
|
|
208
|
+
setCombinedStateVar: string
|
|
209
|
+
skipDebounceRefVar: string
|
|
210
|
+
skipCountFetchRefVar: string
|
|
211
|
+
propsPrefix: string
|
|
212
|
+
} {
|
|
213
|
+
const idx = usage.index
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
pageStateVar: `ds_${idx}_page`,
|
|
217
|
+
setPageStateVar: `setDs_${idx}_page`,
|
|
218
|
+
maxPagesStateVar: `ds_${idx}_maxPages`,
|
|
219
|
+
setMaxPagesStateVar: `setDs_${idx}_maxPages`,
|
|
220
|
+
searchQueryVar: `ds_${idx}_searchQuery`,
|
|
221
|
+
setSearchQueryVar: `setDs_${idx}_searchQuery`,
|
|
222
|
+
debouncedSearchQueryVar: `ds_${idx}_debouncedQuery`,
|
|
223
|
+
setDebouncedSearchQueryVar: `setDs_${idx}_debouncedQuery`,
|
|
224
|
+
combinedStateVar: `ds_${idx}_state`,
|
|
225
|
+
setCombinedStateVar: `setDs_${idx}_state`,
|
|
226
|
+
skipDebounceRefVar: `ds_${idx}_skipDebounce`,
|
|
227
|
+
skipCountFetchRefVar: `ds_${idx}_skipCountFetch`,
|
|
228
|
+
propsPrefix: `${usage.dataSourceIdentifier}_ds_${idx}`,
|
|
229
|
+
}
|
|
20
230
|
}
|
|
21
231
|
|
|
232
|
+
// ==================== MAIN PLUGIN ====================
|
|
233
|
+
|
|
22
234
|
export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> = () => {
|
|
23
235
|
const paginationPlugin: ComponentPlugin = async (structure) => {
|
|
24
236
|
const { uidl, chunks, dependencies, options } = structure
|
|
@@ -46,62 +258,56 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
46
258
|
}
|
|
47
259
|
const blockStatement = arrowFunction.body as types.BlockStatement
|
|
48
260
|
|
|
49
|
-
|
|
50
|
-
|
|
261
|
+
// STEP 1: Build state registry from UIDL
|
|
262
|
+
const registry = buildStateRegistry(uidl.node)
|
|
51
263
|
|
|
52
|
-
if (
|
|
53
|
-
paginatedMappers.length === 0 &&
|
|
54
|
-
searchOnlyMappers.length === 0 &&
|
|
55
|
-
paginationOnlyMappers.length === 0
|
|
56
|
-
) {
|
|
264
|
+
if (registry.usages.length === 0) {
|
|
57
265
|
return structure
|
|
58
266
|
}
|
|
59
267
|
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
const
|
|
268
|
+
// Check if this is a page or component
|
|
269
|
+
const getStaticPropsChunk = chunks.find((chunk) => chunk.name === 'getStaticProps')
|
|
270
|
+
const isPage = !!getStaticPropsChunk
|
|
63
271
|
|
|
272
|
+
// Add React dependencies
|
|
64
273
|
if (!dependencies.useState) {
|
|
65
274
|
dependencies.useState = {
|
|
66
275
|
type: 'library',
|
|
67
276
|
path: 'react',
|
|
68
277
|
version: '',
|
|
69
|
-
meta: {
|
|
70
|
-
namedImport: true,
|
|
71
|
-
},
|
|
278
|
+
meta: { namedImport: true },
|
|
72
279
|
}
|
|
73
280
|
}
|
|
74
|
-
|
|
75
281
|
if (!dependencies.useMemo) {
|
|
76
282
|
dependencies.useMemo = {
|
|
77
283
|
type: 'library',
|
|
78
284
|
path: 'react',
|
|
79
285
|
version: '',
|
|
80
|
-
meta: {
|
|
81
|
-
namedImport: true,
|
|
82
|
-
},
|
|
286
|
+
meta: { namedImport: true },
|
|
83
287
|
}
|
|
84
288
|
}
|
|
85
|
-
|
|
86
289
|
if (!dependencies.useCallback) {
|
|
87
290
|
dependencies.useCallback = {
|
|
88
291
|
type: 'library',
|
|
89
292
|
path: 'react',
|
|
90
293
|
version: '',
|
|
91
|
-
meta: {
|
|
92
|
-
namedImport: true,
|
|
93
|
-
},
|
|
294
|
+
meta: { namedImport: true },
|
|
94
295
|
}
|
|
95
296
|
}
|
|
96
|
-
|
|
97
297
|
if (!dependencies.useRef) {
|
|
98
298
|
dependencies.useRef = {
|
|
99
299
|
type: 'library',
|
|
100
300
|
path: 'react',
|
|
101
301
|
version: '',
|
|
102
|
-
meta: {
|
|
103
|
-
|
|
104
|
-
|
|
302
|
+
meta: { namedImport: true },
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (!dependencies.useEffect) {
|
|
306
|
+
dependencies.useEffect = {
|
|
307
|
+
type: 'library',
|
|
308
|
+
path: 'react',
|
|
309
|
+
version: '',
|
|
310
|
+
meta: { namedImport: true },
|
|
105
311
|
}
|
|
106
312
|
}
|
|
107
313
|
|
|
@@ -110,2389 +316,1911 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
110
316
|
}
|
|
111
317
|
componentChunk.meta.isClientComponent = true
|
|
112
318
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// Check if this is a page (has getStaticProps) or a component
|
|
116
|
-
const getStaticPropsChunk = chunks.find((chunk) => chunk.name === 'getStaticProps')
|
|
117
|
-
const isPage = !!getStaticPropsChunk
|
|
118
|
-
const isComponent = !isPage
|
|
119
|
-
|
|
120
|
-
// Get pagination and search config from early extraction (done before transformations)
|
|
121
|
-
const opts = options as any
|
|
122
|
-
const perPageMap = opts.paginationConfig?.perPageMap || new Map<string, number>()
|
|
123
|
-
const searchConfigMap = opts.paginationConfig?.searchConfigMap || new Map<string, any>()
|
|
124
|
-
const queryColumnsMap = opts.paginationConfig?.queryColumnsMap || new Map<string, string[]>()
|
|
125
|
-
|
|
319
|
+
// STEP 2: Generate state declarations for all usages
|
|
126
320
|
const stateDeclarations: types.Statement[] = []
|
|
321
|
+
const effectStatements: types.Statement[] = []
|
|
127
322
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const lookupKey = detected.arrayMapperRenderProp || detected.dataSourceIdentifier
|
|
132
|
-
const perPage = perPageMap.get(lookupKey) || 10
|
|
133
|
-
const searchConfig = searchConfigMap.get(lookupKey)
|
|
134
|
-
// queryColumns is keyed by dataSourceIdentifier, not array mapper render prop
|
|
135
|
-
const queryColumns = queryColumnsMap.get(detected.dataSourceIdentifier)
|
|
136
|
-
|
|
137
|
-
const info = generatePaginationLogic(
|
|
138
|
-
paginationNodeId,
|
|
139
|
-
detected.dataSourceIdentifier,
|
|
140
|
-
perPage,
|
|
141
|
-
searchConfig,
|
|
142
|
-
queryColumns
|
|
143
|
-
)
|
|
144
|
-
paginationInfos.push(info)
|
|
145
|
-
|
|
146
|
-
// Add refs to track first render for each useEffect (add first)
|
|
147
|
-
if (info.searchEnabled) {
|
|
148
|
-
const skipCountFetchOnMountRefVar = `skipCountFetchOnMount_pg_${index}`
|
|
149
|
-
const skipCountFetchRefAST = types.variableDeclaration('const', [
|
|
150
|
-
types.variableDeclarator(
|
|
151
|
-
types.identifier(skipCountFetchOnMountRefVar),
|
|
152
|
-
types.callExpression(types.identifier('useRef'), [types.booleanLiteral(true)])
|
|
153
|
-
),
|
|
154
|
-
])
|
|
155
|
-
stateDeclarations.push(skipCountFetchRefAST)
|
|
156
|
-
;(info as any).skipCountFetchOnMountRefVar = skipCountFetchOnMountRefVar
|
|
157
|
-
|
|
158
|
-
const skipDebounceOnMountRefVar = `skipDebounceOnMount_pg_${index}`
|
|
159
|
-
const skipDebounceRefAST = types.variableDeclaration('const', [
|
|
160
|
-
types.variableDeclarator(
|
|
161
|
-
types.identifier(skipDebounceOnMountRefVar),
|
|
162
|
-
types.callExpression(types.identifier('useRef'), [types.booleanLiteral(true)])
|
|
163
|
-
),
|
|
164
|
-
])
|
|
165
|
-
stateDeclarations.push(skipDebounceRefAST)
|
|
166
|
-
;(info as any).skipDebounceOnMountRefVar = skipDebounceOnMountRefVar
|
|
323
|
+
registry.usages.forEach((usage) => {
|
|
324
|
+
if (usage.category === 'plain') {
|
|
325
|
+
return // Plain mappers don't need state
|
|
167
326
|
}
|
|
168
327
|
|
|
169
|
-
|
|
170
|
-
const maxPagesStateVar = `${info.pageStateVar.replace('_page', '')}_maxPages`
|
|
171
|
-
const setMaxPagesStateVar = `set${
|
|
172
|
-
maxPagesStateVar.charAt(0).toUpperCase() + maxPagesStateVar.slice(1)
|
|
173
|
-
}`
|
|
174
|
-
|
|
175
|
-
// For pages: initialize from props (with pagination-specific prop name), for components: initialize to 0
|
|
176
|
-
const maxPagesInitValue = isPage
|
|
177
|
-
? types.logicalExpression(
|
|
178
|
-
'||',
|
|
179
|
-
types.optionalMemberExpression(
|
|
180
|
-
types.identifier('props'),
|
|
181
|
-
types.identifier(`${info.dataSourceIdentifier}_pg_${index}_maxPages`),
|
|
182
|
-
false,
|
|
183
|
-
true
|
|
184
|
-
),
|
|
185
|
-
types.numericLiteral(0)
|
|
186
|
-
)
|
|
187
|
-
: types.numericLiteral(0)
|
|
188
|
-
|
|
189
|
-
const maxPagesStateAST = types.variableDeclaration('const', [
|
|
190
|
-
types.variableDeclarator(
|
|
191
|
-
types.arrayPattern([
|
|
192
|
-
types.identifier(maxPagesStateVar),
|
|
193
|
-
types.identifier(setMaxPagesStateVar),
|
|
194
|
-
]),
|
|
195
|
-
types.callExpression(types.identifier('useState'), [maxPagesInitValue])
|
|
196
|
-
),
|
|
197
|
-
])
|
|
198
|
-
stateDeclarations.push(maxPagesStateAST)
|
|
199
|
-
|
|
200
|
-
// Store these for later use
|
|
201
|
-
;(info as any).maxPagesStateVar = maxPagesStateVar
|
|
202
|
-
;(info as any).setMaxPagesStateVar = setMaxPagesStateVar
|
|
203
|
-
|
|
204
|
-
// If both pagination and search are enabled, combine them into a single state object
|
|
205
|
-
if (info.searchEnabled && info.searchQueryVar && info.setSearchQueryVar) {
|
|
206
|
-
// Combined state: { page: 1, debouncedQuery: '' }
|
|
207
|
-
const combinedStateVar = `paginationState_pg_${index}`
|
|
208
|
-
const setCombinedStateVar = `setPaginationState_pg_${index}`
|
|
209
|
-
|
|
210
|
-
const combinedStateAST = types.variableDeclaration('const', [
|
|
211
|
-
types.variableDeclarator(
|
|
212
|
-
types.arrayPattern([
|
|
213
|
-
types.identifier(combinedStateVar),
|
|
214
|
-
types.identifier(setCombinedStateVar),
|
|
215
|
-
]),
|
|
216
|
-
types.callExpression(types.identifier('useState'), [
|
|
217
|
-
types.objectExpression([
|
|
218
|
-
types.objectProperty(types.identifier('page'), types.numericLiteral(1)),
|
|
219
|
-
types.objectProperty(types.identifier('debouncedQuery'), types.stringLiteral('')),
|
|
220
|
-
]),
|
|
221
|
-
])
|
|
222
|
-
),
|
|
223
|
-
])
|
|
224
|
-
stateDeclarations.push(combinedStateAST)
|
|
225
|
-
|
|
226
|
-
// Still need the immediate search query state for the input
|
|
227
|
-
const searchStateAST = types.variableDeclaration('const', [
|
|
228
|
-
types.variableDeclarator(
|
|
229
|
-
types.arrayPattern([
|
|
230
|
-
types.identifier(info.searchQueryVar),
|
|
231
|
-
types.identifier(info.setSearchQueryVar),
|
|
232
|
-
]),
|
|
233
|
-
types.callExpression(types.identifier('useState'), [types.stringLiteral('')])
|
|
234
|
-
),
|
|
235
|
-
])
|
|
236
|
-
stateDeclarations.push(searchStateAST)
|
|
328
|
+
const vars = getStateVarsForUsage(usage)
|
|
237
329
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
]),
|
|
249
|
-
types.callExpression(types.identifier('useState'), [types.numericLiteral(1)])
|
|
250
|
-
),
|
|
251
|
-
])
|
|
252
|
-
stateDeclarations.push(pageStateAST)
|
|
253
|
-
}
|
|
254
|
-
})
|
|
330
|
+
if (usage.category === 'paginated+search') {
|
|
331
|
+
// Combined state object for pagination + search
|
|
332
|
+
stateDeclarations.push(
|
|
333
|
+
types.variableDeclaration('const', [
|
|
334
|
+
types.variableDeclarator(
|
|
335
|
+
types.identifier(vars.skipDebounceRefVar),
|
|
336
|
+
types.callExpression(types.identifier('useRef'), [types.booleanLiteral(true)])
|
|
337
|
+
),
|
|
338
|
+
])
|
|
339
|
+
)
|
|
255
340
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
341
|
+
// Only add skipCountFetchRef for pages (where we have server-side count)
|
|
342
|
+
// For components, we need to fetch count on mount
|
|
343
|
+
if (isPage) {
|
|
344
|
+
stateDeclarations.push(
|
|
345
|
+
types.variableDeclaration('const', [
|
|
346
|
+
types.variableDeclarator(
|
|
347
|
+
types.identifier(vars.skipCountFetchRefVar),
|
|
348
|
+
types.callExpression(types.identifier('useRef'), [types.booleanLiteral(true)])
|
|
349
|
+
),
|
|
350
|
+
])
|
|
351
|
+
)
|
|
352
|
+
}
|
|
260
353
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
354
|
+
// maxPages state
|
|
355
|
+
const maxPagesInit = isPage
|
|
356
|
+
? types.logicalExpression(
|
|
357
|
+
'||',
|
|
358
|
+
types.optionalMemberExpression(
|
|
359
|
+
types.identifier('props'),
|
|
360
|
+
types.identifier(`${vars.propsPrefix}_maxPages`),
|
|
361
|
+
false,
|
|
362
|
+
true
|
|
363
|
+
),
|
|
364
|
+
types.numericLiteral(0)
|
|
365
|
+
)
|
|
366
|
+
: types.numericLiteral(0)
|
|
367
|
+
|
|
368
|
+
stateDeclarations.push(
|
|
369
|
+
types.variableDeclaration('const', [
|
|
370
|
+
types.variableDeclarator(
|
|
371
|
+
types.arrayPattern([
|
|
372
|
+
types.identifier(vars.maxPagesStateVar),
|
|
373
|
+
types.identifier(vars.setMaxPagesStateVar),
|
|
374
|
+
]),
|
|
375
|
+
types.callExpression(types.identifier('useState'), [maxPagesInit])
|
|
376
|
+
),
|
|
377
|
+
])
|
|
378
|
+
)
|
|
273
379
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
info.debouncedSearchQueryVar &&
|
|
287
|
-
info.setDebouncedSearchQueryVar
|
|
288
|
-
) {
|
|
289
|
-
// Add effect that updates debounced search after delay
|
|
290
|
-
const skipDebounceOnMountRefVar = (info as any).skipDebounceOnMountRefVar
|
|
291
|
-
const setCombinedStateVar = (info as any).setCombinedStateVar
|
|
292
|
-
|
|
293
|
-
const debounceEffect = types.expressionStatement(
|
|
294
|
-
types.callExpression(types.identifier('useEffect'), [
|
|
295
|
-
types.arrowFunctionExpression(
|
|
296
|
-
[],
|
|
297
|
-
types.blockStatement([
|
|
298
|
-
types.ifStatement(
|
|
299
|
-
types.memberExpression(
|
|
300
|
-
types.identifier(skipDebounceOnMountRefVar),
|
|
301
|
-
types.identifier('current')
|
|
302
|
-
),
|
|
303
|
-
types.blockStatement([
|
|
304
|
-
types.expressionStatement(
|
|
305
|
-
types.assignmentExpression(
|
|
306
|
-
'=',
|
|
307
|
-
types.memberExpression(
|
|
308
|
-
types.identifier(skipDebounceOnMountRefVar),
|
|
309
|
-
types.identifier('current')
|
|
310
|
-
),
|
|
311
|
-
types.booleanLiteral(false)
|
|
312
|
-
)
|
|
313
|
-
),
|
|
314
|
-
types.returnStatement(),
|
|
315
|
-
])
|
|
316
|
-
),
|
|
317
|
-
types.variableDeclaration('const', [
|
|
318
|
-
types.variableDeclarator(
|
|
319
|
-
types.identifier('timer'),
|
|
320
|
-
types.callExpression(types.identifier('setTimeout'), [
|
|
321
|
-
types.arrowFunctionExpression(
|
|
322
|
-
[],
|
|
323
|
-
types.blockStatement([
|
|
324
|
-
types.expressionStatement(
|
|
325
|
-
types.callExpression(types.identifier(setCombinedStateVar), [
|
|
326
|
-
types.objectExpression([
|
|
327
|
-
types.objectProperty(
|
|
328
|
-
types.identifier('page'),
|
|
329
|
-
types.numericLiteral(1)
|
|
330
|
-
),
|
|
331
|
-
types.objectProperty(
|
|
332
|
-
types.identifier('debouncedQuery'),
|
|
333
|
-
types.identifier(info.searchQueryVar)
|
|
334
|
-
),
|
|
335
|
-
]),
|
|
336
|
-
])
|
|
337
|
-
),
|
|
338
|
-
])
|
|
339
|
-
),
|
|
340
|
-
types.numericLiteral(info.searchDebounce || 300),
|
|
341
|
-
])
|
|
342
|
-
),
|
|
380
|
+
// Combined state { page, debouncedQuery }
|
|
381
|
+
stateDeclarations.push(
|
|
382
|
+
types.variableDeclaration('const', [
|
|
383
|
+
types.variableDeclarator(
|
|
384
|
+
types.arrayPattern([
|
|
385
|
+
types.identifier(vars.combinedStateVar),
|
|
386
|
+
types.identifier(vars.setCombinedStateVar),
|
|
387
|
+
]),
|
|
388
|
+
types.callExpression(types.identifier('useState'), [
|
|
389
|
+
types.objectExpression([
|
|
390
|
+
types.objectProperty(types.identifier('page'), types.numericLiteral(1)),
|
|
391
|
+
types.objectProperty(types.identifier('debouncedQuery'), types.stringLiteral('')),
|
|
343
392
|
]),
|
|
344
|
-
types.returnStatement(
|
|
345
|
-
types.arrowFunctionExpression(
|
|
346
|
-
[],
|
|
347
|
-
types.callExpression(types.identifier('clearTimeout'), [
|
|
348
|
-
types.identifier('timer'),
|
|
349
|
-
])
|
|
350
|
-
)
|
|
351
|
-
),
|
|
352
393
|
])
|
|
353
394
|
),
|
|
354
|
-
types.arrayExpression([types.identifier(info.searchQueryVar)]),
|
|
355
395
|
])
|
|
356
396
|
)
|
|
357
397
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'resourceDefinition'
|
|
398
|
+
// Immediate search query state
|
|
399
|
+
stateDeclarations.push(
|
|
400
|
+
types.variableDeclaration('const', [
|
|
401
|
+
types.variableDeclarator(
|
|
402
|
+
types.arrayPattern([
|
|
403
|
+
types.identifier(vars.searchQueryVar),
|
|
404
|
+
types.identifier(vars.setSearchQueryVar),
|
|
405
|
+
]),
|
|
406
|
+
types.callExpression(types.identifier('useState'), [types.stringLiteral('')])
|
|
407
|
+
),
|
|
408
|
+
])
|
|
370
409
|
)
|
|
371
410
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
)
|
|
385
|
-
const dataSourceTypeProp = (resourceDef.properties as any[]).find(
|
|
386
|
-
(p: any) => p.type === 'ObjectProperty' && p.key.value === 'dataSourceType'
|
|
387
|
-
)
|
|
388
|
-
|
|
389
|
-
if (dataSourceIdProp && tableNameProp && dataSourceTypeProp) {
|
|
390
|
-
const dataSourceId = dataSourceIdProp.value.value
|
|
391
|
-
const tableName = tableNameProp.value.value
|
|
392
|
-
const dataSourceType = dataSourceTypeProp.value.value
|
|
393
|
-
const fileName = `${dataSourceType}-${tableName}-${dataSourceId.substring(0, 8)}`
|
|
394
|
-
const setMaxPagesStateVar = (info as any).setMaxPagesStateVar
|
|
395
|
-
|
|
396
|
-
// Create useEffect to refetch count when debounced search changes
|
|
397
|
-
const skipCountFetchOnMountRefVar = (info as any).skipCountFetchOnMountRefVar
|
|
398
|
-
const combinedStateVar = (info as any).combinedStateVar
|
|
399
|
-
|
|
400
|
-
// Build URLSearchParams properties - query is always included, queryColumns is optional
|
|
401
|
-
const urlSearchParamsProperties: any[] = [
|
|
402
|
-
types.objectProperty(
|
|
403
|
-
types.identifier('query'),
|
|
404
|
-
types.memberExpression(
|
|
405
|
-
types.identifier(combinedStateVar),
|
|
406
|
-
types.identifier('debouncedQuery')
|
|
407
|
-
)
|
|
408
|
-
),
|
|
409
|
-
]
|
|
410
|
-
|
|
411
|
-
// Add queryColumns only if they exist
|
|
412
|
-
if (info.queryColumns && info.queryColumns.length > 0) {
|
|
413
|
-
urlSearchParamsProperties.push(
|
|
414
|
-
types.objectProperty(
|
|
415
|
-
types.identifier('queryColumns'),
|
|
416
|
-
types.callExpression(
|
|
417
|
-
types.memberExpression(
|
|
418
|
-
types.identifier('JSON'),
|
|
419
|
-
types.identifier('stringify')
|
|
420
|
-
),
|
|
421
|
-
[
|
|
422
|
-
types.arrayExpression(
|
|
423
|
-
info.queryColumns.map((col) => types.stringLiteral(col))
|
|
424
|
-
),
|
|
425
|
-
]
|
|
426
|
-
)
|
|
427
|
-
)
|
|
428
|
-
)
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
const refetchCountEffect = types.expressionStatement(
|
|
432
|
-
types.callExpression(types.identifier('useEffect'), [
|
|
433
|
-
types.arrowFunctionExpression(
|
|
434
|
-
[],
|
|
411
|
+
// Debounce effect
|
|
412
|
+
effectStatements.push(
|
|
413
|
+
types.expressionStatement(
|
|
414
|
+
types.callExpression(types.identifier('useEffect'), [
|
|
415
|
+
types.arrowFunctionExpression(
|
|
416
|
+
[],
|
|
417
|
+
types.blockStatement([
|
|
418
|
+
types.ifStatement(
|
|
419
|
+
types.memberExpression(
|
|
420
|
+
types.identifier(vars.skipDebounceRefVar),
|
|
421
|
+
types.identifier('current')
|
|
422
|
+
),
|
|
435
423
|
types.blockStatement([
|
|
436
|
-
types.ifStatement(
|
|
437
|
-
types.memberExpression(
|
|
438
|
-
types.identifier(skipCountFetchOnMountRefVar),
|
|
439
|
-
types.identifier('current')
|
|
440
|
-
),
|
|
441
|
-
types.blockStatement([
|
|
442
|
-
types.expressionStatement(
|
|
443
|
-
types.assignmentExpression(
|
|
444
|
-
'=',
|
|
445
|
-
types.memberExpression(
|
|
446
|
-
types.identifier(skipCountFetchOnMountRefVar),
|
|
447
|
-
types.identifier('current')
|
|
448
|
-
),
|
|
449
|
-
types.booleanLiteral(false)
|
|
450
|
-
)
|
|
451
|
-
),
|
|
452
|
-
types.returnStatement(),
|
|
453
|
-
])
|
|
454
|
-
),
|
|
455
424
|
types.expressionStatement(
|
|
456
|
-
types.
|
|
425
|
+
types.assignmentExpression(
|
|
426
|
+
'=',
|
|
457
427
|
types.memberExpression(
|
|
458
|
-
types.
|
|
459
|
-
|
|
460
|
-
types.callExpression(types.identifier('fetch'), [
|
|
461
|
-
types.templateLiteral(
|
|
462
|
-
[
|
|
463
|
-
types.templateElement({
|
|
464
|
-
raw: `/api/${fileName}-count?`,
|
|
465
|
-
cooked: `/api/${fileName}-count?`,
|
|
466
|
-
}),
|
|
467
|
-
types.templateElement({ raw: '', cooked: '' }),
|
|
468
|
-
],
|
|
469
|
-
[
|
|
470
|
-
types.newExpression(types.identifier('URLSearchParams'), [
|
|
471
|
-
types.objectExpression(urlSearchParamsProperties),
|
|
472
|
-
]),
|
|
473
|
-
]
|
|
474
|
-
),
|
|
475
|
-
]),
|
|
476
|
-
types.identifier('then')
|
|
477
|
-
),
|
|
478
|
-
[
|
|
479
|
-
types.arrowFunctionExpression(
|
|
480
|
-
[types.identifier('res')],
|
|
481
|
-
types.callExpression(
|
|
482
|
-
types.memberExpression(
|
|
483
|
-
types.identifier('res'),
|
|
484
|
-
types.identifier('json')
|
|
485
|
-
),
|
|
486
|
-
[]
|
|
487
|
-
)
|
|
488
|
-
),
|
|
489
|
-
]
|
|
490
|
-
),
|
|
491
|
-
types.identifier('then')
|
|
428
|
+
types.identifier(vars.skipDebounceRefVar),
|
|
429
|
+
types.identifier('current')
|
|
492
430
|
),
|
|
493
|
-
|
|
494
|
-
types.arrowFunctionExpression(
|
|
495
|
-
[types.identifier('data')],
|
|
496
|
-
types.blockStatement([
|
|
497
|
-
types.ifStatement(
|
|
498
|
-
types.logicalExpression(
|
|
499
|
-
'&&',
|
|
500
|
-
types.identifier('data'),
|
|
501
|
-
types.binaryExpression(
|
|
502
|
-
'in',
|
|
503
|
-
types.stringLiteral('count'),
|
|
504
|
-
types.identifier('data')
|
|
505
|
-
)
|
|
506
|
-
),
|
|
507
|
-
types.blockStatement([
|
|
508
|
-
types.expressionStatement(
|
|
509
|
-
types.callExpression(types.identifier(setMaxPagesStateVar), [
|
|
510
|
-
types.conditionalExpression(
|
|
511
|
-
types.binaryExpression(
|
|
512
|
-
'===',
|
|
513
|
-
types.memberExpression(
|
|
514
|
-
types.identifier('data'),
|
|
515
|
-
types.identifier('count')
|
|
516
|
-
),
|
|
517
|
-
types.numericLiteral(0)
|
|
518
|
-
),
|
|
519
|
-
types.numericLiteral(0),
|
|
520
|
-
types.callExpression(
|
|
521
|
-
types.memberExpression(
|
|
522
|
-
types.identifier('Math'),
|
|
523
|
-
types.identifier('ceil')
|
|
524
|
-
),
|
|
525
|
-
[
|
|
526
|
-
types.binaryExpression(
|
|
527
|
-
'/',
|
|
528
|
-
types.memberExpression(
|
|
529
|
-
types.identifier('data'),
|
|
530
|
-
types.identifier('count')
|
|
531
|
-
),
|
|
532
|
-
types.numericLiteral(info.perPage)
|
|
533
|
-
),
|
|
534
|
-
]
|
|
535
|
-
)
|
|
536
|
-
),
|
|
537
|
-
])
|
|
538
|
-
),
|
|
539
|
-
])
|
|
540
|
-
),
|
|
541
|
-
])
|
|
542
|
-
),
|
|
543
|
-
]
|
|
431
|
+
types.booleanLiteral(false)
|
|
544
432
|
)
|
|
545
433
|
),
|
|
434
|
+
types.returnStatement(),
|
|
546
435
|
])
|
|
547
436
|
),
|
|
548
|
-
types.
|
|
549
|
-
types.
|
|
550
|
-
types.identifier(
|
|
551
|
-
types.identifier('
|
|
437
|
+
types.variableDeclaration('const', [
|
|
438
|
+
types.variableDeclarator(
|
|
439
|
+
types.identifier('timer'),
|
|
440
|
+
types.callExpression(types.identifier('setTimeout'), [
|
|
441
|
+
types.arrowFunctionExpression(
|
|
442
|
+
[],
|
|
443
|
+
types.blockStatement([
|
|
444
|
+
types.expressionStatement(
|
|
445
|
+
types.callExpression(types.identifier(vars.setCombinedStateVar), [
|
|
446
|
+
types.objectExpression([
|
|
447
|
+
types.objectProperty(
|
|
448
|
+
types.identifier('page'),
|
|
449
|
+
types.numericLiteral(1)
|
|
450
|
+
),
|
|
451
|
+
types.objectProperty(
|
|
452
|
+
types.identifier('debouncedQuery'),
|
|
453
|
+
types.identifier(vars.searchQueryVar)
|
|
454
|
+
),
|
|
455
|
+
]),
|
|
456
|
+
])
|
|
457
|
+
),
|
|
458
|
+
])
|
|
459
|
+
),
|
|
460
|
+
types.numericLiteral(usage.searchDebounce),
|
|
461
|
+
])
|
|
552
462
|
),
|
|
553
463
|
]),
|
|
464
|
+
types.returnStatement(
|
|
465
|
+
types.arrowFunctionExpression(
|
|
466
|
+
[],
|
|
467
|
+
types.callExpression(types.identifier('clearTimeout'), [
|
|
468
|
+
types.identifier('timer'),
|
|
469
|
+
])
|
|
470
|
+
)
|
|
471
|
+
),
|
|
554
472
|
])
|
|
555
|
-
)
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
})
|
|
563
|
-
|
|
564
|
-
// For components, add useEffect to fetch count on mount
|
|
565
|
-
if (isComponent) {
|
|
566
|
-
if (!dependencies.useEffect) {
|
|
567
|
-
dependencies.useEffect = {
|
|
568
|
-
type: 'library',
|
|
569
|
-
path: 'react',
|
|
570
|
-
version: '',
|
|
571
|
-
meta: {
|
|
572
|
-
namedImport: true,
|
|
573
|
-
},
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// Group paginationInfos by data source identifier to avoid duplicate fetches
|
|
578
|
-
const dataSourceToInfos = new Map<
|
|
579
|
-
string,
|
|
580
|
-
Array<{ info: ArrayMapperPaginationInfo; detected: any; fileName: string }>
|
|
581
|
-
>()
|
|
473
|
+
),
|
|
474
|
+
types.arrayExpression([types.identifier(vars.searchQueryVar)]),
|
|
475
|
+
])
|
|
476
|
+
)
|
|
477
|
+
)
|
|
582
478
|
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
|
|
479
|
+
// Count refetch effect
|
|
480
|
+
const fileName = generateSafeFileName(
|
|
481
|
+
usage.resourceDefinition.dataSourceType,
|
|
482
|
+
usage.resourceDefinition.tableName,
|
|
483
|
+
usage.resourceDefinition.dataSourceId
|
|
586
484
|
)
|
|
587
|
-
|
|
588
|
-
|
|
485
|
+
const urlParams: types.ObjectProperty[] = [
|
|
486
|
+
types.objectProperty(
|
|
487
|
+
types.identifier('query'),
|
|
488
|
+
types.memberExpression(
|
|
489
|
+
types.identifier(vars.combinedStateVar),
|
|
490
|
+
types.identifier('debouncedQuery')
|
|
491
|
+
)
|
|
492
|
+
),
|
|
493
|
+
]
|
|
494
|
+
if (usage.queryColumns.length > 0) {
|
|
495
|
+
urlParams.push(
|
|
496
|
+
types.objectProperty(
|
|
497
|
+
types.identifier('queryColumns'),
|
|
498
|
+
types.callExpression(
|
|
499
|
+
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
500
|
+
[types.arrayExpression(usage.queryColumns.map((c) => types.stringLiteral(c)))]
|
|
501
|
+
)
|
|
502
|
+
)
|
|
503
|
+
)
|
|
589
504
|
}
|
|
590
505
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
)
|
|
506
|
+
// Build the count fetch effect body
|
|
507
|
+
const countFetchEffectBody: types.Statement[] = []
|
|
594
508
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
509
|
+
// Only add skip-on-mount check for pages (where we have server-side count)
|
|
510
|
+
if (isPage) {
|
|
511
|
+
countFetchEffectBody.push(
|
|
512
|
+
types.ifStatement(
|
|
513
|
+
types.memberExpression(
|
|
514
|
+
types.identifier(vars.skipCountFetchRefVar),
|
|
515
|
+
types.identifier('current')
|
|
516
|
+
),
|
|
517
|
+
types.blockStatement([
|
|
518
|
+
types.expressionStatement(
|
|
519
|
+
types.assignmentExpression(
|
|
520
|
+
'=',
|
|
521
|
+
types.memberExpression(
|
|
522
|
+
types.identifier(vars.skipCountFetchRefVar),
|
|
523
|
+
types.identifier('current')
|
|
524
|
+
),
|
|
525
|
+
types.booleanLiteral(false)
|
|
526
|
+
)
|
|
527
|
+
),
|
|
528
|
+
types.returnStatement(),
|
|
529
|
+
])
|
|
530
|
+
)
|
|
531
|
+
)
|
|
606
532
|
}
|
|
607
533
|
|
|
608
|
-
|
|
609
|
-
|
|
534
|
+
// Add the fetch call
|
|
535
|
+
countFetchEffectBody.push(
|
|
536
|
+
types.expressionStatement(
|
|
537
|
+
types.callExpression(
|
|
538
|
+
types.memberExpression(
|
|
539
|
+
types.callExpression(
|
|
540
|
+
types.memberExpression(
|
|
541
|
+
types.callExpression(types.identifier('fetch'), [
|
|
542
|
+
types.templateLiteral(
|
|
543
|
+
[
|
|
544
|
+
types.templateElement({
|
|
545
|
+
raw: `/api/${fileName}-count?`,
|
|
546
|
+
cooked: `/api/${fileName}-count?`,
|
|
547
|
+
}),
|
|
548
|
+
types.templateElement({ raw: '', cooked: '' }),
|
|
549
|
+
],
|
|
550
|
+
[
|
|
551
|
+
types.newExpression(types.identifier('URLSearchParams'), [
|
|
552
|
+
types.objectExpression(urlParams),
|
|
553
|
+
]),
|
|
554
|
+
]
|
|
555
|
+
),
|
|
556
|
+
]),
|
|
557
|
+
types.identifier('then')
|
|
558
|
+
),
|
|
559
|
+
[
|
|
560
|
+
types.arrowFunctionExpression(
|
|
561
|
+
[types.identifier('res')],
|
|
562
|
+
types.callExpression(
|
|
563
|
+
types.memberExpression(types.identifier('res'), types.identifier('json')),
|
|
564
|
+
[]
|
|
565
|
+
)
|
|
566
|
+
),
|
|
567
|
+
]
|
|
568
|
+
),
|
|
569
|
+
types.identifier('then')
|
|
570
|
+
),
|
|
571
|
+
[
|
|
572
|
+
types.arrowFunctionExpression(
|
|
573
|
+
[types.identifier('data')],
|
|
574
|
+
types.blockStatement([
|
|
575
|
+
types.ifStatement(
|
|
576
|
+
types.logicalExpression(
|
|
577
|
+
'&&',
|
|
578
|
+
types.identifier('data'),
|
|
579
|
+
types.binaryExpression(
|
|
580
|
+
'in',
|
|
581
|
+
types.stringLiteral('count'),
|
|
582
|
+
types.identifier('data')
|
|
583
|
+
)
|
|
584
|
+
),
|
|
585
|
+
types.blockStatement([
|
|
586
|
+
types.expressionStatement(
|
|
587
|
+
types.callExpression(types.identifier(vars.setMaxPagesStateVar), [
|
|
588
|
+
types.conditionalExpression(
|
|
589
|
+
types.binaryExpression(
|
|
590
|
+
'===',
|
|
591
|
+
types.memberExpression(
|
|
592
|
+
types.identifier('data'),
|
|
593
|
+
types.identifier('count')
|
|
594
|
+
),
|
|
595
|
+
types.numericLiteral(0)
|
|
596
|
+
),
|
|
597
|
+
types.numericLiteral(0),
|
|
598
|
+
types.callExpression(
|
|
599
|
+
types.memberExpression(
|
|
600
|
+
types.identifier('Math'),
|
|
601
|
+
types.identifier('ceil')
|
|
602
|
+
),
|
|
603
|
+
[
|
|
604
|
+
types.binaryExpression(
|
|
605
|
+
'/',
|
|
606
|
+
types.memberExpression(
|
|
607
|
+
types.identifier('data'),
|
|
608
|
+
types.identifier('count')
|
|
609
|
+
),
|
|
610
|
+
types.numericLiteral(usage.perPage)
|
|
611
|
+
),
|
|
612
|
+
]
|
|
613
|
+
)
|
|
614
|
+
),
|
|
615
|
+
])
|
|
616
|
+
),
|
|
617
|
+
])
|
|
618
|
+
),
|
|
619
|
+
])
|
|
620
|
+
),
|
|
621
|
+
]
|
|
622
|
+
)
|
|
623
|
+
)
|
|
610
624
|
)
|
|
611
|
-
|
|
612
|
-
|
|
625
|
+
|
|
626
|
+
effectStatements.push(
|
|
627
|
+
types.expressionStatement(
|
|
628
|
+
types.callExpression(types.identifier('useEffect'), [
|
|
629
|
+
types.arrowFunctionExpression([], types.blockStatement(countFetchEffectBody)),
|
|
630
|
+
types.arrayExpression([
|
|
631
|
+
types.memberExpression(
|
|
632
|
+
types.identifier(vars.combinedStateVar),
|
|
633
|
+
types.identifier('debouncedQuery')
|
|
634
|
+
),
|
|
635
|
+
]),
|
|
636
|
+
])
|
|
637
|
+
)
|
|
613
638
|
)
|
|
614
|
-
|
|
615
|
-
|
|
639
|
+
} else if (usage.category === 'paginated-only') {
|
|
640
|
+
// Simple page state
|
|
641
|
+
const maxPagesInit = isPage
|
|
642
|
+
? types.logicalExpression(
|
|
643
|
+
'||',
|
|
644
|
+
types.optionalMemberExpression(
|
|
645
|
+
types.identifier('props'),
|
|
646
|
+
types.identifier(`${vars.propsPrefix}_maxPages`),
|
|
647
|
+
false,
|
|
648
|
+
true
|
|
649
|
+
),
|
|
650
|
+
types.numericLiteral(0)
|
|
651
|
+
)
|
|
652
|
+
: types.numericLiteral(0)
|
|
653
|
+
|
|
654
|
+
stateDeclarations.push(
|
|
655
|
+
types.variableDeclaration('const', [
|
|
656
|
+
types.variableDeclarator(
|
|
657
|
+
types.arrayPattern([
|
|
658
|
+
types.identifier(vars.maxPagesStateVar),
|
|
659
|
+
types.identifier(vars.setMaxPagesStateVar),
|
|
660
|
+
]),
|
|
661
|
+
types.callExpression(types.identifier('useState'), [maxPagesInit])
|
|
662
|
+
),
|
|
663
|
+
])
|
|
616
664
|
)
|
|
617
665
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
dataSourceToInfos.set(info.dataSourceIdentifier, [])
|
|
630
|
-
}
|
|
631
|
-
dataSourceToInfos.get(info.dataSourceIdentifier)!.push({ info, detected, fileName })
|
|
632
|
-
})
|
|
633
|
-
|
|
634
|
-
// Create ONE useEffect per unique data source
|
|
635
|
-
// Collect all useEffect statements first
|
|
636
|
-
const componentUseEffects: types.Statement[] = []
|
|
637
|
-
|
|
638
|
-
dataSourceToInfos.forEach((infos) => {
|
|
639
|
-
const { fileName } = infos[0]
|
|
666
|
+
stateDeclarations.push(
|
|
667
|
+
types.variableDeclaration('const', [
|
|
668
|
+
types.variableDeclarator(
|
|
669
|
+
types.arrayPattern([
|
|
670
|
+
types.identifier(vars.pageStateVar),
|
|
671
|
+
types.identifier(vars.setPageStateVar),
|
|
672
|
+
]),
|
|
673
|
+
types.callExpression(types.identifier('useState'), [types.numericLiteral(1)])
|
|
674
|
+
),
|
|
675
|
+
])
|
|
676
|
+
)
|
|
640
677
|
|
|
641
|
-
//
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
[
|
|
649
|
-
types.binaryExpression(
|
|
650
|
-
'/',
|
|
651
|
-
types.memberExpression(types.identifier('data'), types.identifier('count')),
|
|
652
|
-
types.numericLiteral(info.perPage)
|
|
653
|
-
),
|
|
654
|
-
]
|
|
655
|
-
),
|
|
656
|
-
])
|
|
678
|
+
// For components (not pages), add a useEffect to fetch count on mount
|
|
679
|
+
// Pages get count from getStaticProps, but components need to fetch it client-side
|
|
680
|
+
if (!isPage) {
|
|
681
|
+
const fileName = generateSafeFileName(
|
|
682
|
+
usage.resourceDefinition.dataSourceType,
|
|
683
|
+
usage.resourceDefinition.tableName,
|
|
684
|
+
usage.resourceDefinition.dataSourceId
|
|
657
685
|
)
|
|
658
|
-
})
|
|
659
686
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
types.callExpression(
|
|
668
|
-
types.memberExpression(
|
|
687
|
+
effectStatements.push(
|
|
688
|
+
types.expressionStatement(
|
|
689
|
+
types.callExpression(types.identifier('useEffect'), [
|
|
690
|
+
types.arrowFunctionExpression(
|
|
691
|
+
[],
|
|
692
|
+
types.blockStatement([
|
|
693
|
+
types.expressionStatement(
|
|
669
694
|
types.callExpression(
|
|
670
695
|
types.memberExpression(
|
|
671
|
-
types.callExpression(
|
|
672
|
-
types.
|
|
673
|
-
|
|
696
|
+
types.callExpression(
|
|
697
|
+
types.memberExpression(
|
|
698
|
+
types.callExpression(types.identifier('fetch'), [
|
|
699
|
+
types.stringLiteral(`/api/${fileName}-count`),
|
|
700
|
+
]),
|
|
701
|
+
types.identifier('then')
|
|
702
|
+
),
|
|
703
|
+
[
|
|
704
|
+
types.arrowFunctionExpression(
|
|
705
|
+
[types.identifier('res')],
|
|
706
|
+
types.callExpression(
|
|
707
|
+
types.memberExpression(
|
|
708
|
+
types.identifier('res'),
|
|
709
|
+
types.identifier('json')
|
|
710
|
+
),
|
|
711
|
+
[]
|
|
712
|
+
)
|
|
713
|
+
),
|
|
714
|
+
]
|
|
715
|
+
),
|
|
674
716
|
types.identifier('then')
|
|
675
717
|
),
|
|
676
718
|
[
|
|
677
719
|
types.arrowFunctionExpression(
|
|
678
|
-
[types.identifier('
|
|
679
|
-
types.
|
|
680
|
-
types.
|
|
681
|
-
types.
|
|
682
|
-
|
|
720
|
+
[types.identifier('data')],
|
|
721
|
+
types.blockStatement([
|
|
722
|
+
types.ifStatement(
|
|
723
|
+
types.logicalExpression(
|
|
724
|
+
'&&',
|
|
725
|
+
types.identifier('data'),
|
|
726
|
+
types.binaryExpression(
|
|
727
|
+
'in',
|
|
728
|
+
types.stringLiteral('count'),
|
|
729
|
+
types.identifier('data')
|
|
730
|
+
)
|
|
731
|
+
),
|
|
732
|
+
types.blockStatement([
|
|
733
|
+
types.expressionStatement(
|
|
734
|
+
types.callExpression(
|
|
735
|
+
types.identifier(vars.setMaxPagesStateVar),
|
|
736
|
+
[
|
|
737
|
+
types.conditionalExpression(
|
|
738
|
+
types.binaryExpression(
|
|
739
|
+
'===',
|
|
740
|
+
types.memberExpression(
|
|
741
|
+
types.identifier('data'),
|
|
742
|
+
types.identifier('count')
|
|
743
|
+
),
|
|
744
|
+
types.numericLiteral(0)
|
|
745
|
+
),
|
|
746
|
+
types.numericLiteral(0),
|
|
747
|
+
types.callExpression(
|
|
748
|
+
types.memberExpression(
|
|
749
|
+
types.identifier('Math'),
|
|
750
|
+
types.identifier('ceil')
|
|
751
|
+
),
|
|
752
|
+
[
|
|
753
|
+
types.binaryExpression(
|
|
754
|
+
'/',
|
|
755
|
+
types.memberExpression(
|
|
756
|
+
types.identifier('data'),
|
|
757
|
+
types.identifier('count')
|
|
758
|
+
),
|
|
759
|
+
types.numericLiteral(usage.perPage)
|
|
760
|
+
),
|
|
761
|
+
]
|
|
762
|
+
)
|
|
763
|
+
),
|
|
764
|
+
]
|
|
765
|
+
)
|
|
766
|
+
),
|
|
767
|
+
])
|
|
683
768
|
),
|
|
684
|
-
|
|
685
|
-
)
|
|
769
|
+
])
|
|
686
770
|
),
|
|
687
771
|
]
|
|
688
|
-
)
|
|
689
|
-
types.identifier('then')
|
|
772
|
+
)
|
|
690
773
|
),
|
|
691
|
-
|
|
692
|
-
types.arrowFunctionExpression(
|
|
693
|
-
[types.identifier('data')],
|
|
694
|
-
types.blockStatement([
|
|
695
|
-
types.ifStatement(
|
|
696
|
-
types.logicalExpression(
|
|
697
|
-
'&&',
|
|
698
|
-
types.identifier('data'),
|
|
699
|
-
types.memberExpression(
|
|
700
|
-
types.identifier('data'),
|
|
701
|
-
types.identifier('count')
|
|
702
|
-
)
|
|
703
|
-
),
|
|
704
|
-
types.blockStatement(setStateStatements)
|
|
705
|
-
),
|
|
706
|
-
])
|
|
707
|
-
),
|
|
708
|
-
]
|
|
709
|
-
)
|
|
774
|
+
])
|
|
710
775
|
),
|
|
776
|
+
types.arrayExpression([]), // Empty dependency array - fetch on mount only
|
|
711
777
|
])
|
|
778
|
+
)
|
|
779
|
+
)
|
|
780
|
+
}
|
|
781
|
+
} else if (usage.category === 'search-only') {
|
|
782
|
+
// Search-only state
|
|
783
|
+
stateDeclarations.push(
|
|
784
|
+
types.variableDeclaration('const', [
|
|
785
|
+
types.variableDeclarator(
|
|
786
|
+
types.identifier(vars.skipDebounceRefVar),
|
|
787
|
+
types.callExpression(types.identifier('useRef'), [types.booleanLiteral(true)])
|
|
712
788
|
),
|
|
713
|
-
types.arrayExpression([]),
|
|
714
789
|
])
|
|
715
790
|
)
|
|
716
791
|
|
|
717
|
-
|
|
718
|
-
|
|
792
|
+
stateDeclarations.push(
|
|
793
|
+
types.variableDeclaration('const', [
|
|
794
|
+
types.variableDeclarator(
|
|
795
|
+
types.arrayPattern([
|
|
796
|
+
types.identifier(vars.debouncedSearchQueryVar),
|
|
797
|
+
types.identifier(vars.setDebouncedSearchQueryVar),
|
|
798
|
+
]),
|
|
799
|
+
types.callExpression(types.identifier('useState'), [types.stringLiteral('')])
|
|
800
|
+
),
|
|
801
|
+
])
|
|
802
|
+
)
|
|
719
803
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
})
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
createAPIRoutesForPaginatedDataSources(
|
|
735
|
-
uidl.node,
|
|
736
|
-
options.dataSources,
|
|
737
|
-
componentChunk,
|
|
738
|
-
options.extractedResources,
|
|
739
|
-
paginationInfos,
|
|
740
|
-
isComponent
|
|
741
|
-
)
|
|
804
|
+
stateDeclarations.push(
|
|
805
|
+
types.variableDeclaration('const', [
|
|
806
|
+
types.variableDeclarator(
|
|
807
|
+
types.arrayPattern([
|
|
808
|
+
types.identifier(vars.searchQueryVar),
|
|
809
|
+
types.identifier(vars.setSearchQueryVar),
|
|
810
|
+
]),
|
|
811
|
+
types.callExpression(types.identifier('useState'), [types.stringLiteral('')])
|
|
812
|
+
),
|
|
813
|
+
])
|
|
814
|
+
)
|
|
742
815
|
|
|
743
|
-
|
|
744
|
-
|
|
816
|
+
// Debounce effect
|
|
817
|
+
effectStatements.push(
|
|
818
|
+
types.expressionStatement(
|
|
819
|
+
types.callExpression(types.identifier('useEffect'), [
|
|
820
|
+
types.arrowFunctionExpression(
|
|
821
|
+
[],
|
|
822
|
+
types.blockStatement([
|
|
823
|
+
types.ifStatement(
|
|
824
|
+
types.memberExpression(
|
|
825
|
+
types.identifier(vars.skipDebounceRefVar),
|
|
826
|
+
types.identifier('current')
|
|
827
|
+
),
|
|
828
|
+
types.blockStatement([
|
|
829
|
+
types.expressionStatement(
|
|
830
|
+
types.assignmentExpression(
|
|
831
|
+
'=',
|
|
832
|
+
types.memberExpression(
|
|
833
|
+
types.identifier(vars.skipDebounceRefVar),
|
|
834
|
+
types.identifier('current')
|
|
835
|
+
),
|
|
836
|
+
types.booleanLiteral(false)
|
|
837
|
+
)
|
|
838
|
+
),
|
|
839
|
+
types.returnStatement(),
|
|
840
|
+
])
|
|
841
|
+
),
|
|
842
|
+
types.variableDeclaration('const', [
|
|
843
|
+
types.variableDeclarator(
|
|
844
|
+
types.identifier('timer'),
|
|
845
|
+
types.callExpression(types.identifier('setTimeout'), [
|
|
846
|
+
types.arrowFunctionExpression(
|
|
847
|
+
[],
|
|
848
|
+
types.blockStatement([
|
|
849
|
+
types.expressionStatement(
|
|
850
|
+
types.callExpression(
|
|
851
|
+
types.identifier(vars.setDebouncedSearchQueryVar),
|
|
852
|
+
[types.identifier(vars.searchQueryVar)]
|
|
853
|
+
)
|
|
854
|
+
),
|
|
855
|
+
])
|
|
856
|
+
),
|
|
857
|
+
types.numericLiteral(usage.searchDebounce),
|
|
858
|
+
])
|
|
859
|
+
),
|
|
860
|
+
]),
|
|
861
|
+
types.returnStatement(
|
|
862
|
+
types.arrowFunctionExpression(
|
|
863
|
+
[],
|
|
864
|
+
types.callExpression(types.identifier('clearTimeout'), [
|
|
865
|
+
types.identifier('timer'),
|
|
866
|
+
])
|
|
867
|
+
)
|
|
868
|
+
),
|
|
869
|
+
])
|
|
870
|
+
),
|
|
871
|
+
types.arrayExpression([types.identifier(vars.searchQueryVar)]),
|
|
872
|
+
])
|
|
873
|
+
)
|
|
874
|
+
)
|
|
875
|
+
}
|
|
745
876
|
})
|
|
746
877
|
|
|
747
|
-
|
|
748
|
-
|
|
878
|
+
// Insert state declarations at the beginning
|
|
879
|
+
stateDeclarations.reverse().forEach((s) => blockStatement.body.unshift(s))
|
|
749
880
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
881
|
+
// Insert effects before return statement
|
|
882
|
+
const returnIndex = blockStatement.body.findIndex((s: any) => s.type === 'ReturnStatement')
|
|
883
|
+
const insertIndex = returnIndex !== -1 ? returnIndex : blockStatement.body.length
|
|
884
|
+
effectStatements.reverse().forEach((e) => blockStatement.body.splice(insertIndex, 0, e))
|
|
753
885
|
|
|
754
|
-
//
|
|
755
|
-
|
|
756
|
-
const searchOnlyDataSources = detectedSearchOnly
|
|
886
|
+
// STEP 3: Find all DataProviders in JSX that have Repeaters and wire them to correct states
|
|
887
|
+
const dataProviders = findAllDataProvidersInJSX(blockStatement)
|
|
757
888
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
queryColumnsMap,
|
|
764
|
-
dependencies
|
|
765
|
-
)
|
|
889
|
+
// Filter to only DataProviders with Repeaters (array mappers)
|
|
890
|
+
const dataProvidersWithRepeaters = dataProviders.filter((dp) => {
|
|
891
|
+
const hasRepeater = findArrayMapperRenderPropInDataProvider(dp) !== undefined
|
|
892
|
+
return hasRepeater
|
|
893
|
+
})
|
|
766
894
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
895
|
+
// Track which usage index we're on for each dataSourceIdentifier
|
|
896
|
+
// We use pure order-based matching - the order of DataProviders in JSX should match UIDL order
|
|
897
|
+
const usageIndexByDataSourceId = new Map<string, number>()
|
|
898
|
+
|
|
899
|
+
dataProvidersWithRepeaters.forEach((dp) => {
|
|
900
|
+
const nameAttr = dp.openingElement.attributes.find(
|
|
901
|
+
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'name'
|
|
774
902
|
)
|
|
775
|
-
|
|
903
|
+
if (!nameAttr?.value?.expression?.value) {
|
|
904
|
+
return
|
|
905
|
+
}
|
|
776
906
|
|
|
777
|
-
|
|
778
|
-
}
|
|
907
|
+
const dataSourceIdentifier = nameAttr.value.expression.value
|
|
779
908
|
|
|
780
|
-
|
|
781
|
-
|
|
909
|
+
// Use pure order-based matching within each dataSourceIdentifier
|
|
910
|
+
const usages = registry.byDataSourceId.get(dataSourceIdentifier) || []
|
|
911
|
+
const currentIndex = usageIndexByDataSourceId.get(dataSourceIdentifier) || 0
|
|
782
912
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
}
|
|
913
|
+
if (currentIndex >= usages.length) {
|
|
914
|
+
return
|
|
915
|
+
}
|
|
787
916
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
}
|
|
917
|
+
const usage = usages[currentIndex]
|
|
918
|
+
usageIndexByDataSourceId.set(dataSourceIdentifier, currentIndex + 1)
|
|
791
919
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
if (
|
|
805
|
-
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
} else if (root.type === 'BlockStatement') {
|
|
809
|
-
if (root.body && Array.isArray(root.body)) {
|
|
810
|
-
for (const stmt of root.body) {
|
|
811
|
-
const found = findParentNode(stmt, target, root)
|
|
812
|
-
if (found !== null) {
|
|
813
|
-
return found
|
|
814
|
-
}
|
|
920
|
+
const vars = getStateVarsForUsage(usage)
|
|
921
|
+
const fileName = generateSafeFileName(
|
|
922
|
+
usage.resourceDefinition.dataSourceType,
|
|
923
|
+
usage.resourceDefinition.tableName,
|
|
924
|
+
usage.resourceDefinition.dataSourceId
|
|
925
|
+
)
|
|
926
|
+
|
|
927
|
+
// Update DataProvider based on category
|
|
928
|
+
if (usage.category === 'paginated+search') {
|
|
929
|
+
updateDataProviderForPaginatedSearch(dp, usage, vars, fileName)
|
|
930
|
+
} else if (usage.category === 'paginated-only') {
|
|
931
|
+
updateDataProviderForPaginationOnly(dp, usage, vars, fileName)
|
|
932
|
+
} else if (usage.category === 'search-only') {
|
|
933
|
+
updateDataProviderForSearchOnly(dp, usage, vars, fileName)
|
|
934
|
+
} else {
|
|
935
|
+
updateDataProviderForPlain(dp, vars)
|
|
815
936
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
if (found !== null) {
|
|
821
|
-
return found
|
|
937
|
+
|
|
938
|
+
// Create API route if needed
|
|
939
|
+
if (usage.category !== 'plain') {
|
|
940
|
+
ensureAPIRouteExists(options.extractedResources, usage)
|
|
822
941
|
}
|
|
823
|
-
}
|
|
824
|
-
} else if (root.type === 'ConditionalExpression') {
|
|
825
|
-
const foundConsequent = findParentNode(root.consequent, target, root)
|
|
826
|
-
if (foundConsequent !== null) {
|
|
827
|
-
return foundConsequent
|
|
828
|
-
}
|
|
829
|
-
const foundAlternate = findParentNode(root.alternate, target, root)
|
|
830
|
-
if (foundAlternate !== null) {
|
|
831
|
-
return foundAlternate
|
|
832
|
-
}
|
|
833
|
-
}
|
|
942
|
+
})
|
|
834
943
|
|
|
835
|
-
|
|
836
|
-
|
|
944
|
+
// STEP 3.5: Handle DataProviders WITHOUT repeaters (data-source-item type)
|
|
945
|
+
// These access single items like data[0].name and should not re-render on state changes
|
|
946
|
+
// We wrap their params in useMemo to prevent reference changes from triggering re-renders
|
|
947
|
+
const dataProvidersWithoutRepeaters = dataProviders.filter((dp) => {
|
|
948
|
+
const hasRepeater = findArrayMapperRenderPropInDataProvider(dp) !== undefined
|
|
949
|
+
return !hasRepeater
|
|
950
|
+
})
|
|
837
951
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
): {
|
|
842
|
-
paginatedMappers: DetectedPagination[]
|
|
843
|
-
searchOnlyMappers: DetectedPagination[]
|
|
844
|
-
paginationOnlyMappers: DetectedPagination[]
|
|
845
|
-
plainMappers: DetectedPagination[]
|
|
846
|
-
} {
|
|
847
|
-
interface DataProviderInfo {
|
|
848
|
-
identifier: string
|
|
849
|
-
dataProvider: any
|
|
850
|
-
arrayMapperRenderProp?: string
|
|
851
|
-
paginationNode?: { class: string; prevClass: string | null; nextClass: string | null }
|
|
852
|
-
searchInput?: { class: string | null; jsx: any }
|
|
853
|
-
hasPagination: boolean
|
|
854
|
-
hasSearch: boolean
|
|
855
|
-
}
|
|
952
|
+
dataProvidersWithoutRepeaters.forEach((dp) => {
|
|
953
|
+
stabilizeDataProviderWithoutRepeater(dp)
|
|
954
|
+
})
|
|
856
955
|
|
|
857
|
-
|
|
956
|
+
// STEP 4: Wire search inputs
|
|
957
|
+
// Match search inputs to usages by order (within each dataSourceIdentifier that has search enabled)
|
|
958
|
+
const searchInputs = findAllSearchInputsInJSX(blockStatement)
|
|
858
959
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
dataSourceIdentifier: string
|
|
862
|
-
renderProp: string
|
|
863
|
-
paginated: boolean
|
|
864
|
-
searchEnabled: boolean
|
|
865
|
-
}
|
|
866
|
-
const arrayMapperInfoMap = new Map<string, ArrayMapperInfo>()
|
|
960
|
+
// Get all search-enabled usages in order
|
|
961
|
+
const searchEnabledUsages = registry.usages.filter((u) => u.searchEnabled)
|
|
867
962
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
if (
|
|
963
|
+
// Match by order - search input 0 -> searchEnabledUsages[0], etc.
|
|
964
|
+
searchInputs.forEach((input, idx) => {
|
|
965
|
+
if (idx >= searchEnabledUsages.length) {
|
|
871
966
|
return
|
|
872
967
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
if (node.content?.children) {
|
|
891
|
-
node.content.children.forEach((child: any) => collectArrayMapperInfo(child))
|
|
892
|
-
}
|
|
893
|
-
if (node.children && Array.isArray(node.children)) {
|
|
894
|
-
node.children.forEach((child: any) => collectArrayMapperInfo(child))
|
|
968
|
+
|
|
969
|
+
const usage = searchEnabledUsages[idx]
|
|
970
|
+
const vars = getStateVarsForUsage(usage)
|
|
971
|
+
wireSearchInput(input.node, vars)
|
|
972
|
+
})
|
|
973
|
+
|
|
974
|
+
// STEP 5: Wire pagination buttons
|
|
975
|
+
// Match pagination nodes to usages by order (within paginated usages)
|
|
976
|
+
const paginationNodes = findAllPaginationNodesInJSX(blockStatement)
|
|
977
|
+
|
|
978
|
+
// Get all paginated usages in order
|
|
979
|
+
const paginatedUsages = registry.usages.filter((u) => u.paginated)
|
|
980
|
+
|
|
981
|
+
// Match by order - pagination node 0 -> paginatedUsages[0], etc.
|
|
982
|
+
paginationNodes.forEach((paginationNode, idx) => {
|
|
983
|
+
if (idx >= paginatedUsages.length) {
|
|
984
|
+
return
|
|
895
985
|
}
|
|
986
|
+
|
|
987
|
+
const usage = paginatedUsages[idx]
|
|
988
|
+
const vars = getStateVarsForUsage(usage)
|
|
989
|
+
wirePaginationButtons(paginationNode.node, usage, vars)
|
|
990
|
+
})
|
|
991
|
+
|
|
992
|
+
// STEP 6: Update getStaticProps if this is a page
|
|
993
|
+
if (isPage) {
|
|
994
|
+
updateGetStaticProps(chunks, registry)
|
|
896
995
|
}
|
|
897
|
-
collectArrayMapperInfo(uidlNode)
|
|
898
|
-
}
|
|
899
996
|
|
|
900
|
-
|
|
901
|
-
interface DataProviderWithParent {
|
|
902
|
-
dataProvider: any
|
|
903
|
-
parent: any
|
|
997
|
+
return structure
|
|
904
998
|
}
|
|
905
|
-
const dataProvidersWithParents: DataProviderWithParent[] = []
|
|
906
999
|
|
|
907
|
-
|
|
1000
|
+
return paginationPlugin
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// ==================== HELPER FUNCTIONS ====================
|
|
1004
|
+
|
|
1005
|
+
function findAllDataProvidersInJSX(blockStatement: types.BlockStatement): any[] {
|
|
1006
|
+
const results: any[] = []
|
|
1007
|
+
|
|
1008
|
+
const traverse = (node: any): void => {
|
|
908
1009
|
if (!node) {
|
|
909
1010
|
return
|
|
910
1011
|
}
|
|
911
1012
|
|
|
912
|
-
// Check if this is a DataProvider
|
|
913
1013
|
if (node.type === 'JSXElement' && node.openingElement?.name?.name === 'DataProvider') {
|
|
914
|
-
|
|
915
|
-
dataProvider: node,
|
|
916
|
-
parent,
|
|
917
|
-
})
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
// Only recurse through JSX structure, not into embedded expressions or code
|
|
921
|
-
if (node.type === 'ReturnStatement' && node.argument) {
|
|
922
|
-
findDataProvidersAndParents(node.argument, parent)
|
|
923
|
-
} else if (node.type === 'JSXElement' || node.type === 'JSXFragment') {
|
|
924
|
-
// Recurse through JSX children
|
|
925
|
-
if (node.children && Array.isArray(node.children)) {
|
|
926
|
-
node.children.forEach((child: any) => findDataProvidersAndParents(child, node))
|
|
927
|
-
}
|
|
928
|
-
} else if (node.type === 'JSXExpressionContainer') {
|
|
929
|
-
// For expression containers, continue but don't go into the expression itself
|
|
930
|
-
if (
|
|
931
|
-
node.expression &&
|
|
932
|
-
(node.expression.type === 'JSXElement' || node.expression.type === 'JSXFragment')
|
|
933
|
-
) {
|
|
934
|
-
findDataProvidersAndParents(node.expression, parent)
|
|
935
|
-
}
|
|
936
|
-
} else if (node.type === 'BlockStatement') {
|
|
937
|
-
// For block statements (function bodies), look through body array
|
|
938
|
-
if (node.body && Array.isArray(node.body)) {
|
|
939
|
-
node.body.forEach((stmt: any) => findDataProvidersAndParents(stmt, node))
|
|
940
|
-
}
|
|
941
|
-
} else if (node.type === 'ConditionalExpression') {
|
|
942
|
-
// Check both branches of conditional
|
|
943
|
-
findDataProvidersAndParents(node.consequent, parent)
|
|
944
|
-
findDataProvidersAndParents(node.alternate, parent)
|
|
1014
|
+
results.push(node)
|
|
945
1015
|
}
|
|
946
|
-
}
|
|
947
1016
|
|
|
948
|
-
|
|
1017
|
+
if (node.children && Array.isArray(node.children)) {
|
|
1018
|
+
node.children.forEach((c: any) => traverse(c))
|
|
1019
|
+
}
|
|
949
1020
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1021
|
+
if (node.body) {
|
|
1022
|
+
if (Array.isArray(node.body)) {
|
|
1023
|
+
node.body.forEach((s: any) => traverse(s))
|
|
1024
|
+
} else {
|
|
1025
|
+
traverse(node.body)
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
955
1028
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
)
|
|
1029
|
+
if (node.consequent) {
|
|
1030
|
+
traverse(node.consequent)
|
|
1031
|
+
}
|
|
960
1032
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
if (renderFunc.type === 'ArrowFunctionExpression') {
|
|
965
|
-
// Look for Repeater inside the render function
|
|
966
|
-
const findRepeater = (node: any): any => {
|
|
967
|
-
if (!node) {
|
|
968
|
-
return null
|
|
969
|
-
}
|
|
970
|
-
if (node.type === 'JSXElement' && node.openingElement?.name?.name === 'Repeater') {
|
|
971
|
-
return node
|
|
972
|
-
}
|
|
973
|
-
if (node.body) {
|
|
974
|
-
return findRepeater(node.body)
|
|
975
|
-
}
|
|
976
|
-
if (node.children && Array.isArray(node.children)) {
|
|
977
|
-
for (const child of node.children) {
|
|
978
|
-
const result = findRepeater(child)
|
|
979
|
-
if (result) {
|
|
980
|
-
return result
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
if (node.type === 'JSXFragment' || node.type === 'JSXElement') {
|
|
985
|
-
if (node.children && Array.isArray(node.children)) {
|
|
986
|
-
for (const child of node.children) {
|
|
987
|
-
const result = findRepeater(child)
|
|
988
|
-
if (result) {
|
|
989
|
-
return result
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
if (node.type === 'JSXExpressionContainer') {
|
|
995
|
-
return findRepeater(node.expression)
|
|
996
|
-
}
|
|
997
|
-
if (node.expression) {
|
|
998
|
-
return findRepeater(node.expression)
|
|
999
|
-
}
|
|
1000
|
-
if (node.consequent) {
|
|
1001
|
-
const result = findRepeater(node.consequent)
|
|
1002
|
-
if (result) {
|
|
1003
|
-
return result
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
if (node.alternate) {
|
|
1007
|
-
return findRepeater(node.alternate)
|
|
1008
|
-
}
|
|
1009
|
-
return null
|
|
1010
|
-
}
|
|
1033
|
+
if (node.alternate) {
|
|
1034
|
+
traverse(node.alternate)
|
|
1035
|
+
}
|
|
1011
1036
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
// Find renderItem attribute on Repeater
|
|
1015
|
-
const renderItemAttr = repeater.openingElement.attributes.find(
|
|
1016
|
-
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'renderItem'
|
|
1017
|
-
)
|
|
1018
|
-
if (renderItemAttr && renderItemAttr.value?.type === 'JSXExpressionContainer') {
|
|
1019
|
-
const renderItemFunc = renderItemAttr.value.expression
|
|
1020
|
-
if (
|
|
1021
|
-
renderItemFunc.type === 'ArrowFunctionExpression' &&
|
|
1022
|
-
renderItemFunc.params &&
|
|
1023
|
-
renderItemFunc.params.length > 0
|
|
1024
|
-
) {
|
|
1025
|
-
const param = renderItemFunc.params[0]
|
|
1026
|
-
if (param.type === 'Identifier') {
|
|
1027
|
-
arrayMapperRenderProp = param.name
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1037
|
+
if (node.expression) {
|
|
1038
|
+
traverse(node.expression)
|
|
1033
1039
|
}
|
|
1034
1040
|
|
|
1035
|
-
if (
|
|
1036
|
-
|
|
1037
|
-
!nameAttr.value ||
|
|
1038
|
-
nameAttr.value.type !== 'JSXExpressionContainer' ||
|
|
1039
|
-
!arrayMapperRenderProp
|
|
1040
|
-
) {
|
|
1041
|
-
return
|
|
1041
|
+
if (node.argument) {
|
|
1042
|
+
traverse(node.argument)
|
|
1042
1043
|
}
|
|
1043
1044
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
nextClass: string | null
|
|
1049
|
-
} | null = null
|
|
1050
|
-
let searchInputInfo: { class: string | null; jsx: any } | null = null
|
|
1045
|
+
if (node.arguments) {
|
|
1046
|
+
node.arguments.forEach((a: any) => traverse(a))
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1051
1049
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
}
|
|
1050
|
+
traverse(blockStatement)
|
|
1051
|
+
return results
|
|
1052
|
+
}
|
|
1056
1053
|
|
|
1057
|
-
|
|
1054
|
+
function findArrayMapperRenderPropInDataProvider(dataProvider: any): string | undefined {
|
|
1055
|
+
const renderSuccessAttr = dataProvider.openingElement.attributes.find(
|
|
1056
|
+
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'renderSuccess'
|
|
1057
|
+
)
|
|
1058
1058
|
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
}
|
|
1059
|
+
if (!renderSuccessAttr?.value?.expression) {
|
|
1060
|
+
return undefined
|
|
1061
|
+
}
|
|
1063
1062
|
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
// Found pagination node
|
|
1069
|
-
if (childClassName && childClassName.includes('cms-pagination-node')) {
|
|
1070
|
-
const prevClass = findChildWithClass(child, 'previous')
|
|
1071
|
-
const nextClass = findChildWithClass(child, 'next')
|
|
1072
|
-
if (prevClass || nextClass) {
|
|
1073
|
-
paginationNodeInfo = {
|
|
1074
|
-
class: childClassName,
|
|
1075
|
-
prevClass,
|
|
1076
|
-
nextClass,
|
|
1077
|
-
}
|
|
1078
|
-
foundSomething = true
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
// Found search container - search for input inside it
|
|
1083
|
-
if (childClassName && childClassName.includes('data-source-search-node')) {
|
|
1084
|
-
if (child.children && Array.isArray(child.children)) {
|
|
1085
|
-
for (const searchChild of child.children) {
|
|
1086
|
-
if (searchChild.type === 'JSXElement') {
|
|
1087
|
-
const searchChildElementName = searchChild.openingElement?.name?.name
|
|
1088
|
-
const searchChildClassName = getClassName(
|
|
1089
|
-
searchChild.openingElement?.attributes || []
|
|
1090
|
-
)
|
|
1091
|
-
if (
|
|
1092
|
-
searchChildClassName &&
|
|
1093
|
-
searchChildClassName.includes('search-input') &&
|
|
1094
|
-
searchChildElementName === 'input'
|
|
1095
|
-
) {
|
|
1096
|
-
searchInputInfo = {
|
|
1097
|
-
class: searchChildClassName,
|
|
1098
|
-
jsx: searchChild,
|
|
1099
|
-
}
|
|
1100
|
-
foundSomething = true
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
// Also check if search input is a direct child
|
|
1108
|
-
if (
|
|
1109
|
-
childClassName &&
|
|
1110
|
-
childClassName.includes('search-input') &&
|
|
1111
|
-
childElementName === 'input'
|
|
1112
|
-
) {
|
|
1113
|
-
searchInputInfo = {
|
|
1114
|
-
class: childClassName,
|
|
1115
|
-
jsx: child,
|
|
1116
|
-
}
|
|
1117
|
-
foundSomething = true
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
// Stop searching if we found both or if we found what we're looking for
|
|
1121
|
-
if (foundSomething && (searchInputInfo || paginationNodeInfo)) {
|
|
1122
|
-
return true
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
// Only recurse if we haven't found what we're looking for yet
|
|
1126
|
-
if (!searchInputInfo || !paginationNodeInfo) {
|
|
1127
|
-
if (findSearchAndPaginationInScope(child, skipNode)) {
|
|
1128
|
-
return true
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1063
|
+
const findRepeater = (node: any): any => {
|
|
1064
|
+
if (!node) {
|
|
1065
|
+
return null
|
|
1066
|
+
}
|
|
1133
1067
|
|
|
1134
|
-
|
|
1068
|
+
if (node.type === 'JSXElement' && node.openingElement?.name?.name === 'Repeater') {
|
|
1069
|
+
return node
|
|
1135
1070
|
}
|
|
1136
1071
|
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1072
|
+
if (node.body) {
|
|
1073
|
+
return findRepeater(node.body)
|
|
1074
|
+
}
|
|
1140
1075
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1076
|
+
if (node.children && Array.isArray(node.children)) {
|
|
1077
|
+
for (const c of node.children) {
|
|
1078
|
+
const r = findRepeater(c)
|
|
1079
|
+
if (r) {
|
|
1080
|
+
return r
|
|
1081
|
+
}
|
|
1146
1082
|
}
|
|
1147
|
-
currentScope = findParentNode(blockStatement, currentScope)
|
|
1148
|
-
depth++
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
// Record the DataProvider with its pagination/search info
|
|
1152
|
-
// Use array instead of Map to handle multiple DataProviders with same name
|
|
1153
|
-
dataProviderList.push({
|
|
1154
|
-
identifier: dataProviderIdentifier,
|
|
1155
|
-
dataProvider,
|
|
1156
|
-
arrayMapperRenderProp,
|
|
1157
|
-
paginationNode: paginationNodeInfo || undefined,
|
|
1158
|
-
searchInput: searchInputInfo || undefined,
|
|
1159
|
-
hasPagination: !!paginationNodeInfo,
|
|
1160
|
-
hasSearch: !!searchInputInfo,
|
|
1161
|
-
})
|
|
1162
|
-
})
|
|
1163
|
-
|
|
1164
|
-
// Categorize data providers
|
|
1165
|
-
const paginatedMappers: DetectedPagination[] = []
|
|
1166
|
-
const searchOnlyMappers: DetectedPagination[] = []
|
|
1167
|
-
const paginationOnlyMappers: DetectedPagination[] = []
|
|
1168
|
-
const plainMappers: DetectedPagination[] = []
|
|
1169
|
-
|
|
1170
|
-
dataProviderList.forEach((info) => {
|
|
1171
|
-
// Check UIDL flags for this array mapper
|
|
1172
|
-
const uidlInfo = info.arrayMapperRenderProp
|
|
1173
|
-
? arrayMapperInfoMap.get(info.arrayMapperRenderProp)
|
|
1174
|
-
: null
|
|
1175
|
-
|
|
1176
|
-
// Only process pagination/search if UIDL explicitly enables it
|
|
1177
|
-
const shouldHavePagination = uidlInfo?.paginated && info.hasPagination
|
|
1178
|
-
const shouldHaveSearch = uidlInfo?.searchEnabled && info.hasSearch
|
|
1179
|
-
|
|
1180
|
-
if (shouldHavePagination && shouldHaveSearch) {
|
|
1181
|
-
// Pagination + Search
|
|
1182
|
-
paginatedMappers.push({
|
|
1183
|
-
paginationNodeClass: info.paginationNode!.class,
|
|
1184
|
-
prevButtonClass: info.paginationNode!.prevClass,
|
|
1185
|
-
nextButtonClass: info.paginationNode!.nextClass,
|
|
1186
|
-
dataSourceIdentifier: info.identifier,
|
|
1187
|
-
dataProviderJSX: info.dataProvider,
|
|
1188
|
-
arrayMapperRenderProp: info.arrayMapperRenderProp,
|
|
1189
|
-
searchInputClass: info.searchInput?.class,
|
|
1190
|
-
searchInputJSX: info.searchInput?.jsx,
|
|
1191
|
-
})
|
|
1192
|
-
} else if (shouldHavePagination && !shouldHaveSearch) {
|
|
1193
|
-
// Pagination only
|
|
1194
|
-
paginationOnlyMappers.push({
|
|
1195
|
-
paginationNodeClass: info.paginationNode!.class,
|
|
1196
|
-
prevButtonClass: info.paginationNode!.prevClass,
|
|
1197
|
-
nextButtonClass: info.paginationNode!.nextClass,
|
|
1198
|
-
dataSourceIdentifier: info.identifier,
|
|
1199
|
-
dataProviderJSX: info.dataProvider,
|
|
1200
|
-
arrayMapperRenderProp: info.arrayMapperRenderProp,
|
|
1201
|
-
searchInputClass: undefined,
|
|
1202
|
-
searchInputJSX: undefined,
|
|
1203
|
-
})
|
|
1204
|
-
} else if (!shouldHavePagination && shouldHaveSearch) {
|
|
1205
|
-
// Search only
|
|
1206
|
-
searchOnlyMappers.push({
|
|
1207
|
-
paginationNodeClass: '',
|
|
1208
|
-
prevButtonClass: null,
|
|
1209
|
-
nextButtonClass: null,
|
|
1210
|
-
dataSourceIdentifier: info.identifier,
|
|
1211
|
-
dataProviderJSX: info.dataProvider,
|
|
1212
|
-
arrayMapperRenderProp: info.arrayMapperRenderProp,
|
|
1213
|
-
searchInputClass: info.searchInput?.class,
|
|
1214
|
-
searchInputJSX: info.searchInput?.jsx,
|
|
1215
|
-
})
|
|
1216
|
-
} else {
|
|
1217
|
-
// Plain (no pagination, no search) - UIDL doesn't enable it or no controls found
|
|
1218
|
-
plainMappers.push({
|
|
1219
|
-
paginationNodeClass: '',
|
|
1220
|
-
prevButtonClass: null,
|
|
1221
|
-
nextButtonClass: null,
|
|
1222
|
-
dataSourceIdentifier: info.identifier,
|
|
1223
|
-
dataProviderJSX: info.dataProvider,
|
|
1224
|
-
arrayMapperRenderProp: info.arrayMapperRenderProp,
|
|
1225
|
-
searchInputClass: undefined,
|
|
1226
|
-
searchInputJSX: undefined,
|
|
1227
|
-
})
|
|
1228
1083
|
}
|
|
1229
|
-
|
|
1084
|
+
return null
|
|
1085
|
+
}
|
|
1230
1086
|
|
|
1231
|
-
|
|
1087
|
+
const repeater = findRepeater(renderSuccessAttr.value.expression)
|
|
1088
|
+
if (!repeater) {
|
|
1089
|
+
return undefined
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
const renderItemAttr = repeater.openingElement.attributes.find(
|
|
1093
|
+
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'renderItem'
|
|
1094
|
+
)
|
|
1095
|
+
|
|
1096
|
+
if (!renderItemAttr?.value?.expression?.params?.[0]?.name) {
|
|
1097
|
+
return undefined
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
return renderItemAttr.value.expression.params[0].name
|
|
1232
1101
|
}
|
|
1233
1102
|
|
|
1234
|
-
function
|
|
1235
|
-
blockStatement: types.BlockStatement
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
queryColumnsMap: Map<string, string[]>,
|
|
1239
|
-
dependencies: any
|
|
1240
|
-
): void {
|
|
1241
|
-
const searchOnlyStates: types.Statement[] = []
|
|
1242
|
-
const searchOnlyEffects: types.Statement[] = []
|
|
1243
|
-
|
|
1244
|
-
searchOnlyDataSources.forEach((detected, index) => {
|
|
1245
|
-
const searchId = `search_${index}`
|
|
1246
|
-
const searchQueryVar = `${searchId}_query`
|
|
1247
|
-
const setSearchQueryVar = `set${
|
|
1248
|
-
searchQueryVar.charAt(0).toUpperCase() + searchQueryVar.slice(1)
|
|
1249
|
-
}`
|
|
1250
|
-
const debouncedSearchQueryVar = `debounced${
|
|
1251
|
-
searchQueryVar.charAt(0).toUpperCase() + searchQueryVar.slice(1)
|
|
1252
|
-
}`
|
|
1253
|
-
const setDebouncedSearchQueryVar = `set${
|
|
1254
|
-
debouncedSearchQueryVar.charAt(0).toUpperCase() + debouncedSearchQueryVar.slice(1)
|
|
1255
|
-
}`
|
|
1256
|
-
const skipDebounceOnMountRefVar = `skipDebounceOnMount${searchId}`
|
|
1257
|
-
|
|
1258
|
-
const searchConfig = searchConfigMap.get(detected.dataSourceIdentifier)
|
|
1259
|
-
const searchDebounce = searchConfig?.searchDebounce || 300
|
|
1260
|
-
const queryColumns = queryColumnsMap.get(detected.dataSourceIdentifier)
|
|
1261
|
-
|
|
1262
|
-
// Add skip ref
|
|
1263
|
-
const skipRefAST = types.variableDeclaration('const', [
|
|
1264
|
-
types.variableDeclarator(
|
|
1265
|
-
types.identifier(skipDebounceOnMountRefVar),
|
|
1266
|
-
types.callExpression(types.identifier('useRef'), [types.booleanLiteral(true)])
|
|
1267
|
-
),
|
|
1268
|
-
])
|
|
1269
|
-
searchOnlyStates.push(skipRefAST)
|
|
1270
|
-
|
|
1271
|
-
// Add debounced search state
|
|
1272
|
-
const debouncedSearchStateAST = types.variableDeclaration('const', [
|
|
1273
|
-
types.variableDeclarator(
|
|
1274
|
-
types.arrayPattern([
|
|
1275
|
-
types.identifier(debouncedSearchQueryVar),
|
|
1276
|
-
types.identifier(setDebouncedSearchQueryVar),
|
|
1277
|
-
]),
|
|
1278
|
-
types.callExpression(types.identifier('useState'), [types.stringLiteral('')])
|
|
1279
|
-
),
|
|
1280
|
-
])
|
|
1281
|
-
searchOnlyStates.push(debouncedSearchStateAST)
|
|
1282
|
-
|
|
1283
|
-
// Add search query state
|
|
1284
|
-
const searchStateAST = types.variableDeclaration('const', [
|
|
1285
|
-
types.variableDeclarator(
|
|
1286
|
-
types.arrayPattern([types.identifier(searchQueryVar), types.identifier(setSearchQueryVar)]),
|
|
1287
|
-
types.callExpression(types.identifier('useState'), [types.stringLiteral('')])
|
|
1288
|
-
),
|
|
1289
|
-
])
|
|
1290
|
-
searchOnlyStates.push(searchStateAST)
|
|
1103
|
+
function findAllSearchInputsInJSX(
|
|
1104
|
+
blockStatement: types.BlockStatement
|
|
1105
|
+
): Array<{ node: any; className: string }> {
|
|
1106
|
+
const results: Array<{ node: any; className: string }> = []
|
|
1291
1107
|
|
|
1292
|
-
|
|
1293
|
-
if (!
|
|
1294
|
-
|
|
1295
|
-
type: 'library',
|
|
1296
|
-
path: 'react',
|
|
1297
|
-
version: '',
|
|
1298
|
-
meta: {
|
|
1299
|
-
namedImport: true,
|
|
1300
|
-
},
|
|
1301
|
-
}
|
|
1108
|
+
const traverse = (node: any): void => {
|
|
1109
|
+
if (!node) {
|
|
1110
|
+
return
|
|
1302
1111
|
}
|
|
1303
1112
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
),
|
|
1314
|
-
types.blockStatement([
|
|
1315
|
-
types.expressionStatement(
|
|
1316
|
-
types.assignmentExpression(
|
|
1317
|
-
'=',
|
|
1318
|
-
types.memberExpression(
|
|
1319
|
-
types.identifier(skipDebounceOnMountRefVar),
|
|
1320
|
-
types.identifier('current')
|
|
1321
|
-
),
|
|
1322
|
-
types.booleanLiteral(false)
|
|
1323
|
-
)
|
|
1324
|
-
),
|
|
1325
|
-
types.returnStatement(),
|
|
1326
|
-
])
|
|
1327
|
-
),
|
|
1328
|
-
types.variableDeclaration('const', [
|
|
1329
|
-
types.variableDeclarator(
|
|
1330
|
-
types.identifier('timer'),
|
|
1331
|
-
types.callExpression(types.identifier('setTimeout'), [
|
|
1332
|
-
types.arrowFunctionExpression(
|
|
1333
|
-
[],
|
|
1334
|
-
types.blockStatement([
|
|
1335
|
-
types.expressionStatement(
|
|
1336
|
-
types.callExpression(types.identifier(setDebouncedSearchQueryVar), [
|
|
1337
|
-
types.identifier(searchQueryVar),
|
|
1338
|
-
])
|
|
1339
|
-
),
|
|
1340
|
-
])
|
|
1341
|
-
),
|
|
1342
|
-
types.numericLiteral(searchDebounce),
|
|
1343
|
-
])
|
|
1344
|
-
),
|
|
1345
|
-
]),
|
|
1346
|
-
types.returnStatement(
|
|
1347
|
-
types.arrowFunctionExpression(
|
|
1348
|
-
[],
|
|
1349
|
-
types.callExpression(types.identifier('clearTimeout'), [types.identifier('timer')])
|
|
1350
|
-
)
|
|
1351
|
-
),
|
|
1352
|
-
])
|
|
1353
|
-
),
|
|
1354
|
-
types.arrayExpression([types.identifier(searchQueryVar)]),
|
|
1355
|
-
])
|
|
1356
|
-
)
|
|
1357
|
-
searchOnlyEffects.push(debounceEffect)
|
|
1113
|
+
if (node.type === 'JSXElement' && node.openingElement?.name?.name === 'input') {
|
|
1114
|
+
const classAttr = node.openingElement.attributes?.find(
|
|
1115
|
+
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'className'
|
|
1116
|
+
)
|
|
1117
|
+
const className = classAttr?.value?.value || classAttr?.value?.expression?.value || ''
|
|
1118
|
+
if (className.includes('search-input')) {
|
|
1119
|
+
results.push({ node, className })
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1358
1122
|
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1123
|
+
if (node.children && Array.isArray(node.children)) {
|
|
1124
|
+
node.children.forEach((c: any) => traverse(c))
|
|
1125
|
+
}
|
|
1126
|
+
if (node.body) {
|
|
1127
|
+
if (Array.isArray(node.body)) {
|
|
1128
|
+
node.body.forEach((s: any) => traverse(s))
|
|
1129
|
+
} else {
|
|
1130
|
+
traverse(node.body)
|
|
1131
|
+
}
|
|
1365
1132
|
}
|
|
1133
|
+
if (node.consequent) {
|
|
1134
|
+
traverse(node.consequent)
|
|
1135
|
+
}
|
|
1136
|
+
if (node.alternate) {
|
|
1137
|
+
traverse(node.alternate)
|
|
1138
|
+
}
|
|
1139
|
+
if (node.expression) {
|
|
1140
|
+
traverse(node.expression)
|
|
1141
|
+
}
|
|
1142
|
+
if (node.argument) {
|
|
1143
|
+
traverse(node.argument)
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
traverse(blockStatement)
|
|
1148
|
+
return results
|
|
1149
|
+
}
|
|
1366
1150
|
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1151
|
+
function findAllPaginationNodesInJSX(
|
|
1152
|
+
blockStatement: types.BlockStatement
|
|
1153
|
+
): Array<{ node: any; className: string }> {
|
|
1154
|
+
const results: Array<{ node: any; className: string }> = []
|
|
1155
|
+
|
|
1156
|
+
const traverse = (node: any): void => {
|
|
1157
|
+
if (!node) {
|
|
1158
|
+
return
|
|
1374
1159
|
}
|
|
1375
|
-
})
|
|
1376
1160
|
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1161
|
+
if (node.type === 'JSXElement') {
|
|
1162
|
+
const classAttr = node.openingElement?.attributes?.find(
|
|
1163
|
+
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'className'
|
|
1164
|
+
)
|
|
1165
|
+
const className = classAttr?.value?.value || classAttr?.value?.expression?.value || ''
|
|
1166
|
+
if (className.includes('cms-pagination-node')) {
|
|
1167
|
+
results.push({ node, className })
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1381
1170
|
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1171
|
+
if (node.children && Array.isArray(node.children)) {
|
|
1172
|
+
node.children.forEach((c: any) => traverse(c))
|
|
1173
|
+
}
|
|
1174
|
+
if (node.body) {
|
|
1175
|
+
if (Array.isArray(node.body)) {
|
|
1176
|
+
node.body.forEach((s: any) => traverse(s))
|
|
1177
|
+
} else {
|
|
1178
|
+
traverse(node.body)
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
if (node.consequent) {
|
|
1182
|
+
traverse(node.consequent)
|
|
1183
|
+
}
|
|
1184
|
+
if (node.alternate) {
|
|
1185
|
+
traverse(node.alternate)
|
|
1186
|
+
}
|
|
1187
|
+
if (node.expression) {
|
|
1188
|
+
traverse(node.expression)
|
|
1189
|
+
}
|
|
1190
|
+
if (node.argument) {
|
|
1191
|
+
traverse(node.argument)
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1387
1194
|
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
blockStatement.body.splice(finalInsertIndex, 0, effect)
|
|
1391
|
-
})
|
|
1195
|
+
traverse(blockStatement)
|
|
1196
|
+
return results
|
|
1392
1197
|
}
|
|
1393
1198
|
|
|
1394
|
-
function
|
|
1395
|
-
|
|
1396
|
-
|
|
1199
|
+
function updateDataProviderForPaginatedSearch(
|
|
1200
|
+
dp: any,
|
|
1201
|
+
usage: DataSourceUsage,
|
|
1202
|
+
vars: ReturnType<typeof getStateVarsForUsage>,
|
|
1203
|
+
fileName: string
|
|
1397
1204
|
): void {
|
|
1398
|
-
|
|
1399
|
-
return
|
|
1400
|
-
}
|
|
1205
|
+
const attrs = dp.openingElement.attributes
|
|
1401
1206
|
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1207
|
+
// Remove existing params, key, initialData, fetchData, persistDataDuringLoading
|
|
1208
|
+
dp.openingElement.attributes = attrs.filter(
|
|
1209
|
+
(attr: any) =>
|
|
1210
|
+
!['params', 'key', 'initialData', 'fetchData', 'persistDataDuringLoading'].includes(
|
|
1211
|
+
attr.name?.name
|
|
1212
|
+
)
|
|
1407
1213
|
)
|
|
1408
1214
|
|
|
1409
|
-
|
|
1410
|
-
|
|
1215
|
+
// Add params with useMemo
|
|
1216
|
+
const paramsProps: types.ObjectProperty[] = [
|
|
1217
|
+
types.objectProperty(
|
|
1218
|
+
types.identifier('page'),
|
|
1219
|
+
types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('page'))
|
|
1220
|
+
),
|
|
1221
|
+
types.objectProperty(types.identifier('perPage'), types.numericLiteral(usage.perPage)),
|
|
1222
|
+
types.objectProperty(
|
|
1223
|
+
types.identifier('query'),
|
|
1224
|
+
types.memberExpression(
|
|
1225
|
+
types.identifier(vars.combinedStateVar),
|
|
1226
|
+
types.identifier('debouncedQuery')
|
|
1227
|
+
)
|
|
1228
|
+
),
|
|
1229
|
+
]
|
|
1230
|
+
if (usage.queryColumns.length > 0) {
|
|
1231
|
+
paramsProps.push(
|
|
1232
|
+
types.objectProperty(
|
|
1233
|
+
types.identifier('queryColumns'),
|
|
1234
|
+
types.callExpression(
|
|
1235
|
+
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
1236
|
+
[types.arrayExpression(usage.queryColumns.map((c) => types.stringLiteral(c)))]
|
|
1237
|
+
)
|
|
1238
|
+
)
|
|
1239
|
+
)
|
|
1240
|
+
}
|
|
1411
1241
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1242
|
+
dp.openingElement.attributes.push(
|
|
1243
|
+
types.jsxAttribute(
|
|
1244
|
+
types.jsxIdentifier('params'),
|
|
1245
|
+
types.jsxExpressionContainer(
|
|
1246
|
+
types.callExpression(types.identifier('useMemo'), [
|
|
1247
|
+
types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
|
|
1248
|
+
types.arrayExpression([types.identifier(vars.combinedStateVar)]),
|
|
1249
|
+
])
|
|
1416
1250
|
)
|
|
1251
|
+
)
|
|
1252
|
+
)
|
|
1417
1253
|
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1254
|
+
// Add initialData
|
|
1255
|
+
const initialDataCondition = types.logicalExpression(
|
|
1256
|
+
'&&',
|
|
1257
|
+
types.binaryExpression(
|
|
1258
|
+
'===',
|
|
1259
|
+
types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('page')),
|
|
1260
|
+
types.numericLiteral(1)
|
|
1261
|
+
),
|
|
1262
|
+
types.unaryExpression(
|
|
1263
|
+
'!',
|
|
1264
|
+
types.memberExpression(
|
|
1265
|
+
types.identifier(vars.combinedStateVar),
|
|
1266
|
+
types.identifier('debouncedQuery')
|
|
1267
|
+
),
|
|
1268
|
+
true
|
|
1269
|
+
)
|
|
1270
|
+
)
|
|
1271
|
+
dp.openingElement.attributes.push(
|
|
1272
|
+
types.jsxAttribute(
|
|
1273
|
+
types.jsxIdentifier('initialData'),
|
|
1274
|
+
types.jsxExpressionContainer(
|
|
1275
|
+
types.conditionalExpression(
|
|
1276
|
+
initialDataCondition,
|
|
1277
|
+
types.optionalMemberExpression(
|
|
1278
|
+
types.identifier('props'),
|
|
1279
|
+
types.identifier(vars.propsPrefix),
|
|
1280
|
+
false,
|
|
1281
|
+
true
|
|
1282
|
+
),
|
|
1283
|
+
types.identifier('undefined')
|
|
1428
1284
|
)
|
|
1429
|
-
|
|
1285
|
+
)
|
|
1286
|
+
)
|
|
1287
|
+
)
|
|
1430
1288
|
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1289
|
+
// Add key
|
|
1290
|
+
dp.openingElement.attributes.push(
|
|
1291
|
+
types.jsxAttribute(
|
|
1292
|
+
types.jsxIdentifier('key'),
|
|
1293
|
+
types.jsxExpressionContainer(
|
|
1294
|
+
types.templateLiteral(
|
|
1295
|
+
[
|
|
1296
|
+
types.templateElement({
|
|
1297
|
+
raw: `${usage.dataSourceIdentifier}-`,
|
|
1298
|
+
cooked: `${usage.dataSourceIdentifier}-`,
|
|
1299
|
+
}),
|
|
1300
|
+
types.templateElement({ raw: '-', cooked: '-' }),
|
|
1301
|
+
types.templateElement({ raw: '', cooked: '' }),
|
|
1302
|
+
],
|
|
1303
|
+
[
|
|
1304
|
+
types.memberExpression(
|
|
1305
|
+
types.identifier(vars.combinedStateVar),
|
|
1306
|
+
types.identifier('page')
|
|
1307
|
+
),
|
|
1308
|
+
types.memberExpression(
|
|
1309
|
+
types.identifier(vars.combinedStateVar),
|
|
1310
|
+
types.identifier('debouncedQuery')
|
|
1311
|
+
),
|
|
1312
|
+
]
|
|
1313
|
+
)
|
|
1314
|
+
)
|
|
1315
|
+
)
|
|
1316
|
+
)
|
|
1436
1317
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1318
|
+
// Add fetchData
|
|
1319
|
+
dp.openingElement.attributes.push(createFetchDataAttribute(fileName))
|
|
1320
|
+
|
|
1321
|
+
// Add persistDataDuringLoading
|
|
1322
|
+
dp.openingElement.attributes.push(
|
|
1323
|
+
types.jsxAttribute(
|
|
1324
|
+
types.jsxIdentifier('persistDataDuringLoading'),
|
|
1325
|
+
types.jsxExpressionContainer(types.booleanLiteral(true))
|
|
1326
|
+
)
|
|
1327
|
+
)
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
function updateDataProviderForPaginationOnly(
|
|
1331
|
+
dp: any,
|
|
1332
|
+
usage: DataSourceUsage,
|
|
1333
|
+
vars: ReturnType<typeof getStateVarsForUsage>,
|
|
1334
|
+
fileName: string
|
|
1335
|
+
): void {
|
|
1336
|
+
const attrs = dp.openingElement.attributes
|
|
1440
1337
|
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1338
|
+
dp.openingElement.attributes = attrs.filter(
|
|
1339
|
+
(attr: any) =>
|
|
1340
|
+
!['params', 'key', 'initialData', 'fetchData', 'persistDataDuringLoading'].includes(
|
|
1341
|
+
attr.name?.name
|
|
1342
|
+
)
|
|
1444
1343
|
)
|
|
1445
1344
|
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1345
|
+
// Add params
|
|
1346
|
+
dp.openingElement.attributes.push(
|
|
1347
|
+
types.jsxAttribute(
|
|
1348
|
+
types.jsxIdentifier('params'),
|
|
1349
|
+
types.jsxExpressionContainer(
|
|
1350
|
+
types.callExpression(types.identifier('useMemo'), [
|
|
1351
|
+
types.arrowFunctionExpression(
|
|
1352
|
+
[],
|
|
1353
|
+
types.objectExpression([
|
|
1354
|
+
types.objectProperty(types.identifier('page'), types.identifier(vars.pageStateVar)),
|
|
1355
|
+
types.objectProperty(
|
|
1356
|
+
types.identifier('perPage'),
|
|
1357
|
+
types.numericLiteral(usage.perPage)
|
|
1358
|
+
),
|
|
1359
|
+
])
|
|
1360
|
+
),
|
|
1361
|
+
types.arrayExpression([types.identifier(vars.pageStateVar)]),
|
|
1362
|
+
])
|
|
1363
|
+
)
|
|
1454
1364
|
)
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
// 3. Update key to include search query
|
|
1458
|
-
const existingKeyAttr = dataProviderJSX.openingElement.attributes.find(
|
|
1459
|
-
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'key'
|
|
1460
1365
|
)
|
|
1461
1366
|
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
types.
|
|
1466
|
-
|
|
1467
|
-
|
|
1367
|
+
// Add initialData
|
|
1368
|
+
dp.openingElement.attributes.push(
|
|
1369
|
+
types.jsxAttribute(
|
|
1370
|
+
types.jsxIdentifier('initialData'),
|
|
1371
|
+
types.jsxExpressionContainer(
|
|
1372
|
+
types.conditionalExpression(
|
|
1373
|
+
types.binaryExpression(
|
|
1374
|
+
'===',
|
|
1375
|
+
types.identifier(vars.pageStateVar),
|
|
1376
|
+
types.numericLiteral(1)
|
|
1377
|
+
),
|
|
1378
|
+
types.optionalMemberExpression(
|
|
1379
|
+
types.identifier('props'),
|
|
1380
|
+
types.identifier(vars.propsPrefix),
|
|
1381
|
+
false,
|
|
1382
|
+
true
|
|
1383
|
+
),
|
|
1384
|
+
types.identifier('undefined')
|
|
1385
|
+
)
|
|
1386
|
+
)
|
|
1387
|
+
)
|
|
1468
1388
|
)
|
|
1469
1389
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
types.
|
|
1390
|
+
// Add key
|
|
1391
|
+
dp.openingElement.attributes.push(
|
|
1392
|
+
types.jsxAttribute(
|
|
1393
|
+
types.jsxIdentifier('key'),
|
|
1394
|
+
types.jsxExpressionContainer(
|
|
1395
|
+
types.templateLiteral(
|
|
1396
|
+
[
|
|
1397
|
+
types.templateElement({
|
|
1398
|
+
raw: `${usage.dataSourceIdentifier}-page-`,
|
|
1399
|
+
cooked: `${usage.dataSourceIdentifier}-page-`,
|
|
1400
|
+
}),
|
|
1401
|
+
types.templateElement({ raw: '', cooked: '' }),
|
|
1402
|
+
],
|
|
1403
|
+
[types.identifier(vars.pageStateVar)]
|
|
1404
|
+
)
|
|
1405
|
+
)
|
|
1406
|
+
)
|
|
1473
1407
|
)
|
|
1474
1408
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1409
|
+
// Add fetchData
|
|
1410
|
+
dp.openingElement.attributes.push(createFetchDataAttribute(fileName))
|
|
1411
|
+
|
|
1412
|
+
dp.openingElement.attributes.push(
|
|
1413
|
+
types.jsxAttribute(
|
|
1414
|
+
types.jsxIdentifier('persistDataDuringLoading'),
|
|
1415
|
+
types.jsxExpressionContainer(types.booleanLiteral(true))
|
|
1416
|
+
)
|
|
1417
|
+
)
|
|
1481
1418
|
}
|
|
1482
1419
|
|
|
1483
|
-
function
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1420
|
+
function updateDataProviderForSearchOnly(
|
|
1421
|
+
dp: any,
|
|
1422
|
+
usage: DataSourceUsage,
|
|
1423
|
+
vars: ReturnType<typeof getStateVarsForUsage>,
|
|
1424
|
+
fileName: string
|
|
1487
1425
|
): void {
|
|
1488
|
-
|
|
1489
|
-
return
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
const combinedStateVar = (info as any).combinedStateVar
|
|
1426
|
+
const attrs = dp.openingElement.attributes
|
|
1493
1427
|
|
|
1494
|
-
|
|
1495
|
-
|
|
1428
|
+
dp.openingElement.attributes = attrs.filter(
|
|
1429
|
+
(attr: any) =>
|
|
1430
|
+
!['params', 'key', 'initialData', 'fetchData', 'persistDataDuringLoading'].includes(
|
|
1431
|
+
attr.name?.name
|
|
1432
|
+
)
|
|
1433
|
+
)
|
|
1496
1434
|
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
),
|
|
1504
|
-
types.objectProperty(types.identifier('perPage'), types.numericLiteral(info.perPage)),
|
|
1435
|
+
// Add params
|
|
1436
|
+
const paramsProps: types.ObjectProperty[] = [
|
|
1437
|
+
types.objectProperty(types.identifier('query'), types.identifier(vars.debouncedSearchQueryVar)),
|
|
1438
|
+
]
|
|
1439
|
+
if (usage.queryColumns.length > 0) {
|
|
1440
|
+
paramsProps.push(
|
|
1505
1441
|
types.objectProperty(
|
|
1506
|
-
types.identifier('
|
|
1507
|
-
types.
|
|
1508
|
-
types.identifier(
|
|
1509
|
-
types.
|
|
1510
|
-
)
|
|
1511
|
-
),
|
|
1512
|
-
]
|
|
1513
|
-
|
|
1514
|
-
if (info.queryColumns && info.queryColumns.length > 0) {
|
|
1515
|
-
paramsProperties.push(
|
|
1516
|
-
types.objectProperty(
|
|
1517
|
-
types.identifier('queryColumns'),
|
|
1518
|
-
types.callExpression(
|
|
1519
|
-
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
1520
|
-
[types.arrayExpression(info.queryColumns.map((col) => types.stringLiteral(col)))]
|
|
1521
|
-
)
|
|
1442
|
+
types.identifier('queryColumns'),
|
|
1443
|
+
types.callExpression(
|
|
1444
|
+
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
1445
|
+
[types.arrayExpression(usage.queryColumns.map((c) => types.stringLiteral(c)))]
|
|
1522
1446
|
)
|
|
1523
1447
|
)
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
// Single dependency: the combined state object
|
|
1527
|
-
dependencies = [types.identifier(combinedStateVar)]
|
|
1528
|
-
} else {
|
|
1529
|
-
// Pagination only (no search)
|
|
1530
|
-
paramsProperties = [
|
|
1531
|
-
types.objectProperty(types.identifier('page'), types.identifier(info.pageStateVar)),
|
|
1532
|
-
types.objectProperty(types.identifier('perPage'), types.numericLiteral(info.perPage)),
|
|
1533
|
-
]
|
|
1534
|
-
|
|
1535
|
-
dependencies = [types.identifier(info.pageStateVar)]
|
|
1448
|
+
)
|
|
1536
1449
|
}
|
|
1537
1450
|
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1451
|
+
dp.openingElement.attributes.push(
|
|
1452
|
+
types.jsxAttribute(
|
|
1453
|
+
types.jsxIdentifier('params'),
|
|
1454
|
+
types.jsxExpressionContainer(
|
|
1455
|
+
types.callExpression(types.identifier('useMemo'), [
|
|
1456
|
+
types.arrowFunctionExpression([], types.objectExpression(paramsProps)),
|
|
1457
|
+
types.arrayExpression([types.identifier(vars.debouncedSearchQueryVar)]),
|
|
1458
|
+
])
|
|
1459
|
+
)
|
|
1460
|
+
)
|
|
1461
|
+
)
|
|
1543
1462
|
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
types.
|
|
1463
|
+
// Add initialData
|
|
1464
|
+
dp.openingElement.attributes.push(
|
|
1465
|
+
types.jsxAttribute(
|
|
1466
|
+
types.jsxIdentifier('initialData'),
|
|
1467
|
+
types.jsxExpressionContainer(
|
|
1468
|
+
types.conditionalExpression(
|
|
1469
|
+
types.unaryExpression('!', types.identifier(vars.debouncedSearchQueryVar), true),
|
|
1470
|
+
types.optionalMemberExpression(
|
|
1471
|
+
types.identifier('props'),
|
|
1472
|
+
types.identifier(vars.propsPrefix),
|
|
1473
|
+
false,
|
|
1474
|
+
true
|
|
1475
|
+
),
|
|
1476
|
+
types.identifier('undefined')
|
|
1477
|
+
)
|
|
1478
|
+
)
|
|
1479
|
+
)
|
|
1547
1480
|
)
|
|
1548
1481
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1482
|
+
// Add key
|
|
1483
|
+
dp.openingElement.attributes.push(
|
|
1484
|
+
types.jsxAttribute(
|
|
1485
|
+
types.jsxIdentifier('key'),
|
|
1486
|
+
types.jsxExpressionContainer(
|
|
1487
|
+
types.templateLiteral(
|
|
1488
|
+
[
|
|
1489
|
+
types.templateElement({ raw: 'search-', cooked: 'search-' }),
|
|
1490
|
+
types.templateElement({ raw: '', cooked: '' }),
|
|
1491
|
+
],
|
|
1492
|
+
[types.identifier(vars.debouncedSearchQueryVar)]
|
|
1493
|
+
)
|
|
1494
|
+
)
|
|
1495
|
+
)
|
|
1551
1496
|
)
|
|
1552
1497
|
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
dataProviderJSX.openingElement.attributes[index] = paramsAttr
|
|
1556
|
-
} else {
|
|
1557
|
-
dataProviderJSX.openingElement.attributes.push(paramsAttr)
|
|
1558
|
-
}
|
|
1498
|
+
// Add fetchData
|
|
1499
|
+
dp.openingElement.attributes.push(createFetchDataAttribute(fileName))
|
|
1559
1500
|
|
|
1560
|
-
|
|
1561
|
-
(
|
|
1501
|
+
dp.openingElement.attributes.push(
|
|
1502
|
+
types.jsxAttribute(
|
|
1503
|
+
types.jsxIdentifier('persistDataDuringLoading'),
|
|
1504
|
+
types.jsxExpressionContainer(types.booleanLiteral(true))
|
|
1505
|
+
)
|
|
1562
1506
|
)
|
|
1507
|
+
}
|
|
1563
1508
|
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
const paginatedPropName = `${info.dataSourceIdentifier}_pg_${paginationIndex}`
|
|
1509
|
+
function updateDataProviderForPlain(dp: any, vars: ReturnType<typeof getStateVarsForUsage>): void {
|
|
1510
|
+
const attrs = dp.openingElement.attributes
|
|
1567
1511
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1512
|
+
// Remove params and fetchData - plain mappers only use initialData
|
|
1513
|
+
dp.openingElement.attributes = attrs.filter(
|
|
1514
|
+
(attr: any) => !['params', 'fetchData'].includes(attr.name?.name)
|
|
1515
|
+
)
|
|
1570
1516
|
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
'===',
|
|
1576
|
-
types.memberExpression(types.identifier(combinedStateVar), types.identifier('page')),
|
|
1577
|
-
types.numericLiteral(1)
|
|
1578
|
-
),
|
|
1579
|
-
types.unaryExpression(
|
|
1580
|
-
'!',
|
|
1581
|
-
types.memberExpression(
|
|
1582
|
-
types.identifier(combinedStateVar),
|
|
1583
|
-
types.identifier('debouncedQuery')
|
|
1584
|
-
),
|
|
1585
|
-
true
|
|
1586
|
-
)
|
|
1587
|
-
)
|
|
1588
|
-
} else {
|
|
1589
|
-
condition = types.binaryExpression(
|
|
1590
|
-
'===',
|
|
1591
|
-
types.identifier(info.pageStateVar),
|
|
1592
|
-
types.numericLiteral(1)
|
|
1593
|
-
)
|
|
1594
|
-
}
|
|
1517
|
+
// Update initialData to use correct prop
|
|
1518
|
+
const existingInitialData = dp.openingElement.attributes.find(
|
|
1519
|
+
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'initialData'
|
|
1520
|
+
)
|
|
1595
1521
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1522
|
+
if (existingInitialData) {
|
|
1523
|
+
existingInitialData.value = types.jsxExpressionContainer(
|
|
1598
1524
|
types.optionalMemberExpression(
|
|
1599
1525
|
types.identifier('props'),
|
|
1600
|
-
types.identifier(
|
|
1526
|
+
types.identifier(vars.propsPrefix),
|
|
1601
1527
|
false,
|
|
1602
1528
|
true
|
|
1603
|
-
)
|
|
1604
|
-
types.identifier('undefined')
|
|
1529
|
+
)
|
|
1605
1530
|
)
|
|
1606
1531
|
}
|
|
1532
|
+
}
|
|
1607
1533
|
|
|
1608
|
-
|
|
1609
|
-
|
|
1534
|
+
function stabilizeDataProviderWithoutRepeater(dp: any): void {
|
|
1535
|
+
const attrs = dp.openingElement.attributes
|
|
1536
|
+
|
|
1537
|
+
// Find the params attribute
|
|
1538
|
+
const paramsAttrIndex = attrs.findIndex(
|
|
1539
|
+
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'params'
|
|
1610
1540
|
)
|
|
1611
1541
|
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
if (info.searchEnabled && combinedStateVar) {
|
|
1615
|
-
keyExpression = types.templateLiteral(
|
|
1616
|
-
[
|
|
1617
|
-
types.templateElement({
|
|
1618
|
-
raw: `${info.dataSourceIdentifier}-page-`,
|
|
1619
|
-
cooked: `${info.dataSourceIdentifier}-page-`,
|
|
1620
|
-
}),
|
|
1621
|
-
types.templateElement({ raw: '-search-', cooked: '-search-' }),
|
|
1622
|
-
types.templateElement({ raw: '', cooked: '' }),
|
|
1623
|
-
],
|
|
1624
|
-
[
|
|
1625
|
-
types.memberExpression(types.identifier(combinedStateVar), types.identifier('page')),
|
|
1626
|
-
types.memberExpression(
|
|
1627
|
-
types.identifier(combinedStateVar),
|
|
1628
|
-
types.identifier('debouncedQuery')
|
|
1629
|
-
),
|
|
1630
|
-
]
|
|
1631
|
-
)
|
|
1632
|
-
} else {
|
|
1633
|
-
keyExpression = types.templateLiteral(
|
|
1634
|
-
[
|
|
1635
|
-
types.templateElement({
|
|
1636
|
-
raw: `${info.dataSourceIdentifier}-`,
|
|
1637
|
-
cooked: `${info.dataSourceIdentifier}-`,
|
|
1638
|
-
}),
|
|
1639
|
-
types.templateElement({ raw: '', cooked: '' }),
|
|
1640
|
-
],
|
|
1641
|
-
[types.identifier(info.pageStateVar)]
|
|
1642
|
-
)
|
|
1542
|
+
if (paramsAttrIndex === -1) {
|
|
1543
|
+
return
|
|
1643
1544
|
}
|
|
1644
1545
|
|
|
1645
|
-
const
|
|
1646
|
-
types.jsxIdentifier('key'),
|
|
1647
|
-
types.jsxExpressionContainer(keyExpression)
|
|
1648
|
-
)
|
|
1546
|
+
const paramsAttr = attrs[paramsAttrIndex] as types.JSXAttribute
|
|
1649
1547
|
|
|
1650
|
-
if
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1548
|
+
// Check if params is already wrapped in useMemo
|
|
1549
|
+
if (
|
|
1550
|
+
paramsAttr.value?.type === 'JSXExpressionContainer' &&
|
|
1551
|
+
paramsAttr.value.expression.type === 'CallExpression' &&
|
|
1552
|
+
(paramsAttr.value.expression.callee as types.Identifier)?.name === 'useMemo'
|
|
1553
|
+
) {
|
|
1554
|
+
return
|
|
1655
1555
|
}
|
|
1656
1556
|
|
|
1657
|
-
//
|
|
1658
|
-
|
|
1659
|
-
const resourceDefAttr = dataProviderJSX.openingElement.attributes.find(
|
|
1660
|
-
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'resourceDefinition'
|
|
1661
|
-
)
|
|
1557
|
+
// Get the current params value expression
|
|
1558
|
+
let paramsExpression: types.Expression | null = null
|
|
1662
1559
|
|
|
1663
|
-
if (
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
const dataSourceIdProp = (resourceDef.properties as any[]).find(
|
|
1667
|
-
(p: any) => p.type === 'ObjectProperty' && p.key.value === 'dataSourceId'
|
|
1668
|
-
)
|
|
1669
|
-
const tableNameProp = (resourceDef.properties as any[]).find(
|
|
1670
|
-
(p: any) => p.type === 'ObjectProperty' && p.key.value === 'tableName'
|
|
1671
|
-
)
|
|
1672
|
-
const dataSourceTypeProp = (resourceDef.properties as any[]).find(
|
|
1673
|
-
(p: any) => p.type === 'ObjectProperty' && p.key.value === 'dataSourceType'
|
|
1674
|
-
)
|
|
1560
|
+
if (paramsAttr.value?.type === 'JSXExpressionContainer') {
|
|
1561
|
+
paramsExpression = paramsAttr.value.expression as types.Expression
|
|
1562
|
+
}
|
|
1675
1563
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
const dataSourceType = dataSourceTypeProp.value.value
|
|
1680
|
-
const fileName = `${dataSourceType}-${tableName}-${dataSourceId.substring(0, 8)}`
|
|
1564
|
+
if (!paramsExpression) {
|
|
1565
|
+
return
|
|
1566
|
+
}
|
|
1681
1567
|
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1568
|
+
// Wrap params in useMemo with empty dependencies array
|
|
1569
|
+
// This ensures the object reference stays stable across re-renders
|
|
1570
|
+
const memoizedParams = types.callExpression(types.identifier('useMemo'), [
|
|
1571
|
+
types.arrowFunctionExpression([], paramsExpression),
|
|
1572
|
+
types.arrayExpression([]),
|
|
1573
|
+
])
|
|
1574
|
+
|
|
1575
|
+
// Replace the params attribute
|
|
1576
|
+
attrs[paramsAttrIndex] = types.jsxAttribute(
|
|
1577
|
+
types.jsxIdentifier('params'),
|
|
1578
|
+
types.jsxExpressionContainer(memoizedParams)
|
|
1579
|
+
)
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
function createFetchDataAttribute(fileName: string): types.JSXAttribute {
|
|
1583
|
+
return types.jsxAttribute(
|
|
1584
|
+
types.jsxIdentifier('fetchData'),
|
|
1585
|
+
types.jsxExpressionContainer(
|
|
1586
|
+
types.callExpression(types.identifier('useCallback'), [
|
|
1587
|
+
types.arrowFunctionExpression(
|
|
1588
|
+
[types.identifier('params')],
|
|
1589
|
+
types.callExpression(
|
|
1590
|
+
types.memberExpression(
|
|
1591
|
+
types.callExpression(
|
|
1592
|
+
types.memberExpression(
|
|
1593
|
+
types.callExpression(types.identifier('fetch'), [
|
|
1594
|
+
types.templateLiteral(
|
|
1595
|
+
[
|
|
1596
|
+
types.templateElement({
|
|
1597
|
+
raw: `/api/${fileName}?`,
|
|
1598
|
+
cooked: `/api/${fileName}?`,
|
|
1599
|
+
}),
|
|
1600
|
+
types.templateElement({ raw: '', cooked: '' }),
|
|
1601
|
+
],
|
|
1602
|
+
[
|
|
1603
|
+
types.newExpression(types.identifier('URLSearchParams'), [
|
|
1604
|
+
types.identifier('params'),
|
|
1605
|
+
]),
|
|
1606
|
+
]
|
|
1607
|
+
),
|
|
1608
|
+
types.objectExpression([
|
|
1609
|
+
types.objectProperty(
|
|
1610
|
+
types.identifier('headers'),
|
|
1611
|
+
types.objectExpression([
|
|
1612
|
+
types.objectProperty(
|
|
1613
|
+
types.stringLiteral('Content-Type'),
|
|
1614
|
+
types.stringLiteral('application/json')
|
|
1615
|
+
),
|
|
1616
|
+
])
|
|
1704
1617
|
),
|
|
1705
|
-
types.objectExpression([
|
|
1706
|
-
types.objectProperty(
|
|
1707
|
-
types.identifier('headers'),
|
|
1708
|
-
types.objectExpression([
|
|
1709
|
-
types.objectProperty(
|
|
1710
|
-
types.stringLiteral('Content-Type'),
|
|
1711
|
-
types.stringLiteral('application/json')
|
|
1712
|
-
),
|
|
1713
|
-
])
|
|
1714
|
-
),
|
|
1715
|
-
]),
|
|
1716
1618
|
]),
|
|
1717
|
-
|
|
1718
|
-
)
|
|
1719
|
-
[
|
|
1720
|
-
types.arrowFunctionExpression(
|
|
1721
|
-
[types.identifier('res')],
|
|
1722
|
-
types.callExpression(
|
|
1723
|
-
types.memberExpression(types.identifier('res'), types.identifier('json')),
|
|
1724
|
-
[]
|
|
1725
|
-
)
|
|
1726
|
-
),
|
|
1727
|
-
]
|
|
1619
|
+
]),
|
|
1620
|
+
types.identifier('then')
|
|
1728
1621
|
),
|
|
1729
|
-
|
|
1622
|
+
[
|
|
1623
|
+
types.arrowFunctionExpression(
|
|
1624
|
+
[types.identifier('res')],
|
|
1625
|
+
types.callExpression(
|
|
1626
|
+
types.memberExpression(types.identifier('res'), types.identifier('json')),
|
|
1627
|
+
[]
|
|
1628
|
+
)
|
|
1629
|
+
),
|
|
1630
|
+
]
|
|
1730
1631
|
),
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
)
|
|
1751
|
-
|
|
1752
|
-
// Remove existing fetchData attribute if present
|
|
1753
|
-
const existingFetchDataIndex = dataProviderJSX.openingElement.attributes.findIndex(
|
|
1754
|
-
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'fetchData'
|
|
1755
|
-
)
|
|
1756
|
-
|
|
1757
|
-
if (existingFetchDataIndex !== -1) {
|
|
1758
|
-
dataProviderJSX.openingElement.attributes[existingFetchDataIndex] = newFetchDataAttr
|
|
1759
|
-
} else {
|
|
1760
|
-
dataProviderJSX.openingElement.attributes.push(newFetchDataAttr)
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1632
|
+
types.identifier('then')
|
|
1633
|
+
),
|
|
1634
|
+
[
|
|
1635
|
+
types.arrowFunctionExpression(
|
|
1636
|
+
[types.identifier('response')],
|
|
1637
|
+
types.optionalMemberExpression(
|
|
1638
|
+
types.identifier('response'),
|
|
1639
|
+
types.identifier('data'),
|
|
1640
|
+
false,
|
|
1641
|
+
true
|
|
1642
|
+
)
|
|
1643
|
+
),
|
|
1644
|
+
]
|
|
1645
|
+
)
|
|
1646
|
+
),
|
|
1647
|
+
types.arrayExpression([]),
|
|
1648
|
+
])
|
|
1649
|
+
)
|
|
1650
|
+
)
|
|
1765
1651
|
}
|
|
1766
1652
|
|
|
1767
|
-
function
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
if (node.type === 'JSXElement') {
|
|
1773
|
-
const className = getClassName(node.openingElement?.attributes || [])
|
|
1774
|
-
if (className && className.includes(classSubstring)) {
|
|
1775
|
-
return className
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
if (node.children) {
|
|
1780
|
-
for (const child of node.children) {
|
|
1781
|
-
const found = findChildWithClass(child, classSubstring)
|
|
1782
|
-
if (found) {
|
|
1783
|
-
return found
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1653
|
+
function wireSearchInput(inputNode: any, vars: ReturnType<typeof getStateVarsForUsage>): void {
|
|
1654
|
+
// Remove existing onChange and value
|
|
1655
|
+
inputNode.openingElement.attributes = inputNode.openingElement.attributes.filter(
|
|
1656
|
+
(attr: any) => !['onChange', 'value'].includes(attr.name?.name)
|
|
1657
|
+
)
|
|
1787
1658
|
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1659
|
+
// Add onChange
|
|
1660
|
+
inputNode.openingElement.attributes.push(
|
|
1661
|
+
types.jsxAttribute(
|
|
1662
|
+
types.jsxIdentifier('onChange'),
|
|
1663
|
+
types.jsxExpressionContainer(
|
|
1664
|
+
types.arrowFunctionExpression(
|
|
1665
|
+
[types.identifier('e')],
|
|
1666
|
+
types.callExpression(types.identifier(vars.setSearchQueryVar), [
|
|
1667
|
+
types.memberExpression(
|
|
1668
|
+
types.memberExpression(types.identifier('e'), types.identifier('target')),
|
|
1669
|
+
types.identifier('value')
|
|
1670
|
+
),
|
|
1671
|
+
])
|
|
1672
|
+
)
|
|
1673
|
+
)
|
|
1674
|
+
)
|
|
1675
|
+
)
|
|
1805
1676
|
|
|
1806
|
-
|
|
1677
|
+
// Add value
|
|
1678
|
+
inputNode.openingElement.attributes.push(
|
|
1679
|
+
types.jsxAttribute(
|
|
1680
|
+
types.jsxIdentifier('value'),
|
|
1681
|
+
types.jsxExpressionContainer(types.identifier(vars.searchQueryVar))
|
|
1682
|
+
)
|
|
1683
|
+
)
|
|
1807
1684
|
}
|
|
1808
1685
|
|
|
1809
|
-
function
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1686
|
+
function wirePaginationButtons(
|
|
1687
|
+
paginationNode: any,
|
|
1688
|
+
usage: DataSourceUsage,
|
|
1689
|
+
vars: ReturnType<typeof getStateVarsForUsage>
|
|
1813
1690
|
): void {
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
const modifyNode = (node: any): void => {
|
|
1691
|
+
// Find prev and next buttons
|
|
1692
|
+
const findButton = (node: any, direction: 'previous' | 'next'): any => {
|
|
1817
1693
|
if (!node) {
|
|
1818
|
-
return
|
|
1694
|
+
return null
|
|
1819
1695
|
}
|
|
1820
1696
|
|
|
1821
1697
|
if (node.type === 'JSXElement') {
|
|
1822
|
-
const
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
const detected = detectedPaginations[index]
|
|
1829
|
-
const info = paginationInfos[index]
|
|
1830
|
-
|
|
1831
|
-
if (!info) {
|
|
1832
|
-
continue
|
|
1833
|
-
}
|
|
1834
|
-
|
|
1835
|
-
if (className === detected.prevButtonClass) {
|
|
1836
|
-
convertToButton(node, info, 'prev')
|
|
1837
|
-
modifiedButtons.add(node)
|
|
1838
|
-
break
|
|
1839
|
-
} else if (className === detected.nextButtonClass) {
|
|
1840
|
-
convertToButton(node, info, 'next')
|
|
1841
|
-
modifiedButtons.add(node)
|
|
1842
|
-
break
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1698
|
+
const classAttr = node.openingElement?.attributes?.find(
|
|
1699
|
+
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'className'
|
|
1700
|
+
)
|
|
1701
|
+
const className = classAttr?.value?.value || classAttr?.value?.expression?.value || ''
|
|
1702
|
+
if (className.includes(direction)) {
|
|
1703
|
+
return node
|
|
1846
1704
|
}
|
|
1847
1705
|
}
|
|
1848
1706
|
|
|
1849
|
-
if (
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
modifyNode(value)
|
|
1707
|
+
if (node.children && Array.isArray(node.children)) {
|
|
1708
|
+
for (const c of node.children) {
|
|
1709
|
+
const found = findButton(c, direction)
|
|
1710
|
+
if (found) {
|
|
1711
|
+
return found
|
|
1855
1712
|
}
|
|
1856
|
-
}
|
|
1713
|
+
}
|
|
1857
1714
|
}
|
|
1715
|
+
return null
|
|
1858
1716
|
}
|
|
1859
1717
|
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
function modifySearchInputs(
|
|
1864
|
-
blockStatement: types.BlockStatement,
|
|
1865
|
-
detectedPaginations: DetectedPagination[],
|
|
1866
|
-
paginationInfos: ArrayMapperPaginationInfo[]
|
|
1867
|
-
): void {
|
|
1868
|
-
const modifiedInputs = new Set<any>()
|
|
1718
|
+
const prevButton = findButton(paginationNode, 'previous')
|
|
1719
|
+
const nextButton = findButton(paginationNode, 'next')
|
|
1869
1720
|
|
|
1870
|
-
const
|
|
1871
|
-
if (!node) {
|
|
1872
|
-
return
|
|
1873
|
-
}
|
|
1721
|
+
const isCombinedState = usage.category === 'paginated+search'
|
|
1874
1722
|
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
if (className && !modifiedInputs.has(node)) {
|
|
1881
|
-
for (let index = 0; index < detectedPaginations.length; index++) {
|
|
1882
|
-
const detected = detectedPaginations[index]
|
|
1883
|
-
const info = paginationInfos[index]
|
|
1884
|
-
|
|
1885
|
-
if (!info || !info.searchEnabled) {
|
|
1886
|
-
continue
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
if (className === detected.searchInputClass) {
|
|
1890
|
-
addSearchInputHandlers(node, info)
|
|
1891
|
-
modifiedInputs.add(node)
|
|
1892
|
-
break
|
|
1893
|
-
}
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1723
|
+
if (prevButton) {
|
|
1724
|
+
// Change to button element
|
|
1725
|
+
prevButton.openingElement.name.name = 'button'
|
|
1726
|
+
if (prevButton.closingElement) {
|
|
1727
|
+
prevButton.closingElement.name.name = 'button'
|
|
1897
1728
|
}
|
|
1898
1729
|
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
}
|
|
1906
|
-
})
|
|
1730
|
+
// Add type="button"
|
|
1731
|
+
const hasType = prevButton.openingElement.attributes.some((a: any) => a.name?.name === 'type')
|
|
1732
|
+
if (!hasType) {
|
|
1733
|
+
prevButton.openingElement.attributes.push(
|
|
1734
|
+
types.jsxAttribute(types.jsxIdentifier('type'), types.stringLiteral('button'))
|
|
1735
|
+
)
|
|
1907
1736
|
}
|
|
1908
|
-
}
|
|
1909
|
-
|
|
1910
|
-
modifyNode(blockStatement)
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
function addSearchInputHandlers(jsxElement: any, info: ArrayMapperPaginationInfo): void {
|
|
1914
|
-
if (!info.searchQueryVar || !info.setSearchQueryVar) {
|
|
1915
|
-
return
|
|
1916
|
-
}
|
|
1917
|
-
|
|
1918
|
-
const openingElement = jsxElement.openingElement
|
|
1919
|
-
|
|
1920
|
-
removeAttribute(openingElement.attributes, 'onChange')
|
|
1921
|
-
removeAttribute(openingElement.attributes, 'value')
|
|
1922
|
-
|
|
1923
|
-
const onChangeHandler = types.arrowFunctionExpression(
|
|
1924
|
-
[types.identifier('e')],
|
|
1925
|
-
types.callExpression(types.identifier(info.setSearchQueryVar!), [
|
|
1926
|
-
types.memberExpression(
|
|
1927
|
-
types.memberExpression(types.identifier('e'), types.identifier('target')),
|
|
1928
|
-
types.identifier('value')
|
|
1929
|
-
),
|
|
1930
|
-
])
|
|
1931
|
-
)
|
|
1932
|
-
|
|
1933
|
-
openingElement.attributes.push(
|
|
1934
|
-
types.jsxAttribute(
|
|
1935
|
-
types.jsxIdentifier('onChange'),
|
|
1936
|
-
types.jsxExpressionContainer(onChangeHandler)
|
|
1937
|
-
)
|
|
1938
|
-
)
|
|
1939
|
-
|
|
1940
|
-
openingElement.attributes.push(
|
|
1941
|
-
types.jsxAttribute(
|
|
1942
|
-
types.jsxIdentifier('value'),
|
|
1943
|
-
types.jsxExpressionContainer(types.identifier(info.searchQueryVar!))
|
|
1944
|
-
)
|
|
1945
|
-
)
|
|
1946
|
-
}
|
|
1947
|
-
|
|
1948
|
-
function convertToButton(
|
|
1949
|
-
jsxElement: any,
|
|
1950
|
-
info: ArrayMapperPaginationInfo,
|
|
1951
|
-
buttonType: 'prev' | 'next'
|
|
1952
|
-
): void {
|
|
1953
|
-
const openingElement = jsxElement.openingElement
|
|
1954
|
-
|
|
1955
|
-
if (openingElement.name.type === 'JSXIdentifier') {
|
|
1956
|
-
openingElement.name.name = 'button'
|
|
1957
|
-
}
|
|
1958
|
-
if (jsxElement.closingElement && jsxElement.closingElement.name.type === 'JSXIdentifier') {
|
|
1959
|
-
jsxElement.closingElement.name.name = 'button'
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
removeAttribute(openingElement.attributes, 'onClick')
|
|
1963
|
-
removeAttribute(openingElement.attributes, 'disabled')
|
|
1964
|
-
removeAttribute(openingElement.attributes, 'type')
|
|
1965
1737
|
|
|
1966
|
-
|
|
1967
|
-
|
|
1738
|
+
// Add onClick
|
|
1739
|
+
prevButton.openingElement.attributes = prevButton.openingElement.attributes.filter(
|
|
1740
|
+
(a: any) => a.name?.name !== 'onClick'
|
|
1741
|
+
)
|
|
1968
1742
|
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1743
|
+
if (isCombinedState) {
|
|
1744
|
+
prevButton.openingElement.attributes.push(
|
|
1745
|
+
types.jsxAttribute(
|
|
1746
|
+
types.jsxIdentifier('onClick'),
|
|
1747
|
+
types.jsxExpressionContainer(
|
|
1748
|
+
types.arrowFunctionExpression(
|
|
1749
|
+
[],
|
|
1750
|
+
types.callExpression(types.identifier(vars.setCombinedStateVar), [
|
|
1751
|
+
types.arrowFunctionExpression(
|
|
1752
|
+
[types.identifier('state')],
|
|
1753
|
+
types.objectExpression([
|
|
1754
|
+
types.spreadElement(types.identifier('state')),
|
|
1755
|
+
types.objectProperty(
|
|
1756
|
+
types.identifier('page'),
|
|
1757
|
+
types.callExpression(
|
|
1758
|
+
types.memberExpression(types.identifier('Math'), types.identifier('max')),
|
|
1759
|
+
[
|
|
1760
|
+
types.numericLiteral(1),
|
|
1761
|
+
types.binaryExpression(
|
|
1762
|
+
'-',
|
|
1763
|
+
types.memberExpression(
|
|
1764
|
+
types.identifier('state'),
|
|
1765
|
+
types.identifier('page')
|
|
1766
|
+
),
|
|
1767
|
+
types.numericLiteral(1)
|
|
1992
1768
|
),
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
])
|
|
1769
|
+
]
|
|
1770
|
+
)
|
|
1771
|
+
),
|
|
1772
|
+
])
|
|
1773
|
+
),
|
|
1774
|
+
])
|
|
1775
|
+
)
|
|
2001
1776
|
)
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
1777
|
+
)
|
|
1778
|
+
)
|
|
1779
|
+
|
|
1780
|
+
// Add disabled
|
|
1781
|
+
prevButton.openingElement.attributes = prevButton.openingElement.attributes.filter(
|
|
1782
|
+
(a: any) => a.name?.name !== 'disabled'
|
|
1783
|
+
)
|
|
1784
|
+
prevButton.openingElement.attributes.push(
|
|
1785
|
+
types.jsxAttribute(
|
|
1786
|
+
types.jsxIdentifier('disabled'),
|
|
1787
|
+
types.jsxExpressionContainer(
|
|
1788
|
+
types.binaryExpression(
|
|
1789
|
+
'<=',
|
|
1790
|
+
types.memberExpression(
|
|
1791
|
+
types.identifier(vars.combinedStateVar),
|
|
1792
|
+
types.identifier('page')
|
|
2018
1793
|
),
|
|
2019
|
-
|
|
1794
|
+
types.numericLiteral(1)
|
|
1795
|
+
)
|
|
2020
1796
|
)
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
[
|
|
2033
|
-
|
|
2034
|
-
types.
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
1797
|
+
)
|
|
1798
|
+
)
|
|
1799
|
+
} else {
|
|
1800
|
+
prevButton.openingElement.attributes.push(
|
|
1801
|
+
types.jsxAttribute(
|
|
1802
|
+
types.jsxIdentifier('onClick'),
|
|
1803
|
+
types.jsxExpressionContainer(
|
|
1804
|
+
types.arrowFunctionExpression(
|
|
1805
|
+
[],
|
|
1806
|
+
types.callExpression(types.identifier(vars.setPageStateVar), [
|
|
1807
|
+
types.arrowFunctionExpression(
|
|
1808
|
+
[types.identifier('page')],
|
|
1809
|
+
types.callExpression(
|
|
1810
|
+
types.memberExpression(types.identifier('Math'), types.identifier('max')),
|
|
1811
|
+
[
|
|
1812
|
+
types.numericLiteral(1),
|
|
1813
|
+
types.binaryExpression(
|
|
1814
|
+
'-',
|
|
1815
|
+
types.identifier('page'),
|
|
1816
|
+
types.numericLiteral(1)
|
|
1817
|
+
),
|
|
1818
|
+
]
|
|
1819
|
+
)
|
|
1820
|
+
),
|
|
1821
|
+
])
|
|
1822
|
+
)
|
|
2039
1823
|
)
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
1824
|
+
)
|
|
1825
|
+
)
|
|
1826
|
+
|
|
1827
|
+
prevButton.openingElement.attributes = prevButton.openingElement.attributes.filter(
|
|
1828
|
+
(a: any) => a.name?.name !== 'disabled'
|
|
1829
|
+
)
|
|
1830
|
+
prevButton.openingElement.attributes.push(
|
|
1831
|
+
types.jsxAttribute(
|
|
1832
|
+
types.jsxIdentifier('disabled'),
|
|
1833
|
+
types.jsxExpressionContainer(
|
|
1834
|
+
types.binaryExpression(
|
|
1835
|
+
'<=',
|
|
1836
|
+
types.identifier(vars.pageStateVar),
|
|
1837
|
+
types.numericLiteral(1)
|
|
1838
|
+
)
|
|
2048
1839
|
)
|
|
1840
|
+
)
|
|
1841
|
+
)
|
|
1842
|
+
}
|
|
2049
1843
|
}
|
|
2050
1844
|
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
1845
|
+
if (nextButton) {
|
|
1846
|
+
nextButton.openingElement.name.name = 'button'
|
|
1847
|
+
if (nextButton.closingElement) {
|
|
1848
|
+
nextButton.closingElement.name.name = 'button'
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
const hasType = nextButton.openingElement.attributes.some((a: any) => a.name?.name === 'type')
|
|
1852
|
+
if (!hasType) {
|
|
1853
|
+
nextButton.openingElement.attributes.push(
|
|
1854
|
+
types.jsxAttribute(types.jsxIdentifier('type'), types.stringLiteral('button'))
|
|
1855
|
+
)
|
|
1856
|
+
}
|
|
2057
1857
|
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
1858
|
+
nextButton.openingElement.attributes = nextButton.openingElement.attributes.filter(
|
|
1859
|
+
(a: any) => a.name?.name !== 'onClick'
|
|
1860
|
+
)
|
|
2061
1861
|
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
types.
|
|
2068
|
-
|
|
1862
|
+
if (isCombinedState) {
|
|
1863
|
+
nextButton.openingElement.attributes.push(
|
|
1864
|
+
types.jsxAttribute(
|
|
1865
|
+
types.jsxIdentifier('onClick'),
|
|
1866
|
+
types.jsxExpressionContainer(
|
|
1867
|
+
types.arrowFunctionExpression(
|
|
1868
|
+
[],
|
|
1869
|
+
types.callExpression(types.identifier(vars.setCombinedStateVar), [
|
|
1870
|
+
types.arrowFunctionExpression(
|
|
1871
|
+
[types.identifier('state')],
|
|
1872
|
+
types.objectExpression([
|
|
1873
|
+
types.spreadElement(types.identifier('state')),
|
|
1874
|
+
types.objectProperty(
|
|
1875
|
+
types.identifier('page'),
|
|
1876
|
+
types.binaryExpression(
|
|
1877
|
+
'+',
|
|
1878
|
+
types.memberExpression(types.identifier('state'), types.identifier('page')),
|
|
1879
|
+
types.numericLiteral(1)
|
|
1880
|
+
)
|
|
1881
|
+
),
|
|
1882
|
+
])
|
|
1883
|
+
),
|
|
1884
|
+
])
|
|
1885
|
+
)
|
|
1886
|
+
)
|
|
1887
|
+
)
|
|
1888
|
+
)
|
|
1889
|
+
|
|
1890
|
+
nextButton.openingElement.attributes = nextButton.openingElement.attributes.filter(
|
|
1891
|
+
(a: any) => a.name?.name !== 'disabled'
|
|
1892
|
+
)
|
|
1893
|
+
nextButton.openingElement.attributes.push(
|
|
1894
|
+
types.jsxAttribute(
|
|
1895
|
+
types.jsxIdentifier('disabled'),
|
|
1896
|
+
types.jsxExpressionContainer(
|
|
1897
|
+
types.binaryExpression(
|
|
1898
|
+
'>=',
|
|
1899
|
+
types.memberExpression(
|
|
1900
|
+
types.identifier(vars.combinedStateVar),
|
|
1901
|
+
types.identifier('page')
|
|
1902
|
+
),
|
|
1903
|
+
types.identifier(vars.maxPagesStateVar)
|
|
1904
|
+
)
|
|
2069
1905
|
)
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
1906
|
+
)
|
|
1907
|
+
)
|
|
1908
|
+
} else {
|
|
1909
|
+
nextButton.openingElement.attributes.push(
|
|
1910
|
+
types.jsxAttribute(
|
|
1911
|
+
types.jsxIdentifier('onClick'),
|
|
1912
|
+
types.jsxExpressionContainer(
|
|
1913
|
+
types.arrowFunctionExpression(
|
|
1914
|
+
[],
|
|
1915
|
+
types.callExpression(types.identifier(vars.setPageStateVar), [
|
|
1916
|
+
types.arrowFunctionExpression(
|
|
1917
|
+
[types.identifier('page')],
|
|
1918
|
+
types.binaryExpression('+', types.identifier('page'), types.numericLiteral(1))
|
|
1919
|
+
),
|
|
1920
|
+
])
|
|
1921
|
+
)
|
|
2074
1922
|
)
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
:
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
1923
|
+
)
|
|
1924
|
+
)
|
|
1925
|
+
|
|
1926
|
+
nextButton.openingElement.attributes = nextButton.openingElement.attributes.filter(
|
|
1927
|
+
(a: any) => a.name?.name !== 'disabled'
|
|
1928
|
+
)
|
|
1929
|
+
nextButton.openingElement.attributes.push(
|
|
1930
|
+
types.jsxAttribute(
|
|
1931
|
+
types.jsxIdentifier('disabled'),
|
|
1932
|
+
types.jsxExpressionContainer(
|
|
1933
|
+
types.binaryExpression(
|
|
1934
|
+
'>=',
|
|
1935
|
+
types.identifier(vars.pageStateVar),
|
|
1936
|
+
types.identifier(vars.maxPagesStateVar)
|
|
1937
|
+
)
|
|
2083
1938
|
)
|
|
1939
|
+
)
|
|
1940
|
+
)
|
|
1941
|
+
}
|
|
2084
1942
|
}
|
|
2085
|
-
|
|
2086
|
-
openingElement.attributes.push(
|
|
2087
|
-
types.jsxAttribute(types.jsxIdentifier('disabled'), types.jsxExpressionContainer(disabledExpr))
|
|
2088
|
-
)
|
|
2089
1943
|
}
|
|
2090
1944
|
|
|
2091
|
-
function
|
|
2092
|
-
|
|
2093
|
-
|
|
1945
|
+
function ensureAPIRouteExists(extractedResources: any, usage: DataSourceUsage): void {
|
|
1946
|
+
// Generate file name for the API route
|
|
1947
|
+
const fileName = generateSafeFileName(
|
|
1948
|
+
usage.resourceDefinition.dataSourceType,
|
|
1949
|
+
usage.resourceDefinition.tableName,
|
|
1950
|
+
usage.resourceDefinition.dataSourceId
|
|
2094
1951
|
)
|
|
2095
|
-
if (classNameAttr && classNameAttr.value && classNameAttr.value.type === 'StringLiteral') {
|
|
2096
|
-
return classNameAttr.value.value
|
|
2097
|
-
}
|
|
2098
|
-
return null
|
|
2099
|
-
}
|
|
2100
1952
|
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
1953
|
+
// Check if the utils data source file exists - if so, create API routes that re-export from it
|
|
1954
|
+
if (extractedResources[`utils/${fileName}`]) {
|
|
1955
|
+
// Create main data API route if not exists
|
|
1956
|
+
if (!extractedResources[`api/${fileName}`]) {
|
|
1957
|
+
const apiRouteCode = `import dataSourceModule from '../../utils/data-sources/${fileName}'
|
|
1958
|
+
|
|
1959
|
+
export default dataSourceModule.handler
|
|
1960
|
+
`
|
|
1961
|
+
extractedResources[`api/${fileName}`] = {
|
|
1962
|
+
fileName,
|
|
1963
|
+
fileType: FileType.JS,
|
|
1964
|
+
path: ['pages', 'api'],
|
|
1965
|
+
content: apiRouteCode,
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
2109
1968
|
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
1969
|
+
// Create count API route if not exists (needed for paginated+search cases)
|
|
1970
|
+
const countFileName = `${fileName}-count`
|
|
1971
|
+
if (!extractedResources[`api/${countFileName}`]) {
|
|
1972
|
+
const countApiRouteCode = `import dataSourceModule from '../../utils/data-sources/${fileName}'
|
|
1973
|
+
|
|
1974
|
+
export default dataSourceModule.getCount
|
|
1975
|
+
`
|
|
1976
|
+
extractedResources[`api/${countFileName}`] = {
|
|
1977
|
+
fileName: countFileName,
|
|
1978
|
+
fileType: FileType.JS,
|
|
1979
|
+
path: ['pages', 'api'],
|
|
1980
|
+
content: countApiRouteCode,
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
2117
1983
|
}
|
|
1984
|
+
}
|
|
2118
1985
|
|
|
2119
|
-
|
|
2120
|
-
|
|
1986
|
+
function updateGetStaticProps(chunks: any[], registry: StateRegistry): void {
|
|
1987
|
+
const getStaticPropsChunk = chunks.find((c) => c.name === 'getStaticProps')
|
|
1988
|
+
if (!getStaticPropsChunk || getStaticPropsChunk.type !== ChunkType.AST) {
|
|
2121
1989
|
return
|
|
2122
1990
|
}
|
|
2123
1991
|
|
|
2124
|
-
const
|
|
2125
|
-
if (!
|
|
1992
|
+
const content = getStaticPropsChunk.content as types.ExportNamedDeclaration
|
|
1993
|
+
if (!content.declaration || content.declaration.type !== 'FunctionDeclaration') {
|
|
2126
1994
|
return
|
|
2127
1995
|
}
|
|
2128
1996
|
|
|
2129
|
-
|
|
2130
|
-
const
|
|
2131
|
-
const tryBlock = functionBody.find((stmt: any) => stmt.type === 'TryStatement') as
|
|
1997
|
+
const funcBody = content.declaration.body
|
|
1998
|
+
const tryStmt = funcBody.body.find((s: any) => s.type === 'TryStatement') as
|
|
2132
1999
|
| types.TryStatement
|
|
2133
2000
|
| undefined
|
|
2134
|
-
|
|
2135
|
-
if (!tryBlock) {
|
|
2001
|
+
if (!tryStmt) {
|
|
2136
2002
|
return
|
|
2137
2003
|
}
|
|
2138
2004
|
|
|
2139
|
-
const
|
|
2005
|
+
const tryBlock = tryStmt.block
|
|
2140
2006
|
|
|
2141
|
-
// Find
|
|
2142
|
-
const
|
|
2143
|
-
(
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2007
|
+
// Find existing Promise.all
|
|
2008
|
+
const promiseAllDecl = tryBlock.body.find(
|
|
2009
|
+
(s: any) =>
|
|
2010
|
+
s.type === 'VariableDeclaration' &&
|
|
2011
|
+
s.declarations?.[0]?.init?.type === 'AwaitExpression' &&
|
|
2012
|
+
s.declarations?.[0]?.init?.argument?.type === 'CallExpression' &&
|
|
2013
|
+
s.declarations?.[0]?.init?.argument?.callee?.type === 'MemberExpression' &&
|
|
2014
|
+
s.declarations?.[0]?.init?.argument?.callee?.object?.name === 'Promise'
|
|
2149
2015
|
) as types.VariableDeclaration | undefined
|
|
2150
2016
|
|
|
2151
|
-
if (!
|
|
2017
|
+
if (!promiseAllDecl) {
|
|
2152
2018
|
return
|
|
2153
2019
|
}
|
|
2154
2020
|
|
|
2155
|
-
const
|
|
2021
|
+
const declarator = promiseAllDecl.declarations[0] as types.VariableDeclarator
|
|
2022
|
+
const awaitExpr = declarator.init as types.AwaitExpression
|
|
2156
2023
|
const promiseAllCall = awaitExpr.argument as types.CallExpression
|
|
2157
|
-
const
|
|
2158
|
-
const destructuringPattern = promiseAllStmt.declarations[0].id as types.ArrayPattern
|
|
2159
|
-
|
|
2160
|
-
// Map import names to data source identifiers from existing fetchData calls
|
|
2161
|
-
// Also track which indices to remove (non-paginated calls that will be replaced)
|
|
2162
|
-
const importToDataSource = new Map<string, string>()
|
|
2163
|
-
const indicesToRemove: number[] = []
|
|
2164
|
-
|
|
2165
|
-
promiseArray.elements.forEach((element: any, index: number) => {
|
|
2166
|
-
if (element && element.type === 'CallExpression') {
|
|
2167
|
-
let fetchCallExpr = element
|
|
2168
|
-
|
|
2169
|
-
// If wrapped in .catch(), unwrap it
|
|
2170
|
-
if (
|
|
2171
|
-
element.callee?.type === 'MemberExpression' &&
|
|
2172
|
-
element.callee?.property?.name === 'catch' &&
|
|
2173
|
-
element.callee?.object?.type === 'CallExpression'
|
|
2174
|
-
) {
|
|
2175
|
-
fetchCallExpr = element.callee.object
|
|
2176
|
-
}
|
|
2177
|
-
|
|
2178
|
-
// Now find the .fetchData() call
|
|
2179
|
-
if (
|
|
2180
|
-
fetchCallExpr.callee?.type === 'MemberExpression' &&
|
|
2181
|
-
fetchCallExpr.callee?.property?.name === 'fetchData' &&
|
|
2182
|
-
fetchCallExpr.callee?.object?.type === 'Identifier'
|
|
2183
|
-
) {
|
|
2184
|
-
const importName = fetchCallExpr.callee.object.name
|
|
2185
|
-
const dataSourceVar = (destructuringPattern.elements[index] as types.Identifier).name
|
|
2186
|
-
|
|
2187
|
-
// Check if this fetchData call has page/perPage params
|
|
2188
|
-
const params = fetchCallExpr.arguments[0]
|
|
2189
|
-
const hasPageParam =
|
|
2190
|
-
params &&
|
|
2191
|
-
params.type === 'ObjectExpression' &&
|
|
2192
|
-
params.properties.some(
|
|
2193
|
-
(prop: any) =>
|
|
2194
|
-
prop.type === 'ObjectProperty' &&
|
|
2195
|
-
(prop.key.name === 'page' || prop.key.name === 'perPage')
|
|
2196
|
-
)
|
|
2024
|
+
const fetchesArray = promiseAllCall.arguments[0] as types.ArrayExpression
|
|
2197
2025
|
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
}
|
|
2026
|
+
// Find return statement
|
|
2027
|
+
const returnStmt = tryBlock.body.find((s: any) => s.type === 'ReturnStatement') as
|
|
2028
|
+
| types.ReturnStatement
|
|
2029
|
+
| undefined
|
|
2030
|
+
if (!returnStmt || returnStmt.argument?.type !== 'ObjectExpression') {
|
|
2031
|
+
return
|
|
2032
|
+
}
|
|
2206
2033
|
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2034
|
+
const returnObj = returnStmt.argument as types.ObjectExpression
|
|
2035
|
+
const propsProperty = returnObj.properties.find(
|
|
2036
|
+
(p: any) => p.type === 'ObjectProperty' && p.key.type === 'Identifier' && p.key.name === 'props'
|
|
2037
|
+
) as types.ObjectProperty | undefined
|
|
2211
2038
|
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
const propToRemove = (destructuringPattern.elements[index] as types.Identifier)?.name
|
|
2039
|
+
if (!propsProperty || propsProperty.value.type !== 'ObjectExpression') {
|
|
2040
|
+
return
|
|
2041
|
+
}
|
|
2216
2042
|
|
|
2217
|
-
|
|
2218
|
-
|
|
2043
|
+
const propsObj = propsProperty.value as types.ObjectExpression
|
|
2044
|
+
const arrayPattern = declarator.id as types.ArrayPattern
|
|
2219
2045
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
const foundReturnStmt = tryBody.find((stmt: any) => stmt.type === 'ReturnStatement') as
|
|
2223
|
-
| types.ReturnStatement
|
|
2224
|
-
| undefined
|
|
2046
|
+
// Track unique data sources for count fetching
|
|
2047
|
+
const dataSourcesNeedingCount = new Set<string>()
|
|
2225
2048
|
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2049
|
+
registry.usages.forEach((usage) => {
|
|
2050
|
+
const vars = getStateVarsForUsage(usage)
|
|
2051
|
+
const fileName = generateSafeFileName(
|
|
2052
|
+
usage.resourceDefinition.dataSourceType,
|
|
2053
|
+
usage.resourceDefinition.tableName,
|
|
2054
|
+
usage.resourceDefinition.dataSourceId
|
|
2055
|
+
)
|
|
2056
|
+
// Use consistent import name generation (matches extractDataSourceIntoGetStaticProps)
|
|
2057
|
+
const fetcherImportName = StringUtils.dashCaseToCamelCase(fileName)
|
|
2231
2058
|
|
|
2232
|
-
|
|
2233
|
-
|
|
2059
|
+
// Add fetch call
|
|
2060
|
+
const fetchParams: types.ObjectProperty[] = []
|
|
2234
2061
|
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2062
|
+
if (usage.paginated) {
|
|
2063
|
+
// For paginated array mappers, add page and perPage
|
|
2064
|
+
fetchParams.push(types.objectProperty(types.identifier('page'), types.numericLiteral(1)))
|
|
2065
|
+
fetchParams.push(
|
|
2066
|
+
types.objectProperty(types.identifier('perPage'), types.numericLiteral(usage.perPage))
|
|
2067
|
+
)
|
|
2068
|
+
} else if (usage.perPage > 0) {
|
|
2069
|
+
// For non-paginated array mappers with a limit, add the limit as perPage
|
|
2070
|
+
// This ensures the initial data fetch respects the limit from the UIDL
|
|
2071
|
+
fetchParams.push(
|
|
2072
|
+
types.objectProperty(types.identifier('perPage'), types.numericLiteral(usage.perPage))
|
|
2073
|
+
)
|
|
2244
2074
|
}
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
)?.[0]
|
|
2253
|
-
|
|
2254
|
-
if (!importName) {
|
|
2255
|
-
// Try case-insensitive match
|
|
2256
|
-
const normalizedIdentifier = info.dataSourceIdentifier.toLowerCase().replace(/[_-]/g, '')
|
|
2257
|
-
importName = Array.from(importToDataSource.entries()).find(
|
|
2258
|
-
([_, dataSourceVar]) =>
|
|
2259
|
-
dataSourceVar.toLowerCase().replace(/[_-]/g, '') === normalizedIdentifier
|
|
2260
|
-
)?.[0]
|
|
2261
|
-
}
|
|
2262
|
-
|
|
2263
|
-
if (importName) {
|
|
2264
|
-
const paginatedVarName = `${info.dataSourceIdentifier}_pg_${index}`
|
|
2265
|
-
|
|
2266
|
-
const fetchParams = [
|
|
2267
|
-
types.objectProperty(types.identifier('page'), types.numericLiteral(1)),
|
|
2268
|
-
types.objectProperty(types.identifier('perPage'), types.numericLiteral(info.perPage)),
|
|
2269
|
-
]
|
|
2270
|
-
|
|
2271
|
-
// Add queryColumns if they exist
|
|
2272
|
-
if (info.queryColumns && info.queryColumns.length > 0) {
|
|
2273
|
-
fetchParams.push(
|
|
2274
|
-
types.objectProperty(
|
|
2275
|
-
types.identifier('queryColumns'),
|
|
2276
|
-
types.arrayExpression(info.queryColumns.map((col) => types.stringLiteral(col)))
|
|
2075
|
+
if (usage.queryColumns.length > 0) {
|
|
2076
|
+
fetchParams.push(
|
|
2077
|
+
types.objectProperty(
|
|
2078
|
+
types.identifier('queryColumns'),
|
|
2079
|
+
types.callExpression(
|
|
2080
|
+
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
2081
|
+
[types.arrayExpression(usage.queryColumns.map((c) => types.stringLiteral(c)))]
|
|
2277
2082
|
)
|
|
2278
2083
|
)
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
|
-
// Create new fetchData call with pagination params
|
|
2282
|
-
const newFetchDataCall = types.callExpression(
|
|
2283
|
-
types.memberExpression(
|
|
2284
|
-
types.callExpression(
|
|
2285
|
-
types.memberExpression(types.identifier(importName), types.identifier('fetchData')),
|
|
2286
|
-
[types.objectExpression(fetchParams)]
|
|
2287
|
-
),
|
|
2288
|
-
types.identifier('catch')
|
|
2289
|
-
),
|
|
2290
|
-
[
|
|
2291
|
-
types.arrowFunctionExpression(
|
|
2292
|
-
[types.identifier('error')],
|
|
2293
|
-
types.blockStatement([
|
|
2294
|
-
types.expressionStatement(
|
|
2295
|
-
types.callExpression(
|
|
2296
|
-
types.memberExpression(types.identifier('console'), types.identifier('error')),
|
|
2297
|
-
[
|
|
2298
|
-
types.stringLiteral(`Error fetching ${paginatedVarName}:`),
|
|
2299
|
-
types.identifier('error'),
|
|
2300
|
-
]
|
|
2301
|
-
)
|
|
2302
|
-
),
|
|
2303
|
-
types.returnStatement(types.arrayExpression([])),
|
|
2304
|
-
])
|
|
2305
|
-
),
|
|
2306
|
-
]
|
|
2307
2084
|
)
|
|
2308
|
-
|
|
2309
|
-
promiseArray.elements.push(newFetchDataCall)
|
|
2310
|
-
destructuringPattern.elements.push(types.identifier(paginatedVarName))
|
|
2311
2085
|
}
|
|
2312
|
-
})
|
|
2313
2086
|
|
|
2314
|
-
|
|
2087
|
+
// Check if this fetch already exists
|
|
2088
|
+
const existingFetchIndex = arrayPattern.elements.findIndex(
|
|
2089
|
+
(el: any) => el?.type === 'Identifier' && el.name === vars.propsPrefix
|
|
2090
|
+
)
|
|
2315
2091
|
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
const addedCountFetches = new Set<string>()
|
|
2092
|
+
if (existingFetchIndex === -1) {
|
|
2093
|
+
arrayPattern.elements.push(types.identifier(vars.propsPrefix))
|
|
2319
2094
|
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2095
|
+
fetchesArray.elements.push(
|
|
2096
|
+
types.callExpression(
|
|
2097
|
+
types.memberExpression(
|
|
2098
|
+
types.callExpression(
|
|
2099
|
+
types.memberExpression(
|
|
2100
|
+
types.identifier(fetcherImportName),
|
|
2101
|
+
types.identifier('fetchData')
|
|
2102
|
+
),
|
|
2103
|
+
[types.objectExpression(fetchParams)]
|
|
2104
|
+
),
|
|
2105
|
+
types.identifier('catch')
|
|
2106
|
+
),
|
|
2107
|
+
[
|
|
2108
|
+
types.arrowFunctionExpression(
|
|
2109
|
+
[types.identifier('error')],
|
|
2110
|
+
types.blockStatement([
|
|
2111
|
+
types.expressionStatement(
|
|
2112
|
+
types.callExpression(
|
|
2113
|
+
types.memberExpression(types.identifier('console'), types.identifier('error')),
|
|
2114
|
+
[
|
|
2115
|
+
types.stringLiteral(`Error fetching ${vars.propsPrefix}:`),
|
|
2116
|
+
types.identifier('error'),
|
|
2117
|
+
]
|
|
2118
|
+
)
|
|
2119
|
+
),
|
|
2120
|
+
types.returnStatement(types.arrayExpression([])),
|
|
2121
|
+
])
|
|
2122
|
+
),
|
|
2123
|
+
]
|
|
2124
|
+
)
|
|
2125
|
+
)
|
|
2324
2126
|
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
([_, dataSourceVar]) =>
|
|
2330
|
-
dataSourceVar.toLowerCase().replace(/[_-]/g, '') === normalizedIdentifier
|
|
2331
|
-
)?.[0]
|
|
2127
|
+
// Add to props
|
|
2128
|
+
propsObj.properties.push(
|
|
2129
|
+
types.objectProperty(types.identifier(vars.propsPrefix), types.identifier(vars.propsPrefix))
|
|
2130
|
+
)
|
|
2332
2131
|
}
|
|
2333
2132
|
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2133
|
+
// Track for count fetching
|
|
2134
|
+
if (usage.paginated) {
|
|
2135
|
+
dataSourcesNeedingCount.add(
|
|
2136
|
+
`${usage.resourceDefinition.dataSourceType}:${usage.resourceDefinition.tableName}:${usage.resourceDefinition.dataSourceId}`
|
|
2338
2137
|
)
|
|
2339
|
-
promiseArray.elements.push(fetchCountCall)
|
|
2340
|
-
destructuringPattern.elements.push(types.identifier(`${dataSourceId}_count`))
|
|
2341
|
-
addedCountFetches.add(dataSourceId)
|
|
2342
2138
|
}
|
|
2343
|
-
})
|
|
2344
|
-
|
|
2345
|
-
// Calculate and add maxPages before return
|
|
2346
|
-
const returnStmt = tryBody.find((stmt: any) => stmt.type === 'ReturnStatement') as
|
|
2347
|
-
| types.ReturnStatement
|
|
2348
|
-
| undefined
|
|
2349
2139
|
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
) as types.ObjectProperty | undefined
|
|
2140
|
+
// Add maxPages calculation for paginated
|
|
2141
|
+
if (usage.paginated) {
|
|
2142
|
+
const maxPagesPropName = `${vars.propsPrefix}_maxPages`
|
|
2143
|
+
const countVarName = `${usage.dataSourceIdentifier}_count`
|
|
2355
2144
|
|
|
2356
|
-
|
|
2357
|
-
const
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
const paginatedVarName = `${info.dataSourceIdentifier}_pg_${index}`
|
|
2362
|
-
const countVarName = `${info.dataSourceIdentifier}_count`
|
|
2363
|
-
const maxPagesVarName = `${info.dataSourceIdentifier}_pg_${index}_maxPages`
|
|
2145
|
+
// Check if maxPages calculation already exists
|
|
2146
|
+
const existingMaxPages = tryBlock.body.find(
|
|
2147
|
+
(s: any) =>
|
|
2148
|
+
s.type === 'VariableDeclaration' && s.declarations?.[0]?.id?.name === maxPagesPropName
|
|
2149
|
+
)
|
|
2364
2150
|
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2151
|
+
if (!existingMaxPages) {
|
|
2152
|
+
// Insert maxPages calculation before return
|
|
2153
|
+
const returnIndex = tryBlock.body.indexOf(returnStmt)
|
|
2154
|
+
tryBlock.body.splice(
|
|
2155
|
+
returnIndex,
|
|
2156
|
+
0,
|
|
2157
|
+
types.variableDeclaration('const', [
|
|
2158
|
+
types.variableDeclarator(
|
|
2159
|
+
types.identifier(maxPagesPropName),
|
|
2160
|
+
types.callExpression(
|
|
2161
|
+
types.memberExpression(types.identifier('Math'), types.identifier('ceil')),
|
|
2162
|
+
[
|
|
2163
|
+
types.binaryExpression(
|
|
2164
|
+
'/',
|
|
2165
|
+
types.logicalExpression(
|
|
2166
|
+
'||',
|
|
2167
|
+
types.identifier(countVarName),
|
|
2168
|
+
types.numericLiteral(0)
|
|
2169
|
+
),
|
|
2170
|
+
types.numericLiteral(usage.perPage)
|
|
2377
2171
|
),
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
])
|
|
2384
|
-
|
|
2385
|
-
tryBody.splice(returnIndex, 0, maxPagesCalc)
|
|
2172
|
+
]
|
|
2173
|
+
)
|
|
2174
|
+
),
|
|
2175
|
+
])
|
|
2176
|
+
)
|
|
2386
2177
|
|
|
2387
|
-
// Add
|
|
2388
|
-
|
|
2178
|
+
// Add maxPages to props
|
|
2179
|
+
propsObj.properties.push(
|
|
2389
2180
|
types.objectProperty(
|
|
2390
|
-
types.identifier(
|
|
2391
|
-
types.identifier(
|
|
2181
|
+
types.identifier(maxPagesPropName),
|
|
2182
|
+
types.identifier(maxPagesPropName)
|
|
2392
2183
|
)
|
|
2393
2184
|
)
|
|
2394
|
-
propsObject.properties.push(
|
|
2395
|
-
types.objectProperty(types.identifier(maxPagesVarName), types.identifier(maxPagesVarName))
|
|
2396
|
-
)
|
|
2397
|
-
})
|
|
2398
|
-
}
|
|
2399
|
-
}
|
|
2400
|
-
}
|
|
2401
|
-
|
|
2402
|
-
function createAPIRoutesForPaginatedDataSources(
|
|
2403
|
-
uidlNode: any,
|
|
2404
|
-
dataSources: any,
|
|
2405
|
-
componentChunk: any,
|
|
2406
|
-
extractedResources: any,
|
|
2407
|
-
paginationInfos: ArrayMapperPaginationInfo[],
|
|
2408
|
-
isComponent: boolean
|
|
2409
|
-
): void {
|
|
2410
|
-
const paginatedDataSourceIds = new Set(paginationInfos.map((info) => info.dataSourceIdentifier))
|
|
2411
|
-
|
|
2412
|
-
const searchEnabledDataSources = new Set(
|
|
2413
|
-
paginationInfos.filter((info) => info.searchEnabled).map((info) => info.dataSourceIdentifier)
|
|
2414
|
-
)
|
|
2415
|
-
|
|
2416
|
-
const createdCountRoutes = new Set<string>()
|
|
2417
|
-
|
|
2418
|
-
const traverseForDataSources = (node: any): void => {
|
|
2419
|
-
if (!node) {
|
|
2420
|
-
return
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
|
-
if (node.type === 'data-source-list' || node.type === 'data-source-item') {
|
|
2424
|
-
const renderProp = node.content.renderPropIdentifier
|
|
2425
|
-
|
|
2426
|
-
if (renderProp && paginatedDataSourceIds.has(renderProp)) {
|
|
2427
|
-
extractDataSourceIntoNextAPIFolder(node, dataSources, componentChunk, extractedResources)
|
|
2428
|
-
|
|
2429
|
-
const hasSearch = searchEnabledDataSources.has(renderProp)
|
|
2430
|
-
const needsCountRoute = isComponent || hasSearch
|
|
2431
|
-
|
|
2432
|
-
if (needsCountRoute) {
|
|
2433
|
-
const resourceDef = node.content.resourceDefinition
|
|
2434
|
-
if (resourceDef) {
|
|
2435
|
-
const dataSourceId = resourceDef.dataSourceId
|
|
2436
|
-
const tableName = resourceDef.tableName
|
|
2437
|
-
const dataSourceType = resourceDef.dataSourceType
|
|
2438
|
-
const fileName = `${dataSourceType}-${tableName}-${dataSourceId.substring(0, 8)}`
|
|
2439
|
-
const countFileName = `${fileName}-count`
|
|
2440
|
-
|
|
2441
|
-
if (!createdCountRoutes.has(countFileName)) {
|
|
2442
|
-
extractedResources[`api/${countFileName}`] = {
|
|
2443
|
-
fileName: countFileName,
|
|
2444
|
-
fileType: FileType.JS,
|
|
2445
|
-
path: ['pages', 'api'],
|
|
2446
|
-
content: `import dataSource from '../../utils/data-sources/${fileName}'
|
|
2447
|
-
|
|
2448
|
-
export default dataSource.getCount
|
|
2449
|
-
`,
|
|
2450
|
-
}
|
|
2451
|
-
createdCountRoutes.add(countFileName)
|
|
2452
|
-
}
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
2185
|
}
|
|
2456
2186
|
}
|
|
2187
|
+
})
|
|
2457
2188
|
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
): void {
|
|
2473
|
-
const searchOnlyDataSourceIds = new Set(
|
|
2474
|
-
searchOnlyDataSources.map((info) => info.dataSourceIdentifier)
|
|
2475
|
-
)
|
|
2476
|
-
|
|
2477
|
-
const traverseForDataSources = (node: any): void => {
|
|
2478
|
-
if (!node) {
|
|
2189
|
+
// Add count fetches for unique data sources
|
|
2190
|
+
dataSourcesNeedingCount.forEach((key) => {
|
|
2191
|
+
const [dataSourceType, tableName, dataSourceId] = key.split(':')
|
|
2192
|
+
const fileName = generateSafeFileName(dataSourceType, tableName, dataSourceId)
|
|
2193
|
+
// Use consistent import name generation (matches extractDataSourceIntoGetStaticProps)
|
|
2194
|
+
const fetcherImportName = StringUtils.dashCaseToCamelCase(fileName)
|
|
2195
|
+
|
|
2196
|
+
// Find usage to get dataSourceIdentifier
|
|
2197
|
+
const usage = registry.usages.find(
|
|
2198
|
+
(u) =>
|
|
2199
|
+
u.resourceDefinition.dataSourceId === dataSourceId &&
|
|
2200
|
+
u.resourceDefinition.tableName === tableName
|
|
2201
|
+
)
|
|
2202
|
+
if (!usage) {
|
|
2479
2203
|
return
|
|
2480
2204
|
}
|
|
2481
2205
|
|
|
2482
|
-
|
|
2483
|
-
const renderProp = node.content.renderPropIdentifier
|
|
2206
|
+
const countVarName = `${usage.dataSourceIdentifier}_count`
|
|
2484
2207
|
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2208
|
+
// Check if count fetch already exists
|
|
2209
|
+
const existingCount = arrayPattern.elements.findIndex(
|
|
2210
|
+
(el: any) => el?.type === 'Identifier' && el.name === countVarName
|
|
2211
|
+
)
|
|
2489
2212
|
|
|
2490
|
-
if (
|
|
2491
|
-
|
|
2213
|
+
if (existingCount === -1) {
|
|
2214
|
+
arrayPattern.elements.push(types.identifier(countVarName))
|
|
2215
|
+
fetchesArray.elements.push(
|
|
2216
|
+
types.callExpression(
|
|
2217
|
+
types.memberExpression(
|
|
2218
|
+
types.identifier(fetcherImportName),
|
|
2219
|
+
types.identifier('fetchCount')
|
|
2220
|
+
),
|
|
2221
|
+
[]
|
|
2222
|
+
)
|
|
2223
|
+
)
|
|
2492
2224
|
}
|
|
2493
|
-
}
|
|
2494
|
-
|
|
2495
|
-
traverseForDataSources(uidlNode)
|
|
2225
|
+
})
|
|
2496
2226
|
}
|
|
2497
|
-
|
|
2498
|
-
export default createNextArrayMapperPaginationPlugin()
|