@teleporthq/teleport-plugin-next-data-source 0.42.5 → 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 +895 -1405
- 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 +896 -1406
- 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 +1846 -2201
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,2472 +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
|
-
)
|
|
594
|
-
|
|
595
|
-
if (
|
|
596
|
-
!resourceDefAttr ||
|
|
597
|
-
!resourceDefAttr.value ||
|
|
598
|
-
resourceDefAttr.value.type !== 'JSXExpressionContainer'
|
|
599
|
-
) {
|
|
600
|
-
return
|
|
601
|
-
}
|
|
506
|
+
// Build the count fetch effect body
|
|
507
|
+
const countFetchEffectBody: types.Statement[] = []
|
|
602
508
|
|
|
603
|
-
|
|
604
|
-
if (
|
|
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
|
-
|
|
764
|
-
|
|
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
|
+
})
|
|
894
|
+
|
|
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>()
|
|
766
898
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
options.dataSources,
|
|
771
|
-
componentChunk,
|
|
772
|
-
options.extractedResources,
|
|
773
|
-
searchOnlyDataSources
|
|
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
|
-
|
|
907
|
+
const dataSourceIdentifier = nameAttr.value.expression.value
|
|
778
908
|
|
|
779
|
-
|
|
780
|
-
|
|
909
|
+
// Use pure order-based matching within each dataSourceIdentifier
|
|
910
|
+
const usages = registry.byDataSourceId.get(dataSourceIdentifier) || []
|
|
911
|
+
const currentIndex = usageIndexByDataSourceId.get(dataSourceIdentifier) || 0
|
|
781
912
|
|
|
782
|
-
|
|
783
|
-
|
|
913
|
+
if (currentIndex >= usages.length) {
|
|
914
|
+
return
|
|
915
|
+
}
|
|
784
916
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
if (!node) {
|
|
788
|
-
return results
|
|
789
|
-
}
|
|
917
|
+
const usage = usages[currentIndex]
|
|
918
|
+
usageIndexByDataSourceId.set(dataSourceIdentifier, currentIndex + 1)
|
|
790
919
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
920
|
+
const vars = getStateVarsForUsage(usage)
|
|
921
|
+
const fileName = generateSafeFileName(
|
|
922
|
+
usage.resourceDefinition.dataSourceType,
|
|
923
|
+
usage.resourceDefinition.tableName,
|
|
924
|
+
usage.resourceDefinition.dataSourceId
|
|
925
|
+
)
|
|
794
926
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
if (
|
|
799
|
-
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
(node.expression.type === 'JSXElement' || node.expression.type === 'JSXFragment')
|
|
805
|
-
) {
|
|
806
|
-
findAllDataProviders(node.expression, results)
|
|
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)
|
|
807
936
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
937
|
+
|
|
938
|
+
// Create API route if needed
|
|
939
|
+
if (usage.category !== 'plain') {
|
|
940
|
+
ensureAPIRouteExists(options.extractedResources, usage)
|
|
811
941
|
}
|
|
812
|
-
}
|
|
813
|
-
findAllDataProviders(node.consequent, results)
|
|
814
|
-
findAllDataProviders(node.alternate, results)
|
|
815
|
-
}
|
|
942
|
+
})
|
|
816
943
|
|
|
817
|
-
|
|
818
|
-
|
|
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
|
+
})
|
|
819
951
|
|
|
820
|
-
|
|
952
|
+
dataProvidersWithoutRepeaters.forEach((dp) => {
|
|
953
|
+
stabilizeDataProviderWithoutRepeater(dp)
|
|
954
|
+
})
|
|
821
955
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
)
|
|
826
|
-
const hasFetchData = dataProvider.openingElement.attributes.some(
|
|
827
|
-
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'fetchData'
|
|
828
|
-
)
|
|
829
|
-
const paramsAttr = dataProvider.openingElement.attributes.find(
|
|
830
|
-
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'params'
|
|
831
|
-
)
|
|
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)
|
|
832
959
|
|
|
833
|
-
//
|
|
834
|
-
|
|
835
|
-
if (hasInitialData && !hasFetchData && paramsAttr) {
|
|
836
|
-
dataProvider.openingElement.attributes = dataProvider.openingElement.attributes.filter(
|
|
837
|
-
(attr: any) => attr.type !== 'JSXAttribute' || attr.name.name !== 'params'
|
|
838
|
-
)
|
|
839
|
-
}
|
|
960
|
+
// Get all search-enabled usages in order
|
|
961
|
+
const searchEnabledUsages = registry.usages.filter((u) => u.searchEnabled)
|
|
840
962
|
|
|
841
|
-
//
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
963
|
+
// Match by order - search input 0 -> searchEnabledUsages[0], etc.
|
|
964
|
+
searchInputs.forEach((input, idx) => {
|
|
965
|
+
if (idx >= searchEnabledUsages.length) {
|
|
966
|
+
return
|
|
967
|
+
}
|
|
845
968
|
|
|
846
|
-
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
(paramsExpression.callee.name === 'useMemo' ||
|
|
851
|
-
paramsExpression.callee.name === 'useCallback')
|
|
969
|
+
const usage = searchEnabledUsages[idx]
|
|
970
|
+
const vars = getStateVarsForUsage(usage)
|
|
971
|
+
wireSearchInput(input.node, vars)
|
|
972
|
+
})
|
|
852
973
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
types.arrowFunctionExpression([], paramsExpression),
|
|
857
|
-
types.arrayExpression([]), // Empty deps - params are static
|
|
858
|
-
])
|
|
974
|
+
// STEP 5: Wire pagination buttons
|
|
975
|
+
// Match pagination nodes to usages by order (within paginated usages)
|
|
976
|
+
const paginationNodes = findAllPaginationNodesInJSX(blockStatement)
|
|
859
977
|
|
|
860
|
-
|
|
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
|
|
861
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)
|
|
862
995
|
}
|
|
863
|
-
})
|
|
864
|
-
}
|
|
865
996
|
|
|
866
|
-
|
|
867
|
-
if (!root || !target) {
|
|
868
|
-
return null
|
|
997
|
+
return structure
|
|
869
998
|
}
|
|
870
999
|
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
}
|
|
1000
|
+
return paginationPlugin
|
|
1001
|
+
}
|
|
874
1002
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1003
|
+
// ==================== HELPER FUNCTIONS ====================
|
|
1004
|
+
|
|
1005
|
+
function findAllDataProvidersInJSX(blockStatement: types.BlockStatement): any[] {
|
|
1006
|
+
const results: any[] = []
|
|
1007
|
+
|
|
1008
|
+
const traverse = (node: any): void => {
|
|
1009
|
+
if (!node) {
|
|
1010
|
+
return
|
|
883
1011
|
}
|
|
884
|
-
|
|
885
|
-
if (
|
|
886
|
-
|
|
887
|
-
if (found !== null) {
|
|
888
|
-
return found
|
|
889
|
-
}
|
|
1012
|
+
|
|
1013
|
+
if (node.type === 'JSXElement' && node.openingElement?.name?.name === 'DataProvider') {
|
|
1014
|
+
results.push(node)
|
|
890
1015
|
}
|
|
891
|
-
|
|
892
|
-
if (
|
|
893
|
-
|
|
894
|
-
const found = findParentNode(stmt, target, root)
|
|
895
|
-
if (found !== null) {
|
|
896
|
-
return found
|
|
897
|
-
}
|
|
898
|
-
}
|
|
1016
|
+
|
|
1017
|
+
if (node.children && Array.isArray(node.children)) {
|
|
1018
|
+
node.children.forEach((c: any) => traverse(c))
|
|
899
1019
|
}
|
|
900
|
-
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1020
|
+
|
|
1021
|
+
if (node.body) {
|
|
1022
|
+
if (Array.isArray(node.body)) {
|
|
1023
|
+
node.body.forEach((s: any) => traverse(s))
|
|
1024
|
+
} else {
|
|
1025
|
+
traverse(node.body)
|
|
905
1026
|
}
|
|
906
1027
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1028
|
+
|
|
1029
|
+
if (node.consequent) {
|
|
1030
|
+
traverse(node.consequent)
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
if (node.alternate) {
|
|
1034
|
+
traverse(node.alternate)
|
|
911
1035
|
}
|
|
912
|
-
|
|
913
|
-
if (
|
|
914
|
-
|
|
1036
|
+
|
|
1037
|
+
if (node.expression) {
|
|
1038
|
+
traverse(node.expression)
|
|
915
1039
|
}
|
|
916
|
-
}
|
|
917
1040
|
|
|
918
|
-
|
|
919
|
-
|
|
1041
|
+
if (node.argument) {
|
|
1042
|
+
traverse(node.argument)
|
|
1043
|
+
}
|
|
920
1044
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
): {
|
|
925
|
-
paginatedMappers: DetectedPagination[]
|
|
926
|
-
searchOnlyMappers: DetectedPagination[]
|
|
927
|
-
paginationOnlyMappers: DetectedPagination[]
|
|
928
|
-
plainMappers: DetectedPagination[]
|
|
929
|
-
} {
|
|
930
|
-
interface DataProviderInfo {
|
|
931
|
-
identifier: string
|
|
932
|
-
dataProvider: any
|
|
933
|
-
arrayMapperRenderProp?: string
|
|
934
|
-
paginationNode?: { class: string; prevClass: string | null; nextClass: string | null }
|
|
935
|
-
searchInput?: { class: string | null; jsx: any }
|
|
936
|
-
hasPagination: boolean
|
|
937
|
-
hasSearch: boolean
|
|
1045
|
+
if (node.arguments) {
|
|
1046
|
+
node.arguments.forEach((a: any) => traverse(a))
|
|
1047
|
+
}
|
|
938
1048
|
}
|
|
939
1049
|
|
|
940
|
-
|
|
1050
|
+
traverse(blockStatement)
|
|
1051
|
+
return results
|
|
1052
|
+
}
|
|
1053
|
+
|
|
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
|
+
)
|
|
941
1058
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
dataSourceIdentifier: string
|
|
945
|
-
renderProp: string
|
|
946
|
-
paginated: boolean
|
|
947
|
-
searchEnabled: boolean
|
|
1059
|
+
if (!renderSuccessAttr?.value?.expression) {
|
|
1060
|
+
return undefined
|
|
948
1061
|
}
|
|
949
|
-
const arrayMapperInfoMap = new Map<string, ArrayMapperInfo>()
|
|
950
1062
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
return
|
|
955
|
-
}
|
|
956
|
-
if (node.type === 'data-source-list' && node.content?.renderPropIdentifier) {
|
|
957
|
-
const dataSourceIdentifier = node.content.renderPropIdentifier
|
|
958
|
-
if (node.content.nodes?.success?.content?.children) {
|
|
959
|
-
node.content.nodes.success.content.children.forEach((child: any) => {
|
|
960
|
-
if (child.type === 'cms-list-repeater' && child.content?.renderPropIdentifier) {
|
|
961
|
-
const arrayMapperRenderProp = child.content.renderPropIdentifier
|
|
962
|
-
// Key by array mapper render prop (unique), not data source identifier
|
|
963
|
-
arrayMapperInfoMap.set(arrayMapperRenderProp, {
|
|
964
|
-
dataSourceIdentifier,
|
|
965
|
-
renderProp: arrayMapperRenderProp,
|
|
966
|
-
paginated: child.content.paginated || false,
|
|
967
|
-
searchEnabled: child.content.searchEnabled || false,
|
|
968
|
-
})
|
|
969
|
-
}
|
|
970
|
-
})
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
if (node.content?.children) {
|
|
974
|
-
node.content.children.forEach((child: any) => collectArrayMapperInfo(child))
|
|
975
|
-
}
|
|
976
|
-
if (node.children && Array.isArray(node.children)) {
|
|
977
|
-
node.children.forEach((child: any) => collectArrayMapperInfo(child))
|
|
978
|
-
}
|
|
1063
|
+
const findRepeater = (node: any): any => {
|
|
1064
|
+
if (!node) {
|
|
1065
|
+
return null
|
|
979
1066
|
}
|
|
980
|
-
collectArrayMapperInfo(uidlNode)
|
|
981
|
-
}
|
|
982
1067
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
parent: any
|
|
987
|
-
}
|
|
988
|
-
const dataProvidersWithParents: DataProviderWithParent[] = []
|
|
1068
|
+
if (node.type === 'JSXElement' && node.openingElement?.name?.name === 'Repeater') {
|
|
1069
|
+
return node
|
|
1070
|
+
}
|
|
989
1071
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
return
|
|
1072
|
+
if (node.body) {
|
|
1073
|
+
return findRepeater(node.body)
|
|
993
1074
|
}
|
|
994
1075
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
// Only recurse through JSX structure, not into embedded expressions or code
|
|
1004
|
-
if (node.type === 'ReturnStatement' && node.argument) {
|
|
1005
|
-
findDataProvidersAndParents(node.argument, parent)
|
|
1006
|
-
} else if (node.type === 'JSXElement' || node.type === 'JSXFragment') {
|
|
1007
|
-
// Recurse through JSX children
|
|
1008
|
-
if (node.children && Array.isArray(node.children)) {
|
|
1009
|
-
node.children.forEach((child: any) => findDataProvidersAndParents(child, node))
|
|
1010
|
-
}
|
|
1011
|
-
} else if (node.type === 'JSXExpressionContainer') {
|
|
1012
|
-
// For expression containers, continue but don't go into the expression itself
|
|
1013
|
-
if (
|
|
1014
|
-
node.expression &&
|
|
1015
|
-
(node.expression.type === 'JSXElement' || node.expression.type === 'JSXFragment')
|
|
1016
|
-
) {
|
|
1017
|
-
findDataProvidersAndParents(node.expression, parent)
|
|
1018
|
-
}
|
|
1019
|
-
} else if (node.type === 'BlockStatement') {
|
|
1020
|
-
// For block statements (function bodies), look through body array
|
|
1021
|
-
if (node.body && Array.isArray(node.body)) {
|
|
1022
|
-
node.body.forEach((stmt: any) => findDataProvidersAndParents(stmt, node))
|
|
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
|
+
}
|
|
1023
1082
|
}
|
|
1024
|
-
} else if (node.type === 'ConditionalExpression') {
|
|
1025
|
-
// Check both branches of conditional
|
|
1026
|
-
findDataProvidersAndParents(node.consequent, parent)
|
|
1027
|
-
findDataProvidersAndParents(node.alternate, parent)
|
|
1028
1083
|
}
|
|
1084
|
+
return null
|
|
1029
1085
|
}
|
|
1030
1086
|
|
|
1031
|
-
|
|
1087
|
+
const repeater = findRepeater(renderSuccessAttr.value.expression)
|
|
1088
|
+
if (!repeater) {
|
|
1089
|
+
return undefined
|
|
1090
|
+
}
|
|
1032
1091
|
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'name'
|
|
1037
|
-
)
|
|
1092
|
+
const renderItemAttr = repeater.openingElement.attributes.find(
|
|
1093
|
+
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'renderItem'
|
|
1094
|
+
)
|
|
1038
1095
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
)
|
|
1096
|
+
if (!renderItemAttr?.value?.expression?.params?.[0]?.name) {
|
|
1097
|
+
return undefined
|
|
1098
|
+
}
|
|
1043
1099
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
const renderFunc = renderSuccessAttr.value.expression
|
|
1047
|
-
if (renderFunc.type === 'ArrowFunctionExpression') {
|
|
1048
|
-
// Look for Repeater inside the render function
|
|
1049
|
-
const findRepeater = (node: any): any => {
|
|
1050
|
-
if (!node) {
|
|
1051
|
-
return null
|
|
1052
|
-
}
|
|
1053
|
-
if (node.type === 'JSXElement' && node.openingElement?.name?.name === 'Repeater') {
|
|
1054
|
-
return node
|
|
1055
|
-
}
|
|
1056
|
-
if (node.body) {
|
|
1057
|
-
return findRepeater(node.body)
|
|
1058
|
-
}
|
|
1059
|
-
if (node.children && Array.isArray(node.children)) {
|
|
1060
|
-
for (const child of node.children) {
|
|
1061
|
-
const result = findRepeater(child)
|
|
1062
|
-
if (result) {
|
|
1063
|
-
return result
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
if (node.type === 'JSXFragment' || node.type === 'JSXElement') {
|
|
1068
|
-
if (node.children && Array.isArray(node.children)) {
|
|
1069
|
-
for (const child of node.children) {
|
|
1070
|
-
const result = findRepeater(child)
|
|
1071
|
-
if (result) {
|
|
1072
|
-
return result
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
if (node.type === 'JSXExpressionContainer') {
|
|
1078
|
-
return findRepeater(node.expression)
|
|
1079
|
-
}
|
|
1080
|
-
if (node.expression) {
|
|
1081
|
-
return findRepeater(node.expression)
|
|
1082
|
-
}
|
|
1083
|
-
if (node.consequent) {
|
|
1084
|
-
const result = findRepeater(node.consequent)
|
|
1085
|
-
if (result) {
|
|
1086
|
-
return result
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
if (node.alternate) {
|
|
1090
|
-
return findRepeater(node.alternate)
|
|
1091
|
-
}
|
|
1092
|
-
return null
|
|
1093
|
-
}
|
|
1100
|
+
return renderItemAttr.value.expression.params[0].name
|
|
1101
|
+
}
|
|
1094
1102
|
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'renderItem'
|
|
1100
|
-
)
|
|
1101
|
-
if (renderItemAttr && renderItemAttr.value?.type === 'JSXExpressionContainer') {
|
|
1102
|
-
const renderItemFunc = renderItemAttr.value.expression
|
|
1103
|
-
if (
|
|
1104
|
-
renderItemFunc.type === 'ArrowFunctionExpression' &&
|
|
1105
|
-
renderItemFunc.params &&
|
|
1106
|
-
renderItemFunc.params.length > 0
|
|
1107
|
-
) {
|
|
1108
|
-
const param = renderItemFunc.params[0]
|
|
1109
|
-
if (param.type === 'Identifier') {
|
|
1110
|
-
arrayMapperRenderProp = param.name
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1103
|
+
function findAllSearchInputsInJSX(
|
|
1104
|
+
blockStatement: types.BlockStatement
|
|
1105
|
+
): Array<{ node: any; className: string }> {
|
|
1106
|
+
const results: Array<{ node: any; className: string }> = []
|
|
1117
1107
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
!nameAttr.value ||
|
|
1121
|
-
nameAttr.value.type !== 'JSXExpressionContainer' ||
|
|
1122
|
-
!arrayMapperRenderProp
|
|
1123
|
-
) {
|
|
1108
|
+
const traverse = (node: any): void => {
|
|
1109
|
+
if (!node) {
|
|
1124
1110
|
return
|
|
1125
1111
|
}
|
|
1126
1112
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
const findSearchAndPaginationInScope = (scopeNode: any, skipNode: any = null): boolean => {
|
|
1136
|
-
if (!scopeNode || !scopeNode.children || !Array.isArray(scopeNode.children)) {
|
|
1137
|
-
return false
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
let foundSomething = false
|
|
1141
|
-
|
|
1142
|
-
for (const child of scopeNode.children) {
|
|
1143
|
-
if (child === skipNode) {
|
|
1144
|
-
continue
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
if (child.type === 'JSXElement') {
|
|
1148
|
-
const childClassName = getClassName(child.openingElement?.attributes || [])
|
|
1149
|
-
const childElementName = child.openingElement?.name?.name
|
|
1150
|
-
|
|
1151
|
-
// Found pagination node
|
|
1152
|
-
if (childClassName && childClassName.includes('cms-pagination-node')) {
|
|
1153
|
-
const prevClass = findChildWithClass(child, 'previous')
|
|
1154
|
-
const nextClass = findChildWithClass(child, 'next')
|
|
1155
|
-
if (prevClass || nextClass) {
|
|
1156
|
-
paginationNodeInfo = {
|
|
1157
|
-
class: childClassName,
|
|
1158
|
-
prevClass,
|
|
1159
|
-
nextClass,
|
|
1160
|
-
}
|
|
1161
|
-
foundSomething = true
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
// Found search container - search for input inside it
|
|
1166
|
-
if (childClassName && childClassName.includes('data-source-search-node')) {
|
|
1167
|
-
if (child.children && Array.isArray(child.children)) {
|
|
1168
|
-
for (const searchChild of child.children) {
|
|
1169
|
-
if (searchChild.type === 'JSXElement') {
|
|
1170
|
-
const searchChildElementName = searchChild.openingElement?.name?.name
|
|
1171
|
-
const searchChildClassName = getClassName(
|
|
1172
|
-
searchChild.openingElement?.attributes || []
|
|
1173
|
-
)
|
|
1174
|
-
if (
|
|
1175
|
-
searchChildClassName &&
|
|
1176
|
-
searchChildClassName.includes('search-input') &&
|
|
1177
|
-
searchChildElementName === 'input'
|
|
1178
|
-
) {
|
|
1179
|
-
searchInputInfo = {
|
|
1180
|
-
class: searchChildClassName,
|
|
1181
|
-
jsx: searchChild,
|
|
1182
|
-
}
|
|
1183
|
-
foundSomething = true
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
// Also check if search input is a direct child
|
|
1191
|
-
if (
|
|
1192
|
-
childClassName &&
|
|
1193
|
-
childClassName.includes('search-input') &&
|
|
1194
|
-
childElementName === 'input'
|
|
1195
|
-
) {
|
|
1196
|
-
searchInputInfo = {
|
|
1197
|
-
class: childClassName,
|
|
1198
|
-
jsx: child,
|
|
1199
|
-
}
|
|
1200
|
-
foundSomething = true
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
// Stop searching if we found both or if we found what we're looking for
|
|
1204
|
-
if (foundSomething && (searchInputInfo || paginationNodeInfo)) {
|
|
1205
|
-
return true
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
// Only recurse if we haven't found what we're looking for yet
|
|
1209
|
-
if (!searchInputInfo || !paginationNodeInfo) {
|
|
1210
|
-
if (findSearchAndPaginationInScope(child, skipNode)) {
|
|
1211
|
-
return true
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
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 })
|
|
1215
1120
|
}
|
|
1216
|
-
|
|
1217
|
-
return foundSomething
|
|
1218
1121
|
}
|
|
1219
1122
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
break
|
|
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)
|
|
1229
1131
|
}
|
|
1230
|
-
currentScope = findParentNode(blockStatement, currentScope)
|
|
1231
|
-
depth++
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
// Record the DataProvider with its pagination/search info
|
|
1235
|
-
// Use array instead of Map to handle multiple DataProviders with same name
|
|
1236
|
-
dataProviderList.push({
|
|
1237
|
-
identifier: dataProviderIdentifier,
|
|
1238
|
-
dataProvider,
|
|
1239
|
-
arrayMapperRenderProp,
|
|
1240
|
-
paginationNode: paginationNodeInfo || undefined,
|
|
1241
|
-
searchInput: searchInputInfo || undefined,
|
|
1242
|
-
hasPagination: !!paginationNodeInfo,
|
|
1243
|
-
hasSearch: !!searchInputInfo,
|
|
1244
|
-
})
|
|
1245
|
-
})
|
|
1246
|
-
|
|
1247
|
-
// Categorize data providers
|
|
1248
|
-
const paginatedMappers: DetectedPagination[] = []
|
|
1249
|
-
const searchOnlyMappers: DetectedPagination[] = []
|
|
1250
|
-
const paginationOnlyMappers: DetectedPagination[] = []
|
|
1251
|
-
const plainMappers: DetectedPagination[] = []
|
|
1252
|
-
|
|
1253
|
-
dataProviderList.forEach((info) => {
|
|
1254
|
-
// Check UIDL flags for this array mapper
|
|
1255
|
-
const uidlInfo = info.arrayMapperRenderProp
|
|
1256
|
-
? arrayMapperInfoMap.get(info.arrayMapperRenderProp)
|
|
1257
|
-
: null
|
|
1258
|
-
|
|
1259
|
-
// Only process pagination/search if UIDL explicitly enables it
|
|
1260
|
-
const shouldHavePagination = uidlInfo?.paginated && info.hasPagination
|
|
1261
|
-
const shouldHaveSearch = uidlInfo?.searchEnabled && info.hasSearch
|
|
1262
|
-
|
|
1263
|
-
if (shouldHavePagination && shouldHaveSearch) {
|
|
1264
|
-
// Pagination + Search
|
|
1265
|
-
paginatedMappers.push({
|
|
1266
|
-
paginationNodeClass: info.paginationNode!.class,
|
|
1267
|
-
prevButtonClass: info.paginationNode!.prevClass,
|
|
1268
|
-
nextButtonClass: info.paginationNode!.nextClass,
|
|
1269
|
-
dataSourceIdentifier: info.identifier,
|
|
1270
|
-
dataProviderJSX: info.dataProvider,
|
|
1271
|
-
arrayMapperRenderProp: info.arrayMapperRenderProp,
|
|
1272
|
-
searchInputClass: info.searchInput?.class,
|
|
1273
|
-
searchInputJSX: info.searchInput?.jsx,
|
|
1274
|
-
})
|
|
1275
|
-
} else if (shouldHavePagination && !shouldHaveSearch) {
|
|
1276
|
-
// Pagination only
|
|
1277
|
-
paginationOnlyMappers.push({
|
|
1278
|
-
paginationNodeClass: info.paginationNode!.class,
|
|
1279
|
-
prevButtonClass: info.paginationNode!.prevClass,
|
|
1280
|
-
nextButtonClass: info.paginationNode!.nextClass,
|
|
1281
|
-
dataSourceIdentifier: info.identifier,
|
|
1282
|
-
dataProviderJSX: info.dataProvider,
|
|
1283
|
-
arrayMapperRenderProp: info.arrayMapperRenderProp,
|
|
1284
|
-
searchInputClass: undefined,
|
|
1285
|
-
searchInputJSX: undefined,
|
|
1286
|
-
})
|
|
1287
|
-
} else if (!shouldHavePagination && shouldHaveSearch) {
|
|
1288
|
-
// Search only
|
|
1289
|
-
searchOnlyMappers.push({
|
|
1290
|
-
paginationNodeClass: '',
|
|
1291
|
-
prevButtonClass: null,
|
|
1292
|
-
nextButtonClass: null,
|
|
1293
|
-
dataSourceIdentifier: info.identifier,
|
|
1294
|
-
dataProviderJSX: info.dataProvider,
|
|
1295
|
-
arrayMapperRenderProp: info.arrayMapperRenderProp,
|
|
1296
|
-
searchInputClass: info.searchInput?.class,
|
|
1297
|
-
searchInputJSX: info.searchInput?.jsx,
|
|
1298
|
-
})
|
|
1299
|
-
} else {
|
|
1300
|
-
// Plain (no pagination, no search) - UIDL doesn't enable it or no controls found
|
|
1301
|
-
plainMappers.push({
|
|
1302
|
-
paginationNodeClass: '',
|
|
1303
|
-
prevButtonClass: null,
|
|
1304
|
-
nextButtonClass: null,
|
|
1305
|
-
dataSourceIdentifier: info.identifier,
|
|
1306
|
-
dataProviderJSX: info.dataProvider,
|
|
1307
|
-
arrayMapperRenderProp: info.arrayMapperRenderProp,
|
|
1308
|
-
searchInputClass: undefined,
|
|
1309
|
-
searchInputJSX: undefined,
|
|
1310
|
-
})
|
|
1311
1132
|
}
|
|
1312
|
-
|
|
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
|
+
}
|
|
1313
1146
|
|
|
1314
|
-
|
|
1147
|
+
traverse(blockStatement)
|
|
1148
|
+
return results
|
|
1315
1149
|
}
|
|
1316
1150
|
|
|
1317
|
-
function
|
|
1318
|
-
blockStatement: types.BlockStatement
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
queryColumnsMap: Map<string, string[]>,
|
|
1322
|
-
dependencies: any
|
|
1323
|
-
): void {
|
|
1324
|
-
const searchOnlyStates: types.Statement[] = []
|
|
1325
|
-
const searchOnlyEffects: types.Statement[] = []
|
|
1326
|
-
|
|
1327
|
-
searchOnlyDataSources.forEach((detected, index) => {
|
|
1328
|
-
const searchId = `search_${index}`
|
|
1329
|
-
const searchQueryVar = `${searchId}_query`
|
|
1330
|
-
const setSearchQueryVar = `set${
|
|
1331
|
-
searchQueryVar.charAt(0).toUpperCase() + searchQueryVar.slice(1)
|
|
1332
|
-
}`
|
|
1333
|
-
const debouncedSearchQueryVar = `debounced${
|
|
1334
|
-
searchQueryVar.charAt(0).toUpperCase() + searchQueryVar.slice(1)
|
|
1335
|
-
}`
|
|
1336
|
-
const setDebouncedSearchQueryVar = `set${
|
|
1337
|
-
debouncedSearchQueryVar.charAt(0).toUpperCase() + debouncedSearchQueryVar.slice(1)
|
|
1338
|
-
}`
|
|
1339
|
-
const skipDebounceOnMountRefVar = `skipDebounceOnMount${searchId}`
|
|
1340
|
-
|
|
1341
|
-
const searchConfig = searchConfigMap.get(detected.dataSourceIdentifier)
|
|
1342
|
-
const searchDebounce = searchConfig?.searchDebounce || 300
|
|
1343
|
-
const queryColumns = queryColumnsMap.get(detected.dataSourceIdentifier)
|
|
1344
|
-
|
|
1345
|
-
// Add skip ref
|
|
1346
|
-
const skipRefAST = types.variableDeclaration('const', [
|
|
1347
|
-
types.variableDeclarator(
|
|
1348
|
-
types.identifier(skipDebounceOnMountRefVar),
|
|
1349
|
-
types.callExpression(types.identifier('useRef'), [types.booleanLiteral(true)])
|
|
1350
|
-
),
|
|
1351
|
-
])
|
|
1352
|
-
searchOnlyStates.push(skipRefAST)
|
|
1353
|
-
|
|
1354
|
-
// Add debounced search state
|
|
1355
|
-
const debouncedSearchStateAST = types.variableDeclaration('const', [
|
|
1356
|
-
types.variableDeclarator(
|
|
1357
|
-
types.arrayPattern([
|
|
1358
|
-
types.identifier(debouncedSearchQueryVar),
|
|
1359
|
-
types.identifier(setDebouncedSearchQueryVar),
|
|
1360
|
-
]),
|
|
1361
|
-
types.callExpression(types.identifier('useState'), [types.stringLiteral('')])
|
|
1362
|
-
),
|
|
1363
|
-
])
|
|
1364
|
-
searchOnlyStates.push(debouncedSearchStateAST)
|
|
1365
|
-
|
|
1366
|
-
// Add search query state
|
|
1367
|
-
const searchStateAST = types.variableDeclaration('const', [
|
|
1368
|
-
types.variableDeclarator(
|
|
1369
|
-
types.arrayPattern([types.identifier(searchQueryVar), types.identifier(setSearchQueryVar)]),
|
|
1370
|
-
types.callExpression(types.identifier('useState'), [types.stringLiteral('')])
|
|
1371
|
-
),
|
|
1372
|
-
])
|
|
1373
|
-
searchOnlyStates.push(searchStateAST)
|
|
1151
|
+
function findAllPaginationNodesInJSX(
|
|
1152
|
+
blockStatement: types.BlockStatement
|
|
1153
|
+
): Array<{ node: any; className: string }> {
|
|
1154
|
+
const results: Array<{ node: any; className: string }> = []
|
|
1374
1155
|
|
|
1375
|
-
|
|
1376
|
-
if (!
|
|
1377
|
-
|
|
1378
|
-
type: 'library',
|
|
1379
|
-
path: 'react',
|
|
1380
|
-
version: '',
|
|
1381
|
-
meta: {
|
|
1382
|
-
namedImport: true,
|
|
1383
|
-
},
|
|
1384
|
-
}
|
|
1156
|
+
const traverse = (node: any): void => {
|
|
1157
|
+
if (!node) {
|
|
1158
|
+
return
|
|
1385
1159
|
}
|
|
1386
1160
|
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
types.identifier('current')
|
|
1396
|
-
),
|
|
1397
|
-
types.blockStatement([
|
|
1398
|
-
types.expressionStatement(
|
|
1399
|
-
types.assignmentExpression(
|
|
1400
|
-
'=',
|
|
1401
|
-
types.memberExpression(
|
|
1402
|
-
types.identifier(skipDebounceOnMountRefVar),
|
|
1403
|
-
types.identifier('current')
|
|
1404
|
-
),
|
|
1405
|
-
types.booleanLiteral(false)
|
|
1406
|
-
)
|
|
1407
|
-
),
|
|
1408
|
-
types.returnStatement(),
|
|
1409
|
-
])
|
|
1410
|
-
),
|
|
1411
|
-
types.variableDeclaration('const', [
|
|
1412
|
-
types.variableDeclarator(
|
|
1413
|
-
types.identifier('timer'),
|
|
1414
|
-
types.callExpression(types.identifier('setTimeout'), [
|
|
1415
|
-
types.arrowFunctionExpression(
|
|
1416
|
-
[],
|
|
1417
|
-
types.blockStatement([
|
|
1418
|
-
types.expressionStatement(
|
|
1419
|
-
types.callExpression(types.identifier(setDebouncedSearchQueryVar), [
|
|
1420
|
-
types.identifier(searchQueryVar),
|
|
1421
|
-
])
|
|
1422
|
-
),
|
|
1423
|
-
])
|
|
1424
|
-
),
|
|
1425
|
-
types.numericLiteral(searchDebounce),
|
|
1426
|
-
])
|
|
1427
|
-
),
|
|
1428
|
-
]),
|
|
1429
|
-
types.returnStatement(
|
|
1430
|
-
types.arrowFunctionExpression(
|
|
1431
|
-
[],
|
|
1432
|
-
types.callExpression(types.identifier('clearTimeout'), [types.identifier('timer')])
|
|
1433
|
-
)
|
|
1434
|
-
),
|
|
1435
|
-
])
|
|
1436
|
-
),
|
|
1437
|
-
types.arrayExpression([types.identifier(searchQueryVar)]),
|
|
1438
|
-
])
|
|
1439
|
-
)
|
|
1440
|
-
searchOnlyEffects.push(debounceEffect)
|
|
1441
|
-
|
|
1442
|
-
// Modify DataProvider to include search params (even without queryColumns)
|
|
1443
|
-
if (detected.dataProviderJSX) {
|
|
1444
|
-
addSearchParamsToDataProvider(detected.dataProviderJSX, {
|
|
1445
|
-
debouncedSearchQueryVar,
|
|
1446
|
-
queryColumns: queryColumns || [],
|
|
1447
|
-
})
|
|
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
|
+
}
|
|
1448
1169
|
}
|
|
1449
1170
|
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
addSearchInputHandlers(detected.searchInputJSX, {
|
|
1453
|
-
searchQueryVar,
|
|
1454
|
-
setSearchQueryVar,
|
|
1455
|
-
searchEnabled: true,
|
|
1456
|
-
} as any)
|
|
1171
|
+
if (node.children && Array.isArray(node.children)) {
|
|
1172
|
+
node.children.forEach((c: any) => traverse(c))
|
|
1457
1173
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
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
|
+
}
|
|
1470
1194
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
blockStatement.body.splice(finalInsertIndex, 0, effect)
|
|
1474
|
-
})
|
|
1195
|
+
traverse(blockStatement)
|
|
1196
|
+
return results
|
|
1475
1197
|
}
|
|
1476
1198
|
|
|
1477
|
-
function
|
|
1478
|
-
|
|
1479
|
-
|
|
1199
|
+
function updateDataProviderForPaginatedSearch(
|
|
1200
|
+
dp: any,
|
|
1201
|
+
usage: DataSourceUsage,
|
|
1202
|
+
vars: ReturnType<typeof getStateVarsForUsage>,
|
|
1203
|
+
fileName: string
|
|
1480
1204
|
): void {
|
|
1481
|
-
|
|
1482
|
-
return
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
const { debouncedSearchQueryVar, queryColumns } = config
|
|
1205
|
+
const attrs = dp.openingElement.attributes
|
|
1486
1206
|
|
|
1487
|
-
//
|
|
1488
|
-
|
|
1489
|
-
(attr: any) =>
|
|
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
|
+
)
|
|
1490
1213
|
)
|
|
1491
1214
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
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')
|
|
1499
1227
|
)
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
)
|
|
1510
|
-
)
|
|
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)))]
|
|
1511
1237
|
)
|
|
1512
|
-
|
|
1238
|
+
)
|
|
1239
|
+
)
|
|
1240
|
+
}
|
|
1513
1241
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
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
|
+
])
|
|
1250
|
+
)
|
|
1251
|
+
)
|
|
1252
|
+
)
|
|
1519
1253
|
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
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')
|
|
1284
|
+
)
|
|
1285
|
+
)
|
|
1286
|
+
)
|
|
1287
|
+
)
|
|
1523
1288
|
|
|
1524
|
-
//
|
|
1525
|
-
|
|
1526
|
-
(
|
|
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
|
+
)
|
|
1527
1316
|
)
|
|
1528
1317
|
|
|
1529
|
-
|
|
1530
|
-
|
|
1318
|
+
// Add fetchData
|
|
1319
|
+
dp.openingElement.attributes.push(createFetchDataAttribute(fileName))
|
|
1531
1320
|
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
types.
|
|
1321
|
+
// Add persistDataDuringLoading
|
|
1322
|
+
dp.openingElement.attributes.push(
|
|
1323
|
+
types.jsxAttribute(
|
|
1324
|
+
types.jsxIdentifier('persistDataDuringLoading'),
|
|
1325
|
+
types.jsxExpressionContainer(types.booleanLiteral(true))
|
|
1537
1326
|
)
|
|
1538
|
-
|
|
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
|
|
1337
|
+
|
|
1338
|
+
dp.openingElement.attributes = attrs.filter(
|
|
1339
|
+
(attr: any) =>
|
|
1340
|
+
!['params', 'key', 'initialData', 'fetchData', 'persistDataDuringLoading'].includes(
|
|
1341
|
+
attr.name?.name
|
|
1342
|
+
)
|
|
1343
|
+
)
|
|
1539
1344
|
|
|
1540
|
-
//
|
|
1541
|
-
|
|
1542
|
-
(
|
|
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
|
+
)
|
|
1364
|
+
)
|
|
1543
1365
|
)
|
|
1544
1366
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
types.
|
|
1549
|
-
|
|
1550
|
-
|
|
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
|
+
)
|
|
1551
1388
|
)
|
|
1552
1389
|
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
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
|
+
)
|
|
1556
1407
|
)
|
|
1557
1408
|
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
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
|
+
)
|
|
1564
1418
|
}
|
|
1565
1419
|
|
|
1566
|
-
function
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1420
|
+
function updateDataProviderForSearchOnly(
|
|
1421
|
+
dp: any,
|
|
1422
|
+
usage: DataSourceUsage,
|
|
1423
|
+
vars: ReturnType<typeof getStateVarsForUsage>,
|
|
1424
|
+
fileName: string
|
|
1570
1425
|
): void {
|
|
1571
|
-
|
|
1572
|
-
return
|
|
1573
|
-
}
|
|
1426
|
+
const attrs = dp.openingElement.attributes
|
|
1574
1427
|
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1428
|
+
dp.openingElement.attributes = attrs.filter(
|
|
1429
|
+
(attr: any) =>
|
|
1430
|
+
!['params', 'key', 'initialData', 'fetchData', 'persistDataDuringLoading'].includes(
|
|
1431
|
+
attr.name?.name
|
|
1432
|
+
)
|
|
1433
|
+
)
|
|
1579
1434
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
),
|
|
1587
|
-
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(
|
|
1588
1441
|
types.objectProperty(
|
|
1589
|
-
types.identifier('
|
|
1590
|
-
types.
|
|
1591
|
-
types.identifier(
|
|
1592
|
-
types.
|
|
1593
|
-
)
|
|
1594
|
-
),
|
|
1595
|
-
]
|
|
1596
|
-
|
|
1597
|
-
if (info.queryColumns && info.queryColumns.length > 0) {
|
|
1598
|
-
paramsProperties.push(
|
|
1599
|
-
types.objectProperty(
|
|
1600
|
-
types.identifier('queryColumns'),
|
|
1601
|
-
types.callExpression(
|
|
1602
|
-
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
1603
|
-
[types.arrayExpression(info.queryColumns.map((col) => types.stringLiteral(col)))]
|
|
1604
|
-
)
|
|
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)))]
|
|
1605
1446
|
)
|
|
1606
1447
|
)
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
// Single dependency: the combined state object
|
|
1610
|
-
dependencies = [types.identifier(combinedStateVar)]
|
|
1611
|
-
} else {
|
|
1612
|
-
// Pagination only (no search)
|
|
1613
|
-
paramsProperties = [
|
|
1614
|
-
types.objectProperty(types.identifier('page'), types.identifier(info.pageStateVar)),
|
|
1615
|
-
types.objectProperty(types.identifier('perPage'), types.numericLiteral(info.perPage)),
|
|
1616
|
-
]
|
|
1617
|
-
|
|
1618
|
-
dependencies = [types.identifier(info.pageStateVar)]
|
|
1448
|
+
)
|
|
1619
1449
|
}
|
|
1620
1450
|
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
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
|
+
)
|
|
1626
1462
|
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
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
|
+
)
|
|
1630
1480
|
)
|
|
1631
1481
|
|
|
1632
|
-
|
|
1633
|
-
|
|
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
|
+
)
|
|
1634
1496
|
)
|
|
1635
1497
|
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
dataProviderJSX.openingElement.attributes[index] = paramsAttr
|
|
1639
|
-
} else {
|
|
1640
|
-
dataProviderJSX.openingElement.attributes.push(paramsAttr)
|
|
1641
|
-
}
|
|
1498
|
+
// Add fetchData
|
|
1499
|
+
dp.openingElement.attributes.push(createFetchDataAttribute(fileName))
|
|
1642
1500
|
|
|
1643
|
-
|
|
1644
|
-
(
|
|
1501
|
+
dp.openingElement.attributes.push(
|
|
1502
|
+
types.jsxAttribute(
|
|
1503
|
+
types.jsxIdentifier('persistDataDuringLoading'),
|
|
1504
|
+
types.jsxExpressionContainer(types.booleanLiteral(true))
|
|
1505
|
+
)
|
|
1645
1506
|
)
|
|
1507
|
+
}
|
|
1646
1508
|
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
const paginatedPropName = `${info.dataSourceIdentifier}_pg_${paginationIndex}`
|
|
1509
|
+
function updateDataProviderForPlain(dp: any, vars: ReturnType<typeof getStateVarsForUsage>): void {
|
|
1510
|
+
const attrs = dp.openingElement.attributes
|
|
1650
1511
|
|
|
1651
|
-
|
|
1652
|
-
|
|
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
|
+
)
|
|
1653
1516
|
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
'===',
|
|
1659
|
-
types.memberExpression(types.identifier(combinedStateVar), types.identifier('page')),
|
|
1660
|
-
types.numericLiteral(1)
|
|
1661
|
-
),
|
|
1662
|
-
types.unaryExpression(
|
|
1663
|
-
'!',
|
|
1664
|
-
types.memberExpression(
|
|
1665
|
-
types.identifier(combinedStateVar),
|
|
1666
|
-
types.identifier('debouncedQuery')
|
|
1667
|
-
),
|
|
1668
|
-
true
|
|
1669
|
-
)
|
|
1670
|
-
)
|
|
1671
|
-
} else {
|
|
1672
|
-
condition = types.binaryExpression(
|
|
1673
|
-
'===',
|
|
1674
|
-
types.identifier(info.pageStateVar),
|
|
1675
|
-
types.numericLiteral(1)
|
|
1676
|
-
)
|
|
1677
|
-
}
|
|
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
|
+
)
|
|
1678
1521
|
|
|
1679
|
-
|
|
1680
|
-
|
|
1522
|
+
if (existingInitialData) {
|
|
1523
|
+
existingInitialData.value = types.jsxExpressionContainer(
|
|
1681
1524
|
types.optionalMemberExpression(
|
|
1682
1525
|
types.identifier('props'),
|
|
1683
|
-
types.identifier(
|
|
1526
|
+
types.identifier(vars.propsPrefix),
|
|
1684
1527
|
false,
|
|
1685
1528
|
true
|
|
1686
|
-
)
|
|
1687
|
-
types.identifier('undefined')
|
|
1529
|
+
)
|
|
1688
1530
|
)
|
|
1689
1531
|
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
function stabilizeDataProviderWithoutRepeater(dp: any): void {
|
|
1535
|
+
const attrs = dp.openingElement.attributes
|
|
1690
1536
|
|
|
1691
|
-
|
|
1692
|
-
|
|
1537
|
+
// Find the params attribute
|
|
1538
|
+
const paramsAttrIndex = attrs.findIndex(
|
|
1539
|
+
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'params'
|
|
1693
1540
|
)
|
|
1694
1541
|
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
if (info.searchEnabled && combinedStateVar) {
|
|
1698
|
-
keyExpression = types.templateLiteral(
|
|
1699
|
-
[
|
|
1700
|
-
types.templateElement({
|
|
1701
|
-
raw: `${info.dataSourceIdentifier}-page-`,
|
|
1702
|
-
cooked: `${info.dataSourceIdentifier}-page-`,
|
|
1703
|
-
}),
|
|
1704
|
-
types.templateElement({ raw: '-search-', cooked: '-search-' }),
|
|
1705
|
-
types.templateElement({ raw: '', cooked: '' }),
|
|
1706
|
-
],
|
|
1707
|
-
[
|
|
1708
|
-
types.memberExpression(types.identifier(combinedStateVar), types.identifier('page')),
|
|
1709
|
-
types.memberExpression(
|
|
1710
|
-
types.identifier(combinedStateVar),
|
|
1711
|
-
types.identifier('debouncedQuery')
|
|
1712
|
-
),
|
|
1713
|
-
]
|
|
1714
|
-
)
|
|
1715
|
-
} else {
|
|
1716
|
-
keyExpression = types.templateLiteral(
|
|
1717
|
-
[
|
|
1718
|
-
types.templateElement({
|
|
1719
|
-
raw: `${info.dataSourceIdentifier}-`,
|
|
1720
|
-
cooked: `${info.dataSourceIdentifier}-`,
|
|
1721
|
-
}),
|
|
1722
|
-
types.templateElement({ raw: '', cooked: '' }),
|
|
1723
|
-
],
|
|
1724
|
-
[types.identifier(info.pageStateVar)]
|
|
1725
|
-
)
|
|
1542
|
+
if (paramsAttrIndex === -1) {
|
|
1543
|
+
return
|
|
1726
1544
|
}
|
|
1727
1545
|
|
|
1728
|
-
const
|
|
1729
|
-
types.jsxIdentifier('key'),
|
|
1730
|
-
types.jsxExpressionContainer(keyExpression)
|
|
1731
|
-
)
|
|
1546
|
+
const paramsAttr = attrs[paramsAttrIndex] as types.JSXAttribute
|
|
1732
1547
|
|
|
1733
|
-
if
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
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
|
|
1738
1555
|
}
|
|
1739
1556
|
|
|
1740
|
-
//
|
|
1741
|
-
|
|
1742
|
-
const resourceDefAttr = dataProviderJSX.openingElement.attributes.find(
|
|
1743
|
-
(attr: any) => attr.type === 'JSXAttribute' && attr.name.name === 'resourceDefinition'
|
|
1744
|
-
)
|
|
1557
|
+
// Get the current params value expression
|
|
1558
|
+
let paramsExpression: types.Expression | null = null
|
|
1745
1559
|
|
|
1746
|
-
if (
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
const dataSourceIdProp = (resourceDef.properties as any[]).find(
|
|
1750
|
-
(p: any) => p.type === 'ObjectProperty' && p.key.value === 'dataSourceId'
|
|
1751
|
-
)
|
|
1752
|
-
const tableNameProp = (resourceDef.properties as any[]).find(
|
|
1753
|
-
(p: any) => p.type === 'ObjectProperty' && p.key.value === 'tableName'
|
|
1754
|
-
)
|
|
1755
|
-
const dataSourceTypeProp = (resourceDef.properties as any[]).find(
|
|
1756
|
-
(p: any) => p.type === 'ObjectProperty' && p.key.value === 'dataSourceType'
|
|
1757
|
-
)
|
|
1560
|
+
if (paramsAttr.value?.type === 'JSXExpressionContainer') {
|
|
1561
|
+
paramsExpression = paramsAttr.value.expression as types.Expression
|
|
1562
|
+
}
|
|
1758
1563
|
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
const dataSourceType = dataSourceTypeProp.value.value
|
|
1763
|
-
const fileName = `${dataSourceType}-${tableName}-${dataSourceId.substring(0, 8)}`
|
|
1564
|
+
if (!paramsExpression) {
|
|
1565
|
+
return
|
|
1566
|
+
}
|
|
1764
1567
|
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
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
|
+
])
|
|
1787
1617
|
),
|
|
1788
|
-
types.objectExpression([
|
|
1789
|
-
types.objectProperty(
|
|
1790
|
-
types.identifier('headers'),
|
|
1791
|
-
types.objectExpression([
|
|
1792
|
-
types.objectProperty(
|
|
1793
|
-
types.stringLiteral('Content-Type'),
|
|
1794
|
-
types.stringLiteral('application/json')
|
|
1795
|
-
),
|
|
1796
|
-
])
|
|
1797
|
-
),
|
|
1798
|
-
]),
|
|
1799
1618
|
]),
|
|
1800
|
-
|
|
1801
|
-
)
|
|
1802
|
-
[
|
|
1803
|
-
types.arrowFunctionExpression(
|
|
1804
|
-
[types.identifier('res')],
|
|
1805
|
-
types.callExpression(
|
|
1806
|
-
types.memberExpression(types.identifier('res'), types.identifier('json')),
|
|
1807
|
-
[]
|
|
1808
|
-
)
|
|
1809
|
-
),
|
|
1810
|
-
]
|
|
1619
|
+
]),
|
|
1620
|
+
types.identifier('then')
|
|
1811
1621
|
),
|
|
1812
|
-
|
|
1622
|
+
[
|
|
1623
|
+
types.arrowFunctionExpression(
|
|
1624
|
+
[types.identifier('res')],
|
|
1625
|
+
types.callExpression(
|
|
1626
|
+
types.memberExpression(types.identifier('res'), types.identifier('json')),
|
|
1627
|
+
[]
|
|
1628
|
+
)
|
|
1629
|
+
),
|
|
1630
|
+
]
|
|
1813
1631
|
),
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
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
|
+
)
|
|
1651
|
+
}
|
|
1829
1652
|
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
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
|
+
)
|
|
1834
1658
|
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
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
|
+
])
|
|
1838
1672
|
)
|
|
1673
|
+
)
|
|
1674
|
+
)
|
|
1675
|
+
)
|
|
1839
1676
|
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
}
|
|
1677
|
+
// Add value
|
|
1678
|
+
inputNode.openingElement.attributes.push(
|
|
1679
|
+
types.jsxAttribute(
|
|
1680
|
+
types.jsxIdentifier('value'),
|
|
1681
|
+
types.jsxExpressionContainer(types.identifier(vars.searchQueryVar))
|
|
1682
|
+
)
|
|
1683
|
+
)
|
|
1848
1684
|
}
|
|
1849
1685
|
|
|
1850
|
-
function
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
if (
|
|
1858
|
-
return
|
|
1686
|
+
function wirePaginationButtons(
|
|
1687
|
+
paginationNode: any,
|
|
1688
|
+
usage: DataSourceUsage,
|
|
1689
|
+
vars: ReturnType<typeof getStateVarsForUsage>
|
|
1690
|
+
): void {
|
|
1691
|
+
// Find prev and next buttons
|
|
1692
|
+
const findButton = (node: any, direction: 'previous' | 'next'): any => {
|
|
1693
|
+
if (!node) {
|
|
1694
|
+
return null
|
|
1859
1695
|
}
|
|
1860
|
-
}
|
|
1861
1696
|
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1697
|
+
if (node.type === 'JSXElement') {
|
|
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
|
|
1867
1704
|
}
|
|
1868
1705
|
}
|
|
1869
|
-
}
|
|
1870
1706
|
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
for (const item of value) {
|
|
1875
|
-
const found = findChildWithClass(item, classSubstring)
|
|
1876
|
-
if (found) {
|
|
1877
|
-
return found
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1880
|
-
} else if (typeof value === 'object') {
|
|
1881
|
-
const found = findChildWithClass(value, classSubstring)
|
|
1707
|
+
if (node.children && Array.isArray(node.children)) {
|
|
1708
|
+
for (const c of node.children) {
|
|
1709
|
+
const found = findButton(c, direction)
|
|
1882
1710
|
if (found) {
|
|
1883
1711
|
return found
|
|
1884
1712
|
}
|
|
1885
1713
|
}
|
|
1886
1714
|
}
|
|
1715
|
+
return null
|
|
1887
1716
|
}
|
|
1888
1717
|
|
|
1889
|
-
|
|
1890
|
-
|
|
1718
|
+
const prevButton = findButton(paginationNode, 'previous')
|
|
1719
|
+
const nextButton = findButton(paginationNode, 'next')
|
|
1891
1720
|
|
|
1892
|
-
|
|
1893
|
-
blockStatement: types.BlockStatement,
|
|
1894
|
-
detectedPaginations: DetectedPagination[],
|
|
1895
|
-
paginationInfos: ArrayMapperPaginationInfo[]
|
|
1896
|
-
): void {
|
|
1897
|
-
const modifiedButtons = new Set<any>()
|
|
1721
|
+
const isCombinedState = usage.category === 'paginated+search'
|
|
1898
1722
|
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1723
|
+
if (prevButton) {
|
|
1724
|
+
// Change to button element
|
|
1725
|
+
prevButton.openingElement.name.name = 'button'
|
|
1726
|
+
if (prevButton.closingElement) {
|
|
1727
|
+
prevButton.closingElement.name.name = 'button'
|
|
1902
1728
|
}
|
|
1903
1729
|
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
for (let index = 0; index < detectedPaginations.length; index++) {
|
|
1911
|
-
const detected = detectedPaginations[index]
|
|
1912
|
-
const info = paginationInfos[index]
|
|
1913
|
-
|
|
1914
|
-
if (!info) {
|
|
1915
|
-
continue
|
|
1916
|
-
}
|
|
1917
|
-
|
|
1918
|
-
if (className === detected.prevButtonClass) {
|
|
1919
|
-
convertToButton(node, info, 'prev')
|
|
1920
|
-
modifiedButtons.add(node)
|
|
1921
|
-
break
|
|
1922
|
-
} else if (className === detected.nextButtonClass) {
|
|
1923
|
-
convertToButton(node, info, 'next')
|
|
1924
|
-
modifiedButtons.add(node)
|
|
1925
|
-
break
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
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
|
+
)
|
|
1930
1736
|
}
|
|
1931
1737
|
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
} else if (typeof value === 'object') {
|
|
1937
|
-
modifyNode(value)
|
|
1938
|
-
}
|
|
1939
|
-
})
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1738
|
+
// Add onClick
|
|
1739
|
+
prevButton.openingElement.attributes = prevButton.openingElement.attributes.filter(
|
|
1740
|
+
(a: any) => a.name?.name !== 'onClick'
|
|
1741
|
+
)
|
|
1942
1742
|
|
|
1943
|
-
|
|
1944
|
-
|
|
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)
|
|
1768
|
+
),
|
|
1769
|
+
]
|
|
1770
|
+
)
|
|
1771
|
+
),
|
|
1772
|
+
])
|
|
1773
|
+
),
|
|
1774
|
+
])
|
|
1775
|
+
)
|
|
1776
|
+
)
|
|
1777
|
+
)
|
|
1778
|
+
)
|
|
1945
1779
|
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
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')
|
|
1793
|
+
),
|
|
1794
|
+
types.numericLiteral(1)
|
|
1795
|
+
)
|
|
1796
|
+
)
|
|
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
|
+
)
|
|
1823
|
+
)
|
|
1824
|
+
)
|
|
1825
|
+
)
|
|
1952
1826
|
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
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
|
+
)
|
|
1839
|
+
)
|
|
1840
|
+
)
|
|
1841
|
+
)
|
|
1956
1842
|
}
|
|
1843
|
+
}
|
|
1957
1844
|
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
if (className && !modifiedInputs.has(node)) {
|
|
1964
|
-
for (let index = 0; index < detectedPaginations.length; index++) {
|
|
1965
|
-
const detected = detectedPaginations[index]
|
|
1966
|
-
const info = paginationInfos[index]
|
|
1967
|
-
|
|
1968
|
-
if (!info || !info.searchEnabled) {
|
|
1969
|
-
continue
|
|
1970
|
-
}
|
|
1971
|
-
|
|
1972
|
-
if (className === detected.searchInputClass) {
|
|
1973
|
-
addSearchInputHandlers(node, info)
|
|
1974
|
-
modifiedInputs.add(node)
|
|
1975
|
-
break
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1845
|
+
if (nextButton) {
|
|
1846
|
+
nextButton.openingElement.name.name = 'button'
|
|
1847
|
+
if (nextButton.closingElement) {
|
|
1848
|
+
nextButton.closingElement.name.name = 'button'
|
|
1980
1849
|
}
|
|
1981
1850
|
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
modifyNode(value)
|
|
1988
|
-
}
|
|
1989
|
-
})
|
|
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
|
+
)
|
|
1990
1856
|
}
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
modifyNode(blockStatement)
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
|
-
function addSearchInputHandlers(jsxElement: any, info: ArrayMapperPaginationInfo): void {
|
|
1997
|
-
if (!info.searchQueryVar || !info.setSearchQueryVar) {
|
|
1998
|
-
return
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
|
-
const openingElement = jsxElement.openingElement
|
|
2002
|
-
|
|
2003
|
-
removeAttribute(openingElement.attributes, 'onChange')
|
|
2004
|
-
removeAttribute(openingElement.attributes, 'value')
|
|
2005
|
-
|
|
2006
|
-
const onChangeHandler = types.arrowFunctionExpression(
|
|
2007
|
-
[types.identifier('e')],
|
|
2008
|
-
types.callExpression(types.identifier(info.setSearchQueryVar!), [
|
|
2009
|
-
types.memberExpression(
|
|
2010
|
-
types.memberExpression(types.identifier('e'), types.identifier('target')),
|
|
2011
|
-
types.identifier('value')
|
|
2012
|
-
),
|
|
2013
|
-
])
|
|
2014
|
-
)
|
|
2015
|
-
|
|
2016
|
-
openingElement.attributes.push(
|
|
2017
|
-
types.jsxAttribute(
|
|
2018
|
-
types.jsxIdentifier('onChange'),
|
|
2019
|
-
types.jsxExpressionContainer(onChangeHandler)
|
|
2020
|
-
)
|
|
2021
|
-
)
|
|
2022
1857
|
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
types.jsxIdentifier('value'),
|
|
2026
|
-
types.jsxExpressionContainer(types.identifier(info.searchQueryVar!))
|
|
1858
|
+
nextButton.openingElement.attributes = nextButton.openingElement.attributes.filter(
|
|
1859
|
+
(a: any) => a.name?.name !== 'onClick'
|
|
2027
1860
|
)
|
|
2028
|
-
)
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2031
|
-
function convertToButton(
|
|
2032
|
-
jsxElement: any,
|
|
2033
|
-
info: ArrayMapperPaginationInfo,
|
|
2034
|
-
buttonType: 'prev' | 'next'
|
|
2035
|
-
): void {
|
|
2036
|
-
const openingElement = jsxElement.openingElement
|
|
2037
|
-
|
|
2038
|
-
if (openingElement.name.type === 'JSXIdentifier') {
|
|
2039
|
-
openingElement.name.name = 'button'
|
|
2040
|
-
}
|
|
2041
|
-
if (jsxElement.closingElement && jsxElement.closingElement.name.type === 'JSXIdentifier') {
|
|
2042
|
-
jsxElement.closingElement.name.name = 'button'
|
|
2043
|
-
}
|
|
2044
1861
|
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
types.numericLiteral(1),
|
|
2070
|
-
types.binaryExpression(
|
|
2071
|
-
'-',
|
|
2072
|
-
types.memberExpression(
|
|
2073
|
-
types.identifier('state'),
|
|
2074
|
-
types.identifier('page')
|
|
2075
|
-
),
|
|
2076
|
-
types.numericLiteral(1)
|
|
2077
|
-
),
|
|
2078
|
-
]
|
|
2079
|
-
)
|
|
2080
|
-
),
|
|
2081
|
-
])
|
|
2082
|
-
),
|
|
2083
|
-
])
|
|
2084
|
-
)
|
|
2085
|
-
: types.arrowFunctionExpression(
|
|
2086
|
-
[],
|
|
2087
|
-
types.callExpression(types.identifier(setCombinedStateVar), [
|
|
2088
|
-
types.arrowFunctionExpression(
|
|
2089
|
-
[types.identifier('state')],
|
|
2090
|
-
types.objectExpression([
|
|
2091
|
-
types.spreadElement(types.identifier('state')),
|
|
2092
|
-
types.objectProperty(
|
|
2093
|
-
types.identifier('page'),
|
|
2094
|
-
types.binaryExpression(
|
|
2095
|
-
'+',
|
|
2096
|
-
types.memberExpression(types.identifier('state'), types.identifier('page')),
|
|
2097
|
-
types.numericLiteral(1)
|
|
2098
|
-
)
|
|
2099
|
-
),
|
|
2100
|
-
])
|
|
2101
|
-
),
|
|
2102
|
-
])
|
|
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
|
+
)
|
|
2103
1886
|
)
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
)
|
|
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')
|
|
2120
1902
|
),
|
|
2121
|
-
|
|
1903
|
+
types.identifier(vars.maxPagesStateVar)
|
|
1904
|
+
)
|
|
2122
1905
|
)
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
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
|
+
)
|
|
2131
1922
|
)
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
openingElement.attributes.push(
|
|
2135
|
-
types.jsxAttribute(types.jsxIdentifier('onClick'), types.jsxExpressionContainer(onClickHandler))
|
|
2136
|
-
)
|
|
2137
|
-
openingElement.attributes.push(
|
|
2138
|
-
types.jsxAttribute(types.jsxIdentifier('type'), types.stringLiteral('button'))
|
|
2139
|
-
)
|
|
2140
|
-
|
|
2141
|
-
// Add disabled attribute with simple page number checks
|
|
2142
|
-
const maxPagesStateVar = (info as any).maxPagesStateVar
|
|
2143
|
-
let disabledExpr: any
|
|
1923
|
+
)
|
|
1924
|
+
)
|
|
2144
1925
|
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
)
|
|
2158
|
-
} else {
|
|
2159
|
-
disabledExpr =
|
|
2160
|
-
buttonType === 'prev'
|
|
2161
|
-
? types.binaryExpression('<=', types.identifier(info.pageStateVar), types.numericLiteral(1))
|
|
2162
|
-
: types.binaryExpression(
|
|
2163
|
-
'>=',
|
|
2164
|
-
types.identifier(info.pageStateVar),
|
|
2165
|
-
types.identifier(maxPagesStateVar)
|
|
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
|
+
)
|
|
2166
1938
|
)
|
|
1939
|
+
)
|
|
1940
|
+
)
|
|
1941
|
+
}
|
|
2167
1942
|
}
|
|
2168
|
-
|
|
2169
|
-
openingElement.attributes.push(
|
|
2170
|
-
types.jsxAttribute(types.jsxIdentifier('disabled'), types.jsxExpressionContainer(disabledExpr))
|
|
2171
|
-
)
|
|
2172
1943
|
}
|
|
2173
1944
|
|
|
2174
|
-
function
|
|
2175
|
-
|
|
2176
|
-
|
|
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
|
|
2177
1951
|
)
|
|
2178
|
-
if (classNameAttr && classNameAttr.value && classNameAttr.value.type === 'StringLiteral') {
|
|
2179
|
-
return classNameAttr.value.value
|
|
2180
|
-
}
|
|
2181
|
-
return null
|
|
2182
|
-
}
|
|
2183
1952
|
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
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
|
+
}
|
|
2192
1968
|
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
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
|
+
}
|
|
2200
1983
|
}
|
|
1984
|
+
}
|
|
2201
1985
|
|
|
2202
|
-
|
|
2203
|
-
|
|
1986
|
+
function updateGetStaticProps(chunks: any[], registry: StateRegistry): void {
|
|
1987
|
+
const getStaticPropsChunk = chunks.find((c) => c.name === 'getStaticProps')
|
|
1988
|
+
if (!getStaticPropsChunk || getStaticPropsChunk.type !== ChunkType.AST) {
|
|
2204
1989
|
return
|
|
2205
1990
|
}
|
|
2206
1991
|
|
|
2207
|
-
const
|
|
2208
|
-
if (!
|
|
1992
|
+
const content = getStaticPropsChunk.content as types.ExportNamedDeclaration
|
|
1993
|
+
if (!content.declaration || content.declaration.type !== 'FunctionDeclaration') {
|
|
2209
1994
|
return
|
|
2210
1995
|
}
|
|
2211
1996
|
|
|
2212
|
-
|
|
2213
|
-
const
|
|
2214
|
-
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
|
|
2215
1999
|
| types.TryStatement
|
|
2216
2000
|
| undefined
|
|
2217
|
-
|
|
2218
|
-
if (!tryBlock) {
|
|
2001
|
+
if (!tryStmt) {
|
|
2219
2002
|
return
|
|
2220
2003
|
}
|
|
2221
2004
|
|
|
2222
|
-
const
|
|
2005
|
+
const tryBlock = tryStmt.block
|
|
2223
2006
|
|
|
2224
|
-
// Find
|
|
2225
|
-
const
|
|
2226
|
-
(
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
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'
|
|
2232
2015
|
) as types.VariableDeclaration | undefined
|
|
2233
2016
|
|
|
2234
|
-
if (!
|
|
2017
|
+
if (!promiseAllDecl) {
|
|
2235
2018
|
return
|
|
2236
2019
|
}
|
|
2237
2020
|
|
|
2238
|
-
const
|
|
2021
|
+
const declarator = promiseAllDecl.declarations[0] as types.VariableDeclarator
|
|
2022
|
+
const awaitExpr = declarator.init as types.AwaitExpression
|
|
2239
2023
|
const promiseAllCall = awaitExpr.argument as types.CallExpression
|
|
2240
|
-
const
|
|
2241
|
-
const destructuringPattern = promiseAllStmt.declarations[0].id as types.ArrayPattern
|
|
2242
|
-
|
|
2243
|
-
// Map import names to data source identifiers from existing fetchData calls
|
|
2244
|
-
// Also track which indices to remove (non-paginated calls that will be replaced)
|
|
2245
|
-
const importToDataSource = new Map<string, string>()
|
|
2246
|
-
const indicesToRemove: number[] = []
|
|
2247
|
-
|
|
2248
|
-
promiseArray.elements.forEach((element: any, index: number) => {
|
|
2249
|
-
if (element && element.type === 'CallExpression') {
|
|
2250
|
-
let fetchCallExpr = element
|
|
2251
|
-
|
|
2252
|
-
// If wrapped in .catch(), unwrap it
|
|
2253
|
-
if (
|
|
2254
|
-
element.callee?.type === 'MemberExpression' &&
|
|
2255
|
-
element.callee?.property?.name === 'catch' &&
|
|
2256
|
-
element.callee?.object?.type === 'CallExpression'
|
|
2257
|
-
) {
|
|
2258
|
-
fetchCallExpr = element.callee.object
|
|
2259
|
-
}
|
|
2260
|
-
|
|
2261
|
-
// Now find the .fetchData() call
|
|
2262
|
-
if (
|
|
2263
|
-
fetchCallExpr.callee?.type === 'MemberExpression' &&
|
|
2264
|
-
fetchCallExpr.callee?.property?.name === 'fetchData' &&
|
|
2265
|
-
fetchCallExpr.callee?.object?.type === 'Identifier'
|
|
2266
|
-
) {
|
|
2267
|
-
const importName = fetchCallExpr.callee.object.name
|
|
2268
|
-
const dataSourceVar = (destructuringPattern.elements[index] as types.Identifier).name
|
|
2269
|
-
|
|
2270
|
-
// Check if this fetchData call has page/perPage params
|
|
2271
|
-
const params = fetchCallExpr.arguments[0]
|
|
2272
|
-
const hasPageParam =
|
|
2273
|
-
params &&
|
|
2274
|
-
params.type === 'ObjectExpression' &&
|
|
2275
|
-
params.properties.some(
|
|
2276
|
-
(prop: any) =>
|
|
2277
|
-
prop.type === 'ObjectProperty' &&
|
|
2278
|
-
(prop.key.name === 'page' || prop.key.name === 'perPage')
|
|
2279
|
-
)
|
|
2024
|
+
const fetchesArray = promiseAllCall.arguments[0] as types.ArrayExpression
|
|
2280
2025
|
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
}
|
|
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
|
+
}
|
|
2289
2033
|
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
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
|
|
2294
2038
|
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
const propToRemove = (destructuringPattern.elements[index] as types.Identifier)?.name
|
|
2039
|
+
if (!propsProperty || propsProperty.value.type !== 'ObjectExpression') {
|
|
2040
|
+
return
|
|
2041
|
+
}
|
|
2299
2042
|
|
|
2300
|
-
|
|
2301
|
-
|
|
2043
|
+
const propsObj = propsProperty.value as types.ObjectExpression
|
|
2044
|
+
const arrayPattern = declarator.id as types.ArrayPattern
|
|
2302
2045
|
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
const foundReturnStmt = tryBody.find((stmt: any) => stmt.type === 'ReturnStatement') as
|
|
2306
|
-
| types.ReturnStatement
|
|
2307
|
-
| undefined
|
|
2046
|
+
// Track unique data sources for count fetching
|
|
2047
|
+
const dataSourcesNeedingCount = new Set<string>()
|
|
2308
2048
|
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
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)
|
|
2314
2058
|
|
|
2315
|
-
|
|
2316
|
-
|
|
2059
|
+
// Add fetch call
|
|
2060
|
+
const fetchParams: types.ObjectProperty[] = []
|
|
2317
2061
|
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
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
|
+
)
|
|
2327
2074
|
}
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
)?.[0]
|
|
2336
|
-
|
|
2337
|
-
if (!importName) {
|
|
2338
|
-
// Try case-insensitive match
|
|
2339
|
-
const normalizedIdentifier = info.dataSourceIdentifier.toLowerCase().replace(/[_-]/g, '')
|
|
2340
|
-
importName = Array.from(importToDataSource.entries()).find(
|
|
2341
|
-
([_, dataSourceVar]) =>
|
|
2342
|
-
dataSourceVar.toLowerCase().replace(/[_-]/g, '') === normalizedIdentifier
|
|
2343
|
-
)?.[0]
|
|
2344
|
-
}
|
|
2345
|
-
|
|
2346
|
-
if (importName) {
|
|
2347
|
-
const paginatedVarName = `${info.dataSourceIdentifier}_pg_${index}`
|
|
2348
|
-
|
|
2349
|
-
const fetchParams = [
|
|
2350
|
-
types.objectProperty(types.identifier('page'), types.numericLiteral(1)),
|
|
2351
|
-
types.objectProperty(types.identifier('perPage'), types.numericLiteral(info.perPage)),
|
|
2352
|
-
]
|
|
2353
|
-
|
|
2354
|
-
// Add queryColumns if they exist
|
|
2355
|
-
if (info.queryColumns && info.queryColumns.length > 0) {
|
|
2356
|
-
fetchParams.push(
|
|
2357
|
-
types.objectProperty(
|
|
2358
|
-
types.identifier('queryColumns'),
|
|
2359
|
-
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)))]
|
|
2360
2082
|
)
|
|
2361
2083
|
)
|
|
2362
|
-
}
|
|
2363
|
-
|
|
2364
|
-
// Create new fetchData call with pagination params
|
|
2365
|
-
const newFetchDataCall = types.callExpression(
|
|
2366
|
-
types.memberExpression(
|
|
2367
|
-
types.callExpression(
|
|
2368
|
-
types.memberExpression(types.identifier(importName), types.identifier('fetchData')),
|
|
2369
|
-
[types.objectExpression(fetchParams)]
|
|
2370
|
-
),
|
|
2371
|
-
types.identifier('catch')
|
|
2372
|
-
),
|
|
2373
|
-
[
|
|
2374
|
-
types.arrowFunctionExpression(
|
|
2375
|
-
[types.identifier('error')],
|
|
2376
|
-
types.blockStatement([
|
|
2377
|
-
types.expressionStatement(
|
|
2378
|
-
types.callExpression(
|
|
2379
|
-
types.memberExpression(types.identifier('console'), types.identifier('error')),
|
|
2380
|
-
[
|
|
2381
|
-
types.stringLiteral(`Error fetching ${paginatedVarName}:`),
|
|
2382
|
-
types.identifier('error'),
|
|
2383
|
-
]
|
|
2384
|
-
)
|
|
2385
|
-
),
|
|
2386
|
-
types.returnStatement(types.arrayExpression([])),
|
|
2387
|
-
])
|
|
2388
|
-
),
|
|
2389
|
-
]
|
|
2390
2084
|
)
|
|
2391
|
-
|
|
2392
|
-
promiseArray.elements.push(newFetchDataCall)
|
|
2393
|
-
destructuringPattern.elements.push(types.identifier(paginatedVarName))
|
|
2394
2085
|
}
|
|
2395
|
-
})
|
|
2396
2086
|
|
|
2397
|
-
|
|
2087
|
+
// Check if this fetch already exists
|
|
2088
|
+
const existingFetchIndex = arrayPattern.elements.findIndex(
|
|
2089
|
+
(el: any) => el?.type === 'Identifier' && el.name === vars.propsPrefix
|
|
2090
|
+
)
|
|
2398
2091
|
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
const addedCountFetches = new Set<string>()
|
|
2092
|
+
if (existingFetchIndex === -1) {
|
|
2093
|
+
arrayPattern.elements.push(types.identifier(vars.propsPrefix))
|
|
2402
2094
|
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
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
|
+
)
|
|
2407
2126
|
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
([_, dataSourceVar]) =>
|
|
2413
|
-
dataSourceVar.toLowerCase().replace(/[_-]/g, '') === normalizedIdentifier
|
|
2414
|
-
)?.[0]
|
|
2127
|
+
// Add to props
|
|
2128
|
+
propsObj.properties.push(
|
|
2129
|
+
types.objectProperty(types.identifier(vars.propsPrefix), types.identifier(vars.propsPrefix))
|
|
2130
|
+
)
|
|
2415
2131
|
}
|
|
2416
2132
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2133
|
+
// Track for count fetching
|
|
2134
|
+
if (usage.paginated) {
|
|
2135
|
+
dataSourcesNeedingCount.add(
|
|
2136
|
+
`${usage.resourceDefinition.dataSourceType}:${usage.resourceDefinition.tableName}:${usage.resourceDefinition.dataSourceId}`
|
|
2421
2137
|
)
|
|
2422
|
-
promiseArray.elements.push(fetchCountCall)
|
|
2423
|
-
destructuringPattern.elements.push(types.identifier(`${dataSourceId}_count`))
|
|
2424
|
-
addedCountFetches.add(dataSourceId)
|
|
2425
2138
|
}
|
|
2426
|
-
})
|
|
2427
|
-
|
|
2428
|
-
// Calculate and add maxPages before return
|
|
2429
|
-
const returnStmt = tryBody.find((stmt: any) => stmt.type === 'ReturnStatement') as
|
|
2430
|
-
| types.ReturnStatement
|
|
2431
|
-
| undefined
|
|
2432
2139
|
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
) 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`
|
|
2438
2144
|
|
|
2439
|
-
|
|
2440
|
-
const
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
const paginatedVarName = `${info.dataSourceIdentifier}_pg_${index}`
|
|
2445
|
-
const countVarName = `${info.dataSourceIdentifier}_count`
|
|
2446
|
-
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
|
+
)
|
|
2447
2150
|
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
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)
|
|
2460
2171
|
),
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
])
|
|
2467
|
-
|
|
2468
|
-
tryBody.splice(returnIndex, 0, maxPagesCalc)
|
|
2172
|
+
]
|
|
2173
|
+
)
|
|
2174
|
+
),
|
|
2175
|
+
])
|
|
2176
|
+
)
|
|
2469
2177
|
|
|
2470
|
-
// Add
|
|
2471
|
-
|
|
2178
|
+
// Add maxPages to props
|
|
2179
|
+
propsObj.properties.push(
|
|
2472
2180
|
types.objectProperty(
|
|
2473
|
-
types.identifier(
|
|
2474
|
-
types.identifier(
|
|
2181
|
+
types.identifier(maxPagesPropName),
|
|
2182
|
+
types.identifier(maxPagesPropName)
|
|
2475
2183
|
)
|
|
2476
2184
|
)
|
|
2477
|
-
propsObject.properties.push(
|
|
2478
|
-
types.objectProperty(types.identifier(maxPagesVarName), types.identifier(maxPagesVarName))
|
|
2479
|
-
)
|
|
2480
|
-
})
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
|
-
}
|
|
2484
|
-
|
|
2485
|
-
function createAPIRoutesForPaginatedDataSources(
|
|
2486
|
-
uidlNode: any,
|
|
2487
|
-
dataSources: any,
|
|
2488
|
-
componentChunk: any,
|
|
2489
|
-
extractedResources: any,
|
|
2490
|
-
paginationInfos: ArrayMapperPaginationInfo[],
|
|
2491
|
-
isComponent: boolean
|
|
2492
|
-
): void {
|
|
2493
|
-
const paginatedDataSourceIds = new Set(paginationInfos.map((info) => info.dataSourceIdentifier))
|
|
2494
|
-
|
|
2495
|
-
const searchEnabledDataSources = new Set(
|
|
2496
|
-
paginationInfos.filter((info) => info.searchEnabled).map((info) => info.dataSourceIdentifier)
|
|
2497
|
-
)
|
|
2498
|
-
|
|
2499
|
-
const createdCountRoutes = new Set<string>()
|
|
2500
|
-
|
|
2501
|
-
const traverseForDataSources = (node: any): void => {
|
|
2502
|
-
if (!node) {
|
|
2503
|
-
return
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
|
-
if (node.type === 'data-source-list' || node.type === 'data-source-item') {
|
|
2507
|
-
const renderProp = node.content.renderPropIdentifier
|
|
2508
|
-
|
|
2509
|
-
if (renderProp && paginatedDataSourceIds.has(renderProp)) {
|
|
2510
|
-
extractDataSourceIntoNextAPIFolder(node, dataSources, componentChunk, extractedResources)
|
|
2511
|
-
|
|
2512
|
-
const hasSearch = searchEnabledDataSources.has(renderProp)
|
|
2513
|
-
const needsCountRoute = isComponent || hasSearch
|
|
2514
|
-
|
|
2515
|
-
if (needsCountRoute) {
|
|
2516
|
-
const resourceDef = node.content.resourceDefinition
|
|
2517
|
-
if (resourceDef) {
|
|
2518
|
-
const dataSourceId = resourceDef.dataSourceId
|
|
2519
|
-
const tableName = resourceDef.tableName
|
|
2520
|
-
const dataSourceType = resourceDef.dataSourceType
|
|
2521
|
-
const fileName = `${dataSourceType}-${tableName}-${dataSourceId.substring(0, 8)}`
|
|
2522
|
-
const countFileName = `${fileName}-count`
|
|
2523
|
-
|
|
2524
|
-
if (!createdCountRoutes.has(countFileName)) {
|
|
2525
|
-
extractedResources[`api/${countFileName}`] = {
|
|
2526
|
-
fileName: countFileName,
|
|
2527
|
-
fileType: FileType.JS,
|
|
2528
|
-
path: ['pages', 'api'],
|
|
2529
|
-
content: `import dataSource from '../../utils/data-sources/${fileName}'
|
|
2530
|
-
|
|
2531
|
-
export default dataSource.getCount
|
|
2532
|
-
`,
|
|
2533
|
-
}
|
|
2534
|
-
createdCountRoutes.add(countFileName)
|
|
2535
|
-
}
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2538
2185
|
}
|
|
2539
2186
|
}
|
|
2187
|
+
})
|
|
2540
2188
|
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
): void {
|
|
2556
|
-
const searchOnlyDataSourceIds = new Set(
|
|
2557
|
-
searchOnlyDataSources.map((info) => info.dataSourceIdentifier)
|
|
2558
|
-
)
|
|
2559
|
-
|
|
2560
|
-
const traverseForDataSources = (node: any): void => {
|
|
2561
|
-
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) {
|
|
2562
2203
|
return
|
|
2563
2204
|
}
|
|
2564
2205
|
|
|
2565
|
-
|
|
2566
|
-
const renderProp = node.content.renderPropIdentifier
|
|
2206
|
+
const countVarName = `${usage.dataSourceIdentifier}_count`
|
|
2567
2207
|
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2208
|
+
// Check if count fetch already exists
|
|
2209
|
+
const existingCount = arrayPattern.elements.findIndex(
|
|
2210
|
+
(el: any) => el?.type === 'Identifier' && el.name === countVarName
|
|
2211
|
+
)
|
|
2572
2212
|
|
|
2573
|
-
if (
|
|
2574
|
-
|
|
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
|
+
)
|
|
2575
2224
|
}
|
|
2576
|
-
}
|
|
2577
|
-
|
|
2578
|
-
traverseForDataSources(uidlNode)
|
|
2225
|
+
})
|
|
2579
2226
|
}
|
|
2580
|
-
|
|
2581
|
-
export default createNextArrayMapperPaginationPlugin()
|