@teleporthq/teleport-plugin-next-data-source 0.42.34 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/ecommerce-product-out-of-stock.test.ts +112 -0
- package/__tests__/fetchers.test.ts +0 -42
- package/__tests__/filter-utils.test.ts +149 -0
- package/__tests__/mocks.ts +0 -12
- package/__tests__/utils.test.ts +0 -2
- package/dist/cjs/array-mapper-registry.d.ts +2 -0
- package/dist/cjs/array-mapper-registry.d.ts.map +1 -1
- package/dist/cjs/array-mapper-registry.js +9 -1
- package/dist/cjs/array-mapper-registry.js.map +1 -1
- package/dist/cjs/count-fetchers.d.ts +2 -2
- package/dist/cjs/count-fetchers.d.ts.map +1 -1
- package/dist/cjs/count-fetchers.js +5 -5
- package/dist/cjs/count-fetchers.js.map +1 -1
- package/dist/cjs/data-source-fetchers.d.ts +2 -1
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +11 -9
- package/dist/cjs/data-source-fetchers.js.map +1 -1
- package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
- package/dist/cjs/fetchers/airtable.js +1 -1
- package/dist/cjs/fetchers/airtable.js.map +1 -1
- package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/cjs/fetchers/clickhouse.js +1 -1
- package/dist/cjs/fetchers/clickhouse.js.map +1 -1
- package/dist/cjs/fetchers/csv-file.js +1 -1
- package/dist/cjs/fetchers/csv-file.js.map +1 -1
- package/dist/cjs/fetchers/firestore.js +1 -1
- package/dist/cjs/fetchers/firestore.js.map +1 -1
- package/dist/cjs/fetchers/google-sheets.js +1 -1
- package/dist/cjs/fetchers/google-sheets.js.map +1 -1
- package/dist/cjs/fetchers/index.d.ts +2 -1
- package/dist/cjs/fetchers/index.d.ts.map +1 -1
- package/dist/cjs/fetchers/index.js +8 -5
- package/dist/cjs/fetchers/index.js.map +1 -1
- package/dist/cjs/fetchers/javascript.js +1 -1
- package/dist/cjs/fetchers/javascript.js.map +1 -1
- package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mariadb.js +3 -3
- package/dist/cjs/fetchers/mariadb.js.map +1 -1
- package/dist/cjs/fetchers/mongodb.js +1 -1
- package/dist/cjs/fetchers/mongodb.js.map +1 -1
- package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
- package/dist/cjs/fetchers/mysql.js +2 -2
- package/dist/cjs/fetchers/mysql.js.map +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +2 -2
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/raw-query.d.ts +18 -0
- package/dist/cjs/fetchers/raw-query.d.ts.map +1 -0
- package/dist/cjs/fetchers/raw-query.js +70 -0
- package/dist/cjs/fetchers/raw-query.js.map +1 -0
- package/dist/cjs/fetchers/redis.js +1 -1
- package/dist/cjs/fetchers/redis.js.map +1 -1
- package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
- package/dist/cjs/fetchers/redshift.js +2 -2
- package/dist/cjs/fetchers/redshift.js.map +1 -1
- package/dist/cjs/fetchers/rest-api.js +1 -1
- package/dist/cjs/fetchers/rest-api.js.map +1 -1
- package/dist/cjs/fetchers/supabase.d.ts.map +1 -1
- package/dist/cjs/fetchers/supabase.js +62 -2
- package/dist/cjs/fetchers/supabase.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts +7 -0
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -0
- package/dist/cjs/fetchers/teleport.js +63 -0
- package/dist/cjs/fetchers/teleport.js.map +1 -0
- package/dist/cjs/fetchers/turso.d.ts.map +1 -1
- package/dist/cjs/fetchers/turso.js +1 -1
- package/dist/cjs/fetchers/turso.js.map +1 -1
- package/dist/cjs/filter-utils.d.ts +13 -0
- package/dist/cjs/filter-utils.d.ts.map +1 -0
- package/dist/cjs/filter-utils.js +95 -0
- package/dist/cjs/filter-utils.js.map +1 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +112 -9
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +389 -128
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/sort-utils.d.ts +10 -0
- package/dist/cjs/sort-utils.d.ts.map +1 -0
- package/dist/cjs/sort-utils.js +141 -0
- package/dist/cjs/sort-utils.js.map +1 -0
- package/dist/cjs/transformations/blog-post.d.ts +7 -0
- package/dist/cjs/transformations/blog-post.d.ts.map +1 -0
- package/dist/cjs/transformations/blog-post.js +13 -0
- package/dist/cjs/transformations/blog-post.js.map +1 -0
- package/dist/cjs/transformations/ecommerce-product.d.ts +7 -0
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -0
- package/dist/cjs/transformations/ecommerce-product.js +13 -0
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -0
- package/dist/cjs/transformations/index.d.ts +26 -0
- package/dist/cjs/transformations/index.d.ts.map +1 -0
- package/dist/cjs/transformations/index.js +81 -0
- package/dist/cjs/transformations/index.js.map +1 -0
- package/dist/cjs/transformations/shared-utils.d.ts +7 -0
- package/dist/cjs/transformations/shared-utils.d.ts.map +1 -0
- package/dist/cjs/transformations/shared-utils.js +13 -0
- package/dist/cjs/transformations/shared-utils.js.map +1 -0
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +30 -1
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +173 -10
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/array-mapper-registry.d.ts +2 -0
- package/dist/esm/array-mapper-registry.d.ts.map +1 -1
- package/dist/esm/array-mapper-registry.js +9 -1
- package/dist/esm/array-mapper-registry.js.map +1 -1
- package/dist/esm/count-fetchers.d.ts +2 -2
- package/dist/esm/count-fetchers.d.ts.map +1 -1
- package/dist/esm/count-fetchers.js +4 -4
- package/dist/esm/count-fetchers.js.map +1 -1
- package/dist/esm/data-source-fetchers.d.ts +2 -1
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +10 -9
- package/dist/esm/data-source-fetchers.js.map +1 -1
- package/dist/esm/fetchers/airtable.d.ts.map +1 -1
- package/dist/esm/fetchers/airtable.js +1 -1
- package/dist/esm/fetchers/airtable.js.map +1 -1
- package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/esm/fetchers/clickhouse.js +1 -1
- package/dist/esm/fetchers/clickhouse.js.map +1 -1
- package/dist/esm/fetchers/csv-file.js +1 -1
- package/dist/esm/fetchers/csv-file.js.map +1 -1
- package/dist/esm/fetchers/firestore.js +1 -1
- package/dist/esm/fetchers/firestore.js.map +1 -1
- package/dist/esm/fetchers/google-sheets.js +1 -1
- package/dist/esm/fetchers/google-sheets.js.map +1 -1
- package/dist/esm/fetchers/index.d.ts +2 -1
- package/dist/esm/fetchers/index.d.ts.map +1 -1
- package/dist/esm/fetchers/index.js +2 -1
- package/dist/esm/fetchers/index.js.map +1 -1
- package/dist/esm/fetchers/javascript.js +1 -1
- package/dist/esm/fetchers/javascript.js.map +1 -1
- package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
- package/dist/esm/fetchers/mariadb.js +4 -4
- package/dist/esm/fetchers/mariadb.js.map +1 -1
- package/dist/esm/fetchers/mongodb.js +1 -1
- package/dist/esm/fetchers/mongodb.js.map +1 -1
- package/dist/esm/fetchers/mysql.d.ts.map +1 -1
- package/dist/esm/fetchers/mysql.js +3 -3
- package/dist/esm/fetchers/mysql.js.map +1 -1
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +3 -3
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/raw-query.d.ts +18 -0
- package/dist/esm/fetchers/raw-query.d.ts.map +1 -0
- package/dist/esm/fetchers/raw-query.js +65 -0
- package/dist/esm/fetchers/raw-query.js.map +1 -0
- package/dist/esm/fetchers/redis.js +1 -1
- package/dist/esm/fetchers/redis.js.map +1 -1
- package/dist/esm/fetchers/redshift.d.ts.map +1 -1
- package/dist/esm/fetchers/redshift.js +3 -3
- package/dist/esm/fetchers/redshift.js.map +1 -1
- package/dist/esm/fetchers/rest-api.js +1 -1
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/fetchers/supabase.d.ts.map +1 -1
- package/dist/esm/fetchers/supabase.js +63 -3
- package/dist/esm/fetchers/supabase.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts +7 -0
- package/dist/esm/fetchers/teleport.d.ts.map +1 -0
- package/dist/esm/fetchers/teleport.js +57 -0
- package/dist/esm/fetchers/teleport.js.map +1 -0
- package/dist/esm/fetchers/turso.d.ts.map +1 -1
- package/dist/esm/fetchers/turso.js +2 -2
- package/dist/esm/fetchers/turso.js.map +1 -1
- package/dist/esm/filter-utils.d.ts +13 -0
- package/dist/esm/filter-utils.d.ts.map +1 -0
- package/dist/esm/filter-utils.js +66 -0
- package/dist/esm/filter-utils.js.map +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +113 -10
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +389 -128
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/sort-utils.d.ts +10 -0
- package/dist/esm/sort-utils.d.ts.map +1 -0
- package/dist/esm/sort-utils.js +113 -0
- package/dist/esm/sort-utils.js.map +1 -0
- package/dist/esm/transformations/blog-post.d.ts +7 -0
- package/dist/esm/transformations/blog-post.d.ts.map +1 -0
- package/dist/esm/transformations/blog-post.js +9 -0
- package/dist/esm/transformations/blog-post.js.map +1 -0
- package/dist/esm/transformations/ecommerce-product.d.ts +7 -0
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -0
- package/dist/esm/transformations/ecommerce-product.js +9 -0
- package/dist/esm/transformations/ecommerce-product.js.map +1 -0
- package/dist/esm/transformations/index.d.ts +26 -0
- package/dist/esm/transformations/index.d.ts.map +1 -0
- package/dist/esm/transformations/index.js +74 -0
- package/dist/esm/transformations/index.js.map +1 -0
- package/dist/esm/transformations/shared-utils.d.ts +7 -0
- package/dist/esm/transformations/shared-utils.d.ts.map +1 -0
- package/dist/esm/transformations/shared-utils.js +9 -0
- package/dist/esm/transformations/shared-utils.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +30 -1
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +170 -9
- package/dist/esm/utils.js.map +1 -1
- package/package.json +6 -5
- package/src/array-mapper-registry.ts +13 -0
- package/src/count-fetchers.ts +5 -5
- package/src/data-source-fetchers.ts +15 -11
- package/src/fetchers/airtable.ts +54 -8
- package/src/fetchers/clickhouse.ts +25 -19
- package/src/fetchers/csv-file.ts +2 -2
- package/src/fetchers/firestore.ts +2 -2
- package/src/fetchers/google-sheets.ts +2 -2
- package/src/fetchers/index.ts +6 -5
- package/src/fetchers/javascript.ts +2 -2
- package/src/fetchers/mariadb.ts +27 -12
- package/src/fetchers/mongodb.ts +2 -2
- package/src/fetchers/mysql.ts +27 -12
- package/src/fetchers/postgresql.ts +31 -18
- package/src/fetchers/raw-query.ts +178 -0
- package/src/fetchers/redis.ts +2 -2
- package/src/fetchers/redshift.ts +14 -10
- package/src/fetchers/rest-api.ts +2 -2
- package/src/fetchers/supabase.ts +97 -14
- package/src/fetchers/teleport.ts +485 -0
- package/src/fetchers/turso.ts +15 -7
- package/src/filter-utils.ts +111 -0
- package/src/index.ts +146 -6
- package/src/pagination-plugin.ts +547 -308
- package/src/sort-utils.ts +150 -0
- package/src/transformations/blog-post.ts +128 -0
- package/src/transformations/ecommerce-product.ts +173 -0
- package/src/transformations/index.ts +97 -0
- package/src/transformations/shared-utils.ts +271 -0
- package/src/utils.ts +227 -11
- package/dist/cjs/fetchers/static-collection.d.ts +0 -7
- package/dist/cjs/fetchers/static-collection.d.ts.map +0 -1
- package/dist/cjs/fetchers/static-collection.js +0 -25
- package/dist/cjs/fetchers/static-collection.js.map +0 -1
- package/dist/esm/fetchers/static-collection.d.ts +0 -7
- package/dist/esm/fetchers/static-collection.d.ts.map +0 -1
- package/dist/esm/fetchers/static-collection.js +0 -19
- package/dist/esm/fetchers/static-collection.js.map +0 -1
- package/src/fetchers/static-collection.ts +0 -231
package/src/pagination-plugin.ts
CHANGED
|
@@ -5,10 +5,64 @@ import {
|
|
|
5
5
|
FileType,
|
|
6
6
|
} from '@teleporthq/teleport-types'
|
|
7
7
|
import * as types from '@babel/types'
|
|
8
|
+
import { parseExpression } from '@babel/parser'
|
|
8
9
|
import { StringUtils } from '@teleporthq/teleport-shared'
|
|
9
10
|
import { ASTUtils } from '@teleporthq/teleport-plugin-common'
|
|
10
11
|
import { generateSafeFileName } from './utils'
|
|
11
12
|
import { generateDataSourceFetcherWithCore } from './data-source-fetchers'
|
|
13
|
+
import { appendSortsParam, DynamicSortAST, extractDynamicSort } from './sort-utils'
|
|
14
|
+
import { appendFiltersParam, pushStateIdsAsDeps } from './filter-utils'
|
|
15
|
+
|
|
16
|
+
// ----- searchDefaultValue support -----
|
|
17
|
+
//
|
|
18
|
+
// `searchDefaultValue` on a `cms-list-repeater` UIDL node seeds the
|
|
19
|
+
// generated search input and the pre-debounce combined state. UIDL
|
|
20
|
+
// stores the attribute as either `{type:'static',content:string}` (a
|
|
21
|
+
// hard-coded initial value) or `{type:'expr',content:string}` (a JS
|
|
22
|
+
// expression referencing props / state / router query / a URL param
|
|
23
|
+
// helper). Static strings become `types.stringLiteral(...)` and
|
|
24
|
+
// expressions are parsed via `@babel/parser.parseExpression` into a
|
|
25
|
+
// real AST node we slot straight into `useState(...)`.
|
|
26
|
+
type SearchDefaultValue =
|
|
27
|
+
| { kind: 'static'; value: string }
|
|
28
|
+
| { kind: 'expression'; ast: types.Expression }
|
|
29
|
+
|
|
30
|
+
// tslint:disable-next-line:no-any
|
|
31
|
+
function parseSearchDefaultValue(raw: any): SearchDefaultValue | undefined {
|
|
32
|
+
if (!raw || typeof raw !== 'object') {
|
|
33
|
+
return undefined
|
|
34
|
+
}
|
|
35
|
+
if (raw.type === 'static' && typeof raw.content === 'string' && raw.content.length > 0) {
|
|
36
|
+
return { kind: 'static', value: raw.content }
|
|
37
|
+
}
|
|
38
|
+
if ((raw.type === 'expr' || raw.type === 'dynamic') && typeof raw.content === 'string') {
|
|
39
|
+
const src = raw.content.trim()
|
|
40
|
+
if (src.length === 0) {
|
|
41
|
+
return undefined
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const ast = parseExpression(src, { sourceType: 'module', plugins: ['jsx'] })
|
|
45
|
+
return { kind: 'expression', ast: ast as types.Expression }
|
|
46
|
+
} catch {
|
|
47
|
+
// Fall back to no seed if the expression is malformed — better to
|
|
48
|
+
// render an empty input than to emit broken code.
|
|
49
|
+
return undefined
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return undefined
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function searchDefaultValueInitAST(value: SearchDefaultValue | undefined): types.Expression {
|
|
56
|
+
if (!value) {
|
|
57
|
+
return types.stringLiteral('')
|
|
58
|
+
}
|
|
59
|
+
if (value.kind === 'static') {
|
|
60
|
+
return types.stringLiteral(value.value)
|
|
61
|
+
}
|
|
62
|
+
// Clone so multiple `useState(...)` call sites each get an
|
|
63
|
+
// independent AST node — Babel does not support shared references.
|
|
64
|
+
return types.cloneNode(value.ast, /* deep */ true) as types.Expression
|
|
65
|
+
}
|
|
12
66
|
|
|
13
67
|
// ==================== UIDL-FIRST STATE MANAGEMENT ====================
|
|
14
68
|
// This module uses a UIDL-first approach: we scan the UIDL FIRST to identify
|
|
@@ -34,16 +88,37 @@ interface DataSourceUsage {
|
|
|
34
88
|
// Search config
|
|
35
89
|
searchEnabled: boolean
|
|
36
90
|
searchDebounce: number
|
|
91
|
+
// Initial value for the search input. Static strings are seeded as
|
|
92
|
+
// `useState(<string>)`; dynamic UIDL expressions are parsed via
|
|
93
|
+
// `@babel/parser` and slotted in as real AST nodes so the generated
|
|
94
|
+
// component can reference props / state / router-query / URL-param
|
|
95
|
+
// helpers in the initial value.
|
|
96
|
+
searchDefaultValue?: SearchDefaultValue
|
|
37
97
|
// Query columns from resource params
|
|
38
98
|
queryColumns: string[]
|
|
39
|
-
// Sorts from resource params
|
|
99
|
+
// Sorts from resource params (legacy static-array form)
|
|
40
100
|
// tslint:disable-next-line:no-any
|
|
41
101
|
sorts: any[]
|
|
42
|
-
//
|
|
102
|
+
// Dynamic single-column sort bound to component state (set when legacy sorts empty
|
|
103
|
+
// and cms-list-repeater declares sort/sortDirection fields)
|
|
104
|
+
dynamicSort?: DynamicSortAST
|
|
105
|
+
// Filters from resource params (FLAT condition array — any `{ type: 'group',
|
|
106
|
+
// children: [...] }` wrappers from the UIDL inspector have already been
|
|
107
|
+
// unwrapped via `flattenFilterGroups`). Downstream emit sites rely on
|
|
108
|
+
// entries having `.source` / `.destination` / `.operand` directly.
|
|
43
109
|
// tslint:disable-next-line:no-any
|
|
44
110
|
filters: any[]
|
|
45
|
-
// State IDs from dynamic filter destinations (for useMemo dependencies)
|
|
111
|
+
// State IDs from dynamic filter destinations (for useMemo dependencies).
|
|
112
|
+
// Only state references land here; `urlSearchParams` refs are tracked in
|
|
113
|
+
// `filterUrlSearchParamKeys` because their dep expression is
|
|
114
|
+
// `router.query.<key>`, not a bare identifier.
|
|
46
115
|
filterStateIds: string[]
|
|
116
|
+
// URL search-param keys referenced by filter destinations (e.g.
|
|
117
|
+
// `'categoryFilter'`). Drives `const router = useRouter()` injection and
|
|
118
|
+
// the corresponding `router.query.<key>` entries in `useMemo` deps so the
|
|
119
|
+
// client-side fetch refires when the buyer navigates between
|
|
120
|
+
// `?categoryFilter=Rings` and `?categoryFilter=Necklaces`.
|
|
121
|
+
filterUrlSearchParamKeys: string[]
|
|
47
122
|
// Computed category
|
|
48
123
|
category: 'paginated+search' | 'paginated-only' | 'search-only' | 'plain'
|
|
49
124
|
}
|
|
@@ -56,6 +131,205 @@ interface StateRegistry {
|
|
|
56
131
|
byArrayMapperRenderProp: Map<string, DataSourceUsage>
|
|
57
132
|
}
|
|
58
133
|
|
|
134
|
+
// The UIDL's `filters.content` array can wrap one or more conditions inside
|
|
135
|
+
// a `{ type: 'group', operator: 'and' | 'or', children: [...] }` entry — the
|
|
136
|
+
// GUI builds this shape when the inspector adds a logical group. The data
|
|
137
|
+
// source API endpoint (`processFilters` in the generated data-source module)
|
|
138
|
+
// only knows how to consume a FLAT array of `{ source, destination, operand }`
|
|
139
|
+
// conditions, so we walk groups recursively here and collect their leaf
|
|
140
|
+
// conditions in order. Anything that isn't a group AND isn't a condition is
|
|
141
|
+
// dropped — defensive against partially-built filter entries that would
|
|
142
|
+
// otherwise emit `{ source: '', destination: '', operand: '' }` rows the
|
|
143
|
+
// API ignores anyway.
|
|
144
|
+
//
|
|
145
|
+
// `or`-grouped conditions are flattened just like `and`-grouped ones — the
|
|
146
|
+
// downstream SQL builder always joins flat filters with `AND`, so emitting
|
|
147
|
+
// the conditions side-by-side approximates AND semantics. A future API
|
|
148
|
+
// upgrade that respects group operators would key off the original tree
|
|
149
|
+
// directly; until then, AND-flattening is the closest correct behaviour and
|
|
150
|
+
// matches what the inspector preview shows for the common single-group case
|
|
151
|
+
// the GUI emits today.
|
|
152
|
+
function flattenFilterGroups(filters: any[]): any[] {
|
|
153
|
+
if (!Array.isArray(filters)) {
|
|
154
|
+
return []
|
|
155
|
+
}
|
|
156
|
+
const out: any[] = []
|
|
157
|
+
const walk = (entry: any): void => {
|
|
158
|
+
if (!entry || typeof entry !== 'object') {
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
if (entry.type === 'group' && Array.isArray(entry.children)) {
|
|
162
|
+
for (const child of entry.children) {
|
|
163
|
+
walk(child)
|
|
164
|
+
}
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
// Backwards-compatible: entries without an explicit `type` (legacy flat
|
|
168
|
+
// form) and entries explicitly tagged `condition` are both treated as
|
|
169
|
+
// condition leaves.
|
|
170
|
+
if (entry.type === undefined || entry.type === 'condition') {
|
|
171
|
+
out.push(entry)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const entry of filters) {
|
|
175
|
+
walk(entry)
|
|
176
|
+
}
|
|
177
|
+
return out
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Walks a flat filter list to extract every `urlSearchParams` reference key
|
|
181
|
+
// (e.g. `'categoryFilter'`). Used to (a) inject `const router = useRouter()`
|
|
182
|
+
// once at the top of the component, (b) wire `router.query.<key>` into the
|
|
183
|
+
// `useMemo` dependency array so the client-side fetch reruns whenever the
|
|
184
|
+
// buyer navigates to a URL with a different `?key=value`. Returned in the
|
|
185
|
+
// order keys first appear so the generated dep array stays stable.
|
|
186
|
+
function collectFilterUrlSearchParamKeys(filters: any[]): string[] {
|
|
187
|
+
const seen = new Set<string>()
|
|
188
|
+
const out: string[] = []
|
|
189
|
+
for (const f of filters) {
|
|
190
|
+
const dest = f?.destination
|
|
191
|
+
if (!ASTUtils.isUIDLDynamicReference(dest)) {
|
|
192
|
+
continue
|
|
193
|
+
}
|
|
194
|
+
const content = (dest as { content?: { referenceType?: string; id?: string } }).content
|
|
195
|
+
if (!content || content.referenceType !== 'urlSearchParams' || !content.id) {
|
|
196
|
+
continue
|
|
197
|
+
}
|
|
198
|
+
if (seen.has(content.id)) {
|
|
199
|
+
continue
|
|
200
|
+
}
|
|
201
|
+
seen.add(content.id)
|
|
202
|
+
out.push(content.id)
|
|
203
|
+
}
|
|
204
|
+
return out
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Returns true when any filter destination is a `urlSearchParams` dynamic
|
|
208
|
+
// reference — used by the plugin to know it needs to import `useRouter` and
|
|
209
|
+
// emit `const router = useRouter()` at the top of the component. Cheaper to
|
|
210
|
+
// test against the precomputed key list once than to walk all filters on
|
|
211
|
+
// every check.
|
|
212
|
+
function hasUrlSearchParamFilters(usage: DataSourceUsage): boolean {
|
|
213
|
+
return usage.filterUrlSearchParamKeys.length > 0
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Appends `router.query.<key>` member expressions to a useMemo deps array so
|
|
217
|
+
// every client-side fetch refires when the buyer navigates between URLs
|
|
218
|
+
// that differ only by `?key=value`. React's shallow-compare semantics treat
|
|
219
|
+
// the member expression as a distinct value per render, so the array stays
|
|
220
|
+
// stable across paints with the same query string and changes the moment
|
|
221
|
+
// the URL does. Skip the bare-identifier dedupe `filterStateIds` uses —
|
|
222
|
+
// member expressions never collide with state identifiers, and the deps
|
|
223
|
+
// array allows duplicates without harm.
|
|
224
|
+
function pushUrlSearchParamMemoDeps(memoDeps: types.Expression[], usage: DataSourceUsage): void {
|
|
225
|
+
for (const key of usage.filterUrlSearchParamKeys) {
|
|
226
|
+
memoDeps.push(
|
|
227
|
+
types.memberExpression(
|
|
228
|
+
types.memberExpression(types.identifier('router'), types.identifier('query')),
|
|
229
|
+
types.identifier(key)
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Builds the AST for `!router.query.<key1> && !router.query.<key2> && ...`,
|
|
236
|
+
// used as a runtime guard around `initialData={props.X}` so the server-
|
|
237
|
+
// prefetched (unfiltered) data is only handed to `DataProvider` when the
|
|
238
|
+
// URL has no active filter. Without this guard, navigating to
|
|
239
|
+
// `/products-list?categoryFilter=Rings` (especially via soft Next.js
|
|
240
|
+
// transitions where the page component remounts with fresh getStaticProps
|
|
241
|
+
// but the buyer's URL filter is still in scope) shows the unfiltered list
|
|
242
|
+
// for the first paint AND keeps it on screen because `DataProvider`'s
|
|
243
|
+
// `passFetchBecauseWeHaveInitialData` ref skips the very first fetch when
|
|
244
|
+
// `initialData !== undefined`. By emitting `undefined` here whenever ANY
|
|
245
|
+
// url-search-param filter is set, the DataProvider's mount-time fetch runs
|
|
246
|
+
// immediately with the filtered params instead of presenting stale data.
|
|
247
|
+
//
|
|
248
|
+
// Returns `null` when the usage has no url-search-param filters at all —
|
|
249
|
+
// callers fall back to the existing `props.X` expression unchanged so
|
|
250
|
+
// non-filtered pages still benefit from the SSR prefetch.
|
|
251
|
+
function buildNoUrlFilterGuard(usage: DataSourceUsage): types.Expression | null {
|
|
252
|
+
if (usage.filterUrlSearchParamKeys.length === 0 && usage.filterStateIds.length === 0) {
|
|
253
|
+
return null
|
|
254
|
+
}
|
|
255
|
+
const guards: types.Expression[] = []
|
|
256
|
+
for (const key of usage.filterUrlSearchParamKeys) {
|
|
257
|
+
guards.push(
|
|
258
|
+
types.unaryExpression(
|
|
259
|
+
'!',
|
|
260
|
+
types.memberExpression(
|
|
261
|
+
types.memberExpression(types.identifier('router'), types.identifier('query')),
|
|
262
|
+
types.identifier(key)
|
|
263
|
+
),
|
|
264
|
+
true
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
// State-bound filter destinations (e.g. `selectedCategory`) emit as bare
|
|
269
|
+
// identifiers, so the corresponding guard is `!selectedCategory`. An empty
|
|
270
|
+
// string ('') for the state — which the GUI emits when the user picks the
|
|
271
|
+
// "All Categories" reset option — is falsy and so passes through the guard
|
|
272
|
+
// exactly like a missing URL param: initialData (unfiltered prefetch) wins
|
|
273
|
+
// until the user picks a real value.
|
|
274
|
+
for (const id of usage.filterStateIds) {
|
|
275
|
+
guards.push(types.unaryExpression('!', types.identifier(id), true))
|
|
276
|
+
}
|
|
277
|
+
return guards.reduce((acc, next) => types.logicalExpression('&&', acc, next))
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Wraps an existing `initialData` condition with the additional "no URL
|
|
281
|
+
// filter active" guard so server-prefetched data is only handed to
|
|
282
|
+
// DataProvider when both (a) the original guard (page === 1, no search
|
|
283
|
+
// query, etc.) AND (b) every relevant `router.query.<key>` is falsy. See
|
|
284
|
+
// `buildNoUrlFilterGuard` for the rationale on why bare-identity feature
|
|
285
|
+
// detection is safer than the `dynamicSort`-style "always undefined" path.
|
|
286
|
+
function wrapInitialDataWithUrlFilterGuard(
|
|
287
|
+
baseCondition: types.Expression,
|
|
288
|
+
usage: DataSourceUsage
|
|
289
|
+
): types.Expression {
|
|
290
|
+
const noFilterGuard = buildNoUrlFilterGuard(usage)
|
|
291
|
+
if (!noFilterGuard) {
|
|
292
|
+
return baseCondition
|
|
293
|
+
}
|
|
294
|
+
return types.logicalExpression('&&', baseCondition, noFilterGuard)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Builds the destination AST for a single filter entry. Replaces the bare
|
|
298
|
+
// `ASTUtils.convertFilterDestinationToExpression(filter.destination)` call
|
|
299
|
+
// at every emit site so `urlSearchParams` references resolve to
|
|
300
|
+
// `router?.query?.<key>` instead of falling through to a bare identifier
|
|
301
|
+
// (which the existing helper would emit, leaving the client-side fetch
|
|
302
|
+
// referencing an undeclared symbol).
|
|
303
|
+
//
|
|
304
|
+
// State and prop references delegate back to the shared helper so the
|
|
305
|
+
// existing inspector behaviour (state-bound filter destinations) keeps
|
|
306
|
+
// working unchanged. `router?.query?.<key>` is the same shape the
|
|
307
|
+
// `createNextUrlSearchParamsPlugin`-driven `dynamicReferencePrefixMap`
|
|
308
|
+
// emits for page-level navlink reads.
|
|
309
|
+
function buildFilterDestinationExpression(destination: unknown): types.Expression {
|
|
310
|
+
if (ASTUtils.isUIDLDynamicReference(destination)) {
|
|
311
|
+
const content = (destination as { content?: { referenceType?: string; id?: string } }).content
|
|
312
|
+
if (content?.referenceType === 'urlSearchParams' && content?.id) {
|
|
313
|
+
// router?.query?.<id> — the optional-chain survives the first paint
|
|
314
|
+
// where Next.js's `useRouter()` returns `null` during static export
|
|
315
|
+
// hydration, so the fetch doesn't crash with "Cannot read properties
|
|
316
|
+
// of null" before the router is ready.
|
|
317
|
+
return types.optionalMemberExpression(
|
|
318
|
+
types.optionalMemberExpression(
|
|
319
|
+
types.identifier('router'),
|
|
320
|
+
types.identifier('query'),
|
|
321
|
+
false,
|
|
322
|
+
true
|
|
323
|
+
),
|
|
324
|
+
types.identifier(content.id),
|
|
325
|
+
false,
|
|
326
|
+
true
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return ASTUtils.convertFilterDestinationToExpression(destination)
|
|
331
|
+
}
|
|
332
|
+
|
|
59
333
|
// Scan UIDL to find all data source usages and build a registry
|
|
60
334
|
function buildStateRegistry(uidlNode: any): StateRegistry {
|
|
61
335
|
const usages: DataSourceUsage[] = []
|
|
@@ -113,22 +387,52 @@ function buildStateRegistry(uidlNode: any): StateRegistry {
|
|
|
113
387
|
queryColumns = parentDataSource.resourceParams.queryColumns.content
|
|
114
388
|
}
|
|
115
389
|
|
|
116
|
-
// Extract sorts from parent's resource params
|
|
390
|
+
// Extract sorts from parent's resource params (legacy static array form)
|
|
117
391
|
let sorts: any[] = []
|
|
118
392
|
if (parentDataSource.resourceParams?.sorts?.content) {
|
|
119
393
|
sorts = parentDataSource.resourceParams.sorts.content
|
|
120
394
|
}
|
|
121
395
|
|
|
122
|
-
//
|
|
396
|
+
// If legacy sorts aren't set, fall back to the new dynamic single-column
|
|
397
|
+
// sort fields on the cms-list-repeater (used by admin-panel listing pages).
|
|
398
|
+
let dynamicSort: DynamicSortAST | undefined
|
|
399
|
+
if ((!sorts || sorts.length === 0) && content.sort) {
|
|
400
|
+
dynamicSort = extractDynamicSort(content.sort, content.sortDirection)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Extract filters from parent's resource params. The inspector wraps
|
|
404
|
+
// every condition in a `{ type: 'group' }` envelope (single-group or
|
|
405
|
+
// nested), so flatten to leaf conditions before the downstream emit
|
|
406
|
+
// sites consume `.source` / `.destination` / `.operand` directly —
|
|
407
|
+
// they expect a flat condition array. See `flattenFilterGroups`'s
|
|
408
|
+
// header comment for why AND-flattening is the correct fallback for
|
|
409
|
+
// the API endpoint's flat-condition contract.
|
|
123
410
|
let filters: any[] = []
|
|
124
411
|
if (parentDataSource.resourceParams?.filters?.content) {
|
|
125
|
-
filters = parentDataSource.resourceParams.filters.content
|
|
412
|
+
filters = flattenFilterGroups(parentDataSource.resourceParams.filters.content)
|
|
126
413
|
}
|
|
127
414
|
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
415
|
+
// Split dynamic destination keys by reference type. `state`/`prop`
|
|
416
|
+
// refs resolve to bare identifiers (so they're useMemo deps as-is);
|
|
417
|
+
// `urlSearchParams` refs resolve to `router.query.<key>` and need a
|
|
418
|
+
// `useRouter()` declaration injected separately.
|
|
419
|
+
const filterStateIds: string[] = []
|
|
420
|
+
for (const f of filters) {
|
|
421
|
+
if (!ASTUtils.isUIDLDynamicReference(f.destination)) {
|
|
422
|
+
continue
|
|
423
|
+
}
|
|
424
|
+
const destinationContent = (
|
|
425
|
+
f.destination as { content?: { referenceType?: string; id?: string } }
|
|
426
|
+
).content
|
|
427
|
+
if (!destinationContent || !destinationContent.id) {
|
|
428
|
+
continue
|
|
429
|
+
}
|
|
430
|
+
if (destinationContent.referenceType === 'urlSearchParams') {
|
|
431
|
+
continue
|
|
432
|
+
}
|
|
433
|
+
filterStateIds.push(destinationContent.id)
|
|
434
|
+
}
|
|
435
|
+
const filterUrlSearchParamKeys: string[] = collectFilterUrlSearchParamKeys(filters)
|
|
132
436
|
|
|
133
437
|
// Extract limit from parent's resource params (for plain array mappers)
|
|
134
438
|
let limit = 0
|
|
@@ -140,6 +444,8 @@ function buildStateRegistry(uidlNode: any): StateRegistry {
|
|
|
140
444
|
// For plain mappers, use limit from data-source-list resource params
|
|
141
445
|
const effectivePerPage = content.paginated ? content.perPage : limit || content.perPage
|
|
142
446
|
|
|
447
|
+
const searchDefaultValue = parseSearchDefaultValue(content.searchDefaultValue)
|
|
448
|
+
|
|
143
449
|
const usage: DataSourceUsage = {
|
|
144
450
|
index: index++,
|
|
145
451
|
dataSourceIdentifier: parentDataSource.identifier,
|
|
@@ -153,10 +459,13 @@ function buildStateRegistry(uidlNode: any): StateRegistry {
|
|
|
153
459
|
perPage: effectivePerPage,
|
|
154
460
|
searchEnabled: !!content.searchEnabled,
|
|
155
461
|
searchDebounce: content.searchDebounce || 300,
|
|
462
|
+
searchDefaultValue,
|
|
156
463
|
queryColumns,
|
|
157
464
|
sorts,
|
|
465
|
+
dynamicSort,
|
|
158
466
|
filters,
|
|
159
467
|
filterStateIds,
|
|
468
|
+
filterUrlSearchParamKeys,
|
|
160
469
|
category: 'plain',
|
|
161
470
|
}
|
|
162
471
|
|
|
@@ -419,14 +728,18 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
419
728
|
types.callExpression(types.identifier('useState'), [
|
|
420
729
|
types.objectExpression([
|
|
421
730
|
types.objectProperty(types.identifier('page'), types.numericLiteral(1)),
|
|
422
|
-
types.objectProperty(
|
|
731
|
+
types.objectProperty(
|
|
732
|
+
types.identifier('debouncedQuery'),
|
|
733
|
+
searchDefaultValueInitAST(usage.searchDefaultValue)
|
|
734
|
+
),
|
|
423
735
|
]),
|
|
424
736
|
])
|
|
425
737
|
),
|
|
426
738
|
])
|
|
427
739
|
)
|
|
428
740
|
|
|
429
|
-
// Immediate search query state
|
|
741
|
+
// Immediate search query state — seeded with `searchDefaultValue`
|
|
742
|
+
// when provided so the input is pre-filled on mount.
|
|
430
743
|
stateDeclarations.push(
|
|
431
744
|
types.variableDeclaration('const', [
|
|
432
745
|
types.variableDeclarator(
|
|
@@ -434,7 +747,9 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
434
747
|
types.identifier(vars.searchQueryVar),
|
|
435
748
|
types.identifier(vars.setSearchQueryVar),
|
|
436
749
|
]),
|
|
437
|
-
types.callExpression(types.identifier('useState'), [
|
|
750
|
+
types.callExpression(types.identifier('useState'), [
|
|
751
|
+
searchDefaultValueInitAST(usage.searchDefaultValue),
|
|
752
|
+
])
|
|
438
753
|
),
|
|
439
754
|
])
|
|
440
755
|
)
|
|
@@ -561,36 +876,7 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
561
876
|
)
|
|
562
877
|
}
|
|
563
878
|
// Add filters to count fetch params if present
|
|
564
|
-
|
|
565
|
-
urlParams.push(
|
|
566
|
-
types.objectProperty(
|
|
567
|
-
types.identifier('filters'),
|
|
568
|
-
types.callExpression(
|
|
569
|
-
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
570
|
-
[
|
|
571
|
-
types.arrayExpression(
|
|
572
|
-
usage.filters.map((filter: any) =>
|
|
573
|
-
types.objectExpression([
|
|
574
|
-
types.objectProperty(
|
|
575
|
-
types.identifier('source'),
|
|
576
|
-
types.stringLiteral(filter.source || '')
|
|
577
|
-
),
|
|
578
|
-
types.objectProperty(
|
|
579
|
-
types.identifier('destination'),
|
|
580
|
-
ASTUtils.convertFilterDestinationToExpression(filter.destination)
|
|
581
|
-
),
|
|
582
|
-
types.objectProperty(
|
|
583
|
-
types.identifier('operand'),
|
|
584
|
-
types.stringLiteral(filter.operand || '')
|
|
585
|
-
),
|
|
586
|
-
])
|
|
587
|
-
)
|
|
588
|
-
),
|
|
589
|
-
]
|
|
590
|
-
)
|
|
591
|
-
)
|
|
592
|
-
)
|
|
593
|
-
}
|
|
879
|
+
appendFiltersParam(urlParams, usage.filters, buildFilterDestinationExpression)
|
|
594
880
|
|
|
595
881
|
// Build the count fetch effect body
|
|
596
882
|
const countFetchEffectBody: types.Statement[] = []
|
|
@@ -712,16 +998,26 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
712
998
|
)
|
|
713
999
|
)
|
|
714
1000
|
|
|
1001
|
+
const countEffectDeps: types.Expression[] = [
|
|
1002
|
+
types.memberExpression(
|
|
1003
|
+
types.identifier(vars.combinedStateVar),
|
|
1004
|
+
types.identifier('debouncedQuery')
|
|
1005
|
+
),
|
|
1006
|
+
]
|
|
1007
|
+
// Refresh the count whenever a state-bound filter destination changes
|
|
1008
|
+
// (e.g. user picks a category) so pagination tracks the filtered
|
|
1009
|
+
// result-set, not the mount-time unfiltered total. Without these
|
|
1010
|
+
// deps, ds_0_maxPages stays at the original count and the "Next"
|
|
1011
|
+
// button stays enabled past the actual last page of the filtered
|
|
1012
|
+
// results — letting the user click into empty pages.
|
|
1013
|
+
pushStateIdsAsDeps(countEffectDeps, new Set<string>(), usage.filterStateIds)
|
|
1014
|
+
// Same goes for URL-driven filters (already documented above).
|
|
1015
|
+
pushUrlSearchParamMemoDeps(countEffectDeps, usage)
|
|
715
1016
|
effectStatements.push(
|
|
716
1017
|
types.expressionStatement(
|
|
717
1018
|
types.callExpression(types.identifier('useEffect'), [
|
|
718
1019
|
types.arrowFunctionExpression([], types.blockStatement(countFetchEffectBody)),
|
|
719
|
-
types.arrayExpression(
|
|
720
|
-
types.memberExpression(
|
|
721
|
-
types.identifier(vars.combinedStateVar),
|
|
722
|
-
types.identifier('debouncedQuery')
|
|
723
|
-
),
|
|
724
|
-
]),
|
|
1020
|
+
types.arrayExpression(countEffectDeps),
|
|
725
1021
|
])
|
|
726
1022
|
)
|
|
727
1023
|
)
|
|
@@ -862,7 +1158,18 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
862
1158
|
),
|
|
863
1159
|
])
|
|
864
1160
|
),
|
|
865
|
-
|
|
1161
|
+
// Default to mount-only; refresh when ANY filter destination
|
|
1162
|
+
// changes — state-bound (e.g. `selectedCategory`) and
|
|
1163
|
+
// URL-driven — so the pagination control reflects the current
|
|
1164
|
+
// filtered count instead of the unfiltered mount-time total.
|
|
1165
|
+
types.arrayExpression(
|
|
1166
|
+
((): types.Expression[] => {
|
|
1167
|
+
const deps: types.Expression[] = []
|
|
1168
|
+
pushStateIdsAsDeps(deps, new Set<string>(), usage.filterStateIds)
|
|
1169
|
+
pushUrlSearchParamMemoDeps(deps, usage)
|
|
1170
|
+
return deps
|
|
1171
|
+
})()
|
|
1172
|
+
),
|
|
866
1173
|
])
|
|
867
1174
|
)
|
|
868
1175
|
)
|
|
@@ -885,7 +1192,9 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
885
1192
|
types.identifier(vars.debouncedSearchQueryVar),
|
|
886
1193
|
types.identifier(vars.setDebouncedSearchQueryVar),
|
|
887
1194
|
]),
|
|
888
|
-
types.callExpression(types.identifier('useState'), [
|
|
1195
|
+
types.callExpression(types.identifier('useState'), [
|
|
1196
|
+
searchDefaultValueInitAST(usage.searchDefaultValue),
|
|
1197
|
+
])
|
|
889
1198
|
),
|
|
890
1199
|
])
|
|
891
1200
|
)
|
|
@@ -897,7 +1206,9 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
897
1206
|
types.identifier(vars.searchQueryVar),
|
|
898
1207
|
types.identifier(vars.setSearchQueryVar),
|
|
899
1208
|
]),
|
|
900
|
-
types.callExpression(types.identifier('useState'), [
|
|
1209
|
+
types.callExpression(types.identifier('useState'), [
|
|
1210
|
+
searchDefaultValueInitAST(usage.searchDefaultValue),
|
|
1211
|
+
])
|
|
901
1212
|
),
|
|
902
1213
|
])
|
|
903
1214
|
)
|
|
@@ -967,6 +1278,57 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
967
1278
|
// Insert state declarations at the beginning
|
|
968
1279
|
stateDeclarations.reverse().forEach((s) => blockStatement.body.unshift(s))
|
|
969
1280
|
|
|
1281
|
+
// Inject `const router = useRouter()` at the very top of the component
|
|
1282
|
+
// body whenever any usage's filters reference URL search params (e.g.
|
|
1283
|
+
// a navlink that bakes `?categoryFilter=Rings` into the href). The
|
|
1284
|
+
// filter destination expressions emitted earlier reference `router.query`
|
|
1285
|
+
// directly, so the symbol must be in scope by the time the component
|
|
1286
|
+
// body runs. We add the dependency + declaration here rather than
|
|
1287
|
+
// relying on the sibling `createNextUrlSearchParamsPlugin` because
|
|
1288
|
+
// (a) that plugin only fires when the UIDL declares `pageOptions.searchParams`,
|
|
1289
|
+
// and (b) component-level data fetches can use URL params even when
|
|
1290
|
+
// the page itself has no `searchParams` definition (e.g. a navigation
|
|
1291
|
+
// component embedded on a page that didn't author the param). The
|
|
1292
|
+
// `body.some(isUseRouterDecl)` guard keeps us idempotent with both the
|
|
1293
|
+
// search-params plugin and the i18n plugin, both of which may have
|
|
1294
|
+
// already unshifted the same declaration.
|
|
1295
|
+
const needsUseRouter = registry.usages.some((u) => hasUrlSearchParamFilters(u))
|
|
1296
|
+
if (needsUseRouter) {
|
|
1297
|
+
if (!dependencies.useRouter) {
|
|
1298
|
+
// Match the shape the sibling Next.js plugins use (i18n locale mapper,
|
|
1299
|
+
// search-params plugin) so the deduped import line is identical and
|
|
1300
|
+
// the dependency-resolver merges instead of emitting a second one.
|
|
1301
|
+
dependencies.useRouter = {
|
|
1302
|
+
type: 'library',
|
|
1303
|
+
path: 'next/router',
|
|
1304
|
+
version: '^12.1.10',
|
|
1305
|
+
meta: { namedImport: true },
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
const hasRouterDecl = blockStatement.body.some(
|
|
1309
|
+
(statement) =>
|
|
1310
|
+
statement.type === 'VariableDeclaration' &&
|
|
1311
|
+
statement.declarations.some(
|
|
1312
|
+
(decl) =>
|
|
1313
|
+
decl.id.type === 'Identifier' &&
|
|
1314
|
+
decl.id.name === 'router' &&
|
|
1315
|
+
decl.init?.type === 'CallExpression' &&
|
|
1316
|
+
decl.init.callee.type === 'Identifier' &&
|
|
1317
|
+
decl.init.callee.name === 'useRouter'
|
|
1318
|
+
)
|
|
1319
|
+
)
|
|
1320
|
+
if (!hasRouterDecl) {
|
|
1321
|
+
blockStatement.body.unshift(
|
|
1322
|
+
types.variableDeclaration('const', [
|
|
1323
|
+
types.variableDeclarator(
|
|
1324
|
+
types.identifier('router'),
|
|
1325
|
+
types.callExpression(types.identifier('useRouter'), [])
|
|
1326
|
+
),
|
|
1327
|
+
])
|
|
1328
|
+
)
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
|
|
970
1332
|
// Insert effects before return statement
|
|
971
1333
|
const returnIndex = blockStatement.body.findIndex((s: any) => s.type === 'ReturnStatement')
|
|
972
1334
|
const insertIndex = returnIndex !== -1 ? returnIndex : blockStatement.body.length
|
|
@@ -1078,7 +1440,7 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
|
|
|
1078
1440
|
|
|
1079
1441
|
// STEP 6: Update getStaticProps if this is a page
|
|
1080
1442
|
if (isPage) {
|
|
1081
|
-
updateGetStaticProps(chunks, registry, dependencies)
|
|
1443
|
+
updateGetStaticProps(chunks, registry, dependencies, uidl.outputOptions?.folderPath)
|
|
1082
1444
|
}
|
|
1083
1445
|
|
|
1084
1446
|
return structure
|
|
@@ -1326,71 +1688,20 @@ function updateDataProviderForPaginatedSearch(
|
|
|
1326
1688
|
)
|
|
1327
1689
|
}
|
|
1328
1690
|
|
|
1329
|
-
// Add sorts if present
|
|
1330
|
-
|
|
1331
|
-
paramsProps.push(
|
|
1332
|
-
types.objectProperty(
|
|
1333
|
-
types.identifier('sorts'),
|
|
1334
|
-
types.callExpression(
|
|
1335
|
-
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
1336
|
-
[
|
|
1337
|
-
types.arrayExpression(
|
|
1338
|
-
usage.sorts.map((sort: any) =>
|
|
1339
|
-
types.objectExpression([
|
|
1340
|
-
types.objectProperty(
|
|
1341
|
-
types.identifier('field'),
|
|
1342
|
-
types.stringLiteral(sort.field || '')
|
|
1343
|
-
),
|
|
1344
|
-
types.objectProperty(
|
|
1345
|
-
types.identifier('order'),
|
|
1346
|
-
types.stringLiteral(sort.order || '')
|
|
1347
|
-
),
|
|
1348
|
-
])
|
|
1349
|
-
)
|
|
1350
|
-
),
|
|
1351
|
-
]
|
|
1352
|
-
)
|
|
1353
|
-
)
|
|
1354
|
-
)
|
|
1355
|
-
}
|
|
1691
|
+
// Add sorts if present (legacy static array wins; otherwise dynamic state-bound sort)
|
|
1692
|
+
appendSortsParam(paramsProps, usage.sorts, usage.dynamicSort)
|
|
1356
1693
|
|
|
1357
1694
|
// Add filters if present
|
|
1358
|
-
|
|
1359
|
-
paramsProps.push(
|
|
1360
|
-
types.objectProperty(
|
|
1361
|
-
types.identifier('filters'),
|
|
1362
|
-
types.callExpression(
|
|
1363
|
-
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
1364
|
-
[
|
|
1365
|
-
types.arrayExpression(
|
|
1366
|
-
usage.filters.map((filter: any) =>
|
|
1367
|
-
types.objectExpression([
|
|
1368
|
-
types.objectProperty(
|
|
1369
|
-
types.identifier('source'),
|
|
1370
|
-
types.stringLiteral(filter.source || '')
|
|
1371
|
-
),
|
|
1372
|
-
types.objectProperty(
|
|
1373
|
-
types.identifier('destination'),
|
|
1374
|
-
ASTUtils.convertFilterDestinationToExpression(filter.destination)
|
|
1375
|
-
),
|
|
1376
|
-
types.objectProperty(
|
|
1377
|
-
types.identifier('operand'),
|
|
1378
|
-
types.stringLiteral(filter.operand || '')
|
|
1379
|
-
),
|
|
1380
|
-
])
|
|
1381
|
-
)
|
|
1382
|
-
),
|
|
1383
|
-
]
|
|
1384
|
-
)
|
|
1385
|
-
)
|
|
1386
|
-
)
|
|
1387
|
-
}
|
|
1695
|
+
appendFiltersParam(paramsProps, usage.filters, buildFilterDestinationExpression)
|
|
1388
1696
|
|
|
1389
|
-
// Build useMemo dependencies including filter state IDs
|
|
1697
|
+
// Build useMemo dependencies including filter state IDs and dynamic sort state IDs
|
|
1390
1698
|
const memoDeps: types.Expression[] = [types.identifier(vars.combinedStateVar)]
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1699
|
+
const seenDeps = new Set<string>([vars.combinedStateVar])
|
|
1700
|
+
pushStateIdsAsDeps(memoDeps, seenDeps, usage.filterStateIds)
|
|
1701
|
+
if (usage.dynamicSort) {
|
|
1702
|
+
pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds)
|
|
1703
|
+
}
|
|
1704
|
+
pushUrlSearchParamMemoDeps(memoDeps, usage)
|
|
1394
1705
|
|
|
1395
1706
|
dp.openingElement.attributes.push(
|
|
1396
1707
|
types.jsxAttribute(
|
|
@@ -1404,42 +1715,56 @@ function updateDataProviderForPaginatedSearch(
|
|
|
1404
1715
|
)
|
|
1405
1716
|
)
|
|
1406
1717
|
|
|
1407
|
-
// Add initialData
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1718
|
+
// Add initialData. Skip the server-prefetched data entirely when a dynamic
|
|
1719
|
+
// state-bound sort is active — the prefetch ran without sort parameters, so
|
|
1720
|
+
// reusing it would mask the current sort state AND cause DataProvider to skip
|
|
1721
|
+
// the first client fetch (its internal guard only skips when initialData is
|
|
1722
|
+
// defined on mount). Without that skip, toggling sort correctly triggers a
|
|
1723
|
+
// refetch via the useMemo params dependency chain.
|
|
1724
|
+
if (!usage.dynamicSort) {
|
|
1725
|
+
const initialDataCondition = wrapInitialDataWithUrlFilterGuard(
|
|
1726
|
+
types.logicalExpression(
|
|
1727
|
+
'&&',
|
|
1728
|
+
types.binaryExpression(
|
|
1729
|
+
'===',
|
|
1730
|
+
types.memberExpression(types.identifier(vars.combinedStateVar), types.identifier('page')),
|
|
1731
|
+
types.numericLiteral(1)
|
|
1732
|
+
),
|
|
1733
|
+
types.unaryExpression(
|
|
1734
|
+
'!',
|
|
1735
|
+
types.memberExpression(
|
|
1736
|
+
types.identifier(vars.combinedStateVar),
|
|
1737
|
+
types.identifier('debouncedQuery')
|
|
1738
|
+
),
|
|
1739
|
+
true
|
|
1740
|
+
)
|
|
1420
1741
|
),
|
|
1421
|
-
|
|
1742
|
+
usage
|
|
1422
1743
|
)
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1744
|
+
dp.openingElement.attributes.push(
|
|
1745
|
+
types.jsxAttribute(
|
|
1746
|
+
types.jsxIdentifier('initialData'),
|
|
1747
|
+
types.jsxExpressionContainer(
|
|
1748
|
+
types.conditionalExpression(
|
|
1749
|
+
initialDataCondition,
|
|
1750
|
+
types.optionalMemberExpression(
|
|
1751
|
+
types.identifier('props'),
|
|
1752
|
+
types.identifier(vars.propsPrefix),
|
|
1753
|
+
false,
|
|
1754
|
+
true
|
|
1755
|
+
),
|
|
1756
|
+
types.identifier('undefined')
|
|
1757
|
+
)
|
|
1437
1758
|
)
|
|
1438
1759
|
)
|
|
1439
1760
|
)
|
|
1440
|
-
|
|
1761
|
+
}
|
|
1441
1762
|
|
|
1442
|
-
// Add key
|
|
1763
|
+
// Add key. Sort is intentionally NOT part of the key — a key change would
|
|
1764
|
+
// remount the DataProvider, and a fresh mount re-arms the internal
|
|
1765
|
+
// "skip-first-fetch-when-we-have-initialData" guard, which would prevent the
|
|
1766
|
+
// new sort params from reaching the fetcher. Leaving sort out of the key
|
|
1767
|
+
// lets the useMemo params identity change alone drive refetch.
|
|
1443
1768
|
dp.openingElement.attributes.push(
|
|
1444
1769
|
types.jsxAttribute(
|
|
1445
1770
|
types.jsxIdentifier('key'),
|
|
@@ -1501,71 +1826,20 @@ function updateDataProviderForPaginationOnly(
|
|
|
1501
1826
|
types.objectProperty(types.identifier('perPage'), types.numericLiteral(usage.perPage)),
|
|
1502
1827
|
]
|
|
1503
1828
|
|
|
1504
|
-
// Add sorts if present
|
|
1505
|
-
|
|
1506
|
-
paramsProps.push(
|
|
1507
|
-
types.objectProperty(
|
|
1508
|
-
types.identifier('sorts'),
|
|
1509
|
-
types.callExpression(
|
|
1510
|
-
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
1511
|
-
[
|
|
1512
|
-
types.arrayExpression(
|
|
1513
|
-
usage.sorts.map((sort: any) =>
|
|
1514
|
-
types.objectExpression([
|
|
1515
|
-
types.objectProperty(
|
|
1516
|
-
types.identifier('field'),
|
|
1517
|
-
types.stringLiteral(sort.field || '')
|
|
1518
|
-
),
|
|
1519
|
-
types.objectProperty(
|
|
1520
|
-
types.identifier('order'),
|
|
1521
|
-
types.stringLiteral(sort.order || '')
|
|
1522
|
-
),
|
|
1523
|
-
])
|
|
1524
|
-
)
|
|
1525
|
-
),
|
|
1526
|
-
]
|
|
1527
|
-
)
|
|
1528
|
-
)
|
|
1529
|
-
)
|
|
1530
|
-
}
|
|
1829
|
+
// Add sorts if present (legacy static array wins; otherwise dynamic state-bound sort)
|
|
1830
|
+
appendSortsParam(paramsProps, usage.sorts, usage.dynamicSort)
|
|
1531
1831
|
|
|
1532
1832
|
// Add filters if present
|
|
1533
|
-
|
|
1534
|
-
paramsProps.push(
|
|
1535
|
-
types.objectProperty(
|
|
1536
|
-
types.identifier('filters'),
|
|
1537
|
-
types.callExpression(
|
|
1538
|
-
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
1539
|
-
[
|
|
1540
|
-
types.arrayExpression(
|
|
1541
|
-
usage.filters.map((filter: any) =>
|
|
1542
|
-
types.objectExpression([
|
|
1543
|
-
types.objectProperty(
|
|
1544
|
-
types.identifier('source'),
|
|
1545
|
-
types.stringLiteral(filter.source || '')
|
|
1546
|
-
),
|
|
1547
|
-
types.objectProperty(
|
|
1548
|
-
types.identifier('destination'),
|
|
1549
|
-
ASTUtils.convertFilterDestinationToExpression(filter.destination)
|
|
1550
|
-
),
|
|
1551
|
-
types.objectProperty(
|
|
1552
|
-
types.identifier('operand'),
|
|
1553
|
-
types.stringLiteral(filter.operand || '')
|
|
1554
|
-
),
|
|
1555
|
-
])
|
|
1556
|
-
)
|
|
1557
|
-
),
|
|
1558
|
-
]
|
|
1559
|
-
)
|
|
1560
|
-
)
|
|
1561
|
-
)
|
|
1562
|
-
}
|
|
1833
|
+
appendFiltersParam(paramsProps, usage.filters, buildFilterDestinationExpression)
|
|
1563
1834
|
|
|
1564
|
-
// Build useMemo dependencies including filter state IDs
|
|
1835
|
+
// Build useMemo dependencies including filter state IDs and dynamic sort state IDs
|
|
1565
1836
|
const memoDeps: types.Expression[] = [types.identifier(vars.pageStateVar)]
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1837
|
+
const seenDeps = new Set<string>([vars.pageStateVar])
|
|
1838
|
+
pushStateIdsAsDeps(memoDeps, seenDeps, usage.filterStateIds)
|
|
1839
|
+
if (usage.dynamicSort) {
|
|
1840
|
+
pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds)
|
|
1841
|
+
}
|
|
1842
|
+
pushUrlSearchParamMemoDeps(memoDeps, usage)
|
|
1569
1843
|
|
|
1570
1844
|
// Add params
|
|
1571
1845
|
dp.openingElement.attributes.push(
|
|
@@ -1580,30 +1854,36 @@ function updateDataProviderForPaginationOnly(
|
|
|
1580
1854
|
)
|
|
1581
1855
|
)
|
|
1582
1856
|
|
|
1583
|
-
// Add initialData
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
types.
|
|
1588
|
-
types.
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1857
|
+
// Add initialData. See paginated+search updater for why we skip prefetch
|
|
1858
|
+
// reuse when a dynamic state-bound sort is active.
|
|
1859
|
+
if (!usage.dynamicSort) {
|
|
1860
|
+
dp.openingElement.attributes.push(
|
|
1861
|
+
types.jsxAttribute(
|
|
1862
|
+
types.jsxIdentifier('initialData'),
|
|
1863
|
+
types.jsxExpressionContainer(
|
|
1864
|
+
types.conditionalExpression(
|
|
1865
|
+
wrapInitialDataWithUrlFilterGuard(
|
|
1866
|
+
types.binaryExpression(
|
|
1867
|
+
'===',
|
|
1868
|
+
types.identifier(vars.pageStateVar),
|
|
1869
|
+
types.numericLiteral(1)
|
|
1870
|
+
),
|
|
1871
|
+
usage
|
|
1872
|
+
),
|
|
1873
|
+
types.optionalMemberExpression(
|
|
1874
|
+
types.identifier('props'),
|
|
1875
|
+
types.identifier(vars.propsPrefix),
|
|
1876
|
+
false,
|
|
1877
|
+
true
|
|
1878
|
+
),
|
|
1879
|
+
types.identifier('undefined')
|
|
1880
|
+
)
|
|
1601
1881
|
)
|
|
1602
1882
|
)
|
|
1603
1883
|
)
|
|
1604
|
-
|
|
1884
|
+
}
|
|
1605
1885
|
|
|
1606
|
-
// Add key
|
|
1886
|
+
// Add key — sort is intentionally NOT included; see paginated+search updater.
|
|
1607
1887
|
dp.openingElement.attributes.push(
|
|
1608
1888
|
types.jsxAttribute(
|
|
1609
1889
|
types.jsxIdentifier('key'),
|
|
@@ -1664,71 +1944,20 @@ function updateDataProviderForSearchOnly(
|
|
|
1664
1944
|
)
|
|
1665
1945
|
}
|
|
1666
1946
|
|
|
1667
|
-
// Add sorts if present
|
|
1668
|
-
|
|
1669
|
-
paramsProps.push(
|
|
1670
|
-
types.objectProperty(
|
|
1671
|
-
types.identifier('sorts'),
|
|
1672
|
-
types.callExpression(
|
|
1673
|
-
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
1674
|
-
[
|
|
1675
|
-
types.arrayExpression(
|
|
1676
|
-
usage.sorts.map((sort: any) =>
|
|
1677
|
-
types.objectExpression([
|
|
1678
|
-
types.objectProperty(
|
|
1679
|
-
types.identifier('field'),
|
|
1680
|
-
types.stringLiteral(sort.field || '')
|
|
1681
|
-
),
|
|
1682
|
-
types.objectProperty(
|
|
1683
|
-
types.identifier('order'),
|
|
1684
|
-
types.stringLiteral(sort.order || '')
|
|
1685
|
-
),
|
|
1686
|
-
])
|
|
1687
|
-
)
|
|
1688
|
-
),
|
|
1689
|
-
]
|
|
1690
|
-
)
|
|
1691
|
-
)
|
|
1692
|
-
)
|
|
1693
|
-
}
|
|
1947
|
+
// Add sorts if present (legacy static array wins; otherwise dynamic state-bound sort)
|
|
1948
|
+
appendSortsParam(paramsProps, usage.sorts, usage.dynamicSort)
|
|
1694
1949
|
|
|
1695
1950
|
// Add filters if present
|
|
1696
|
-
|
|
1697
|
-
paramsProps.push(
|
|
1698
|
-
types.objectProperty(
|
|
1699
|
-
types.identifier('filters'),
|
|
1700
|
-
types.callExpression(
|
|
1701
|
-
types.memberExpression(types.identifier('JSON'), types.identifier('stringify')),
|
|
1702
|
-
[
|
|
1703
|
-
types.arrayExpression(
|
|
1704
|
-
usage.filters.map((filter: any) =>
|
|
1705
|
-
types.objectExpression([
|
|
1706
|
-
types.objectProperty(
|
|
1707
|
-
types.identifier('source'),
|
|
1708
|
-
types.stringLiteral(filter.source || '')
|
|
1709
|
-
),
|
|
1710
|
-
types.objectProperty(
|
|
1711
|
-
types.identifier('destination'),
|
|
1712
|
-
ASTUtils.convertFilterDestinationToExpression(filter.destination)
|
|
1713
|
-
),
|
|
1714
|
-
types.objectProperty(
|
|
1715
|
-
types.identifier('operand'),
|
|
1716
|
-
types.stringLiteral(filter.operand || '')
|
|
1717
|
-
),
|
|
1718
|
-
])
|
|
1719
|
-
)
|
|
1720
|
-
),
|
|
1721
|
-
]
|
|
1722
|
-
)
|
|
1723
|
-
)
|
|
1724
|
-
)
|
|
1725
|
-
}
|
|
1951
|
+
appendFiltersParam(paramsProps, usage.filters, buildFilterDestinationExpression)
|
|
1726
1952
|
|
|
1727
|
-
// Build useMemo dependencies including filter state IDs
|
|
1953
|
+
// Build useMemo dependencies including filter state IDs and dynamic sort state IDs
|
|
1728
1954
|
const memoDeps: types.Expression[] = [types.identifier(vars.debouncedSearchQueryVar)]
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1955
|
+
const seenDeps = new Set<string>([vars.debouncedSearchQueryVar])
|
|
1956
|
+
pushStateIdsAsDeps(memoDeps, seenDeps, usage.filterStateIds)
|
|
1957
|
+
if (usage.dynamicSort) {
|
|
1958
|
+
pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds)
|
|
1959
|
+
}
|
|
1960
|
+
pushUrlSearchParamMemoDeps(memoDeps, usage)
|
|
1732
1961
|
|
|
1733
1962
|
dp.openingElement.attributes.push(
|
|
1734
1963
|
types.jsxAttribute(
|
|
@@ -1742,26 +1971,32 @@ function updateDataProviderForSearchOnly(
|
|
|
1742
1971
|
)
|
|
1743
1972
|
)
|
|
1744
1973
|
|
|
1745
|
-
// Add initialData
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
types.
|
|
1750
|
-
types.
|
|
1751
|
-
|
|
1752
|
-
types.
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1974
|
+
// Add initialData. See paginated+search updater for why we skip prefetch
|
|
1975
|
+
// reuse when a dynamic state-bound sort is active.
|
|
1976
|
+
if (!usage.dynamicSort) {
|
|
1977
|
+
dp.openingElement.attributes.push(
|
|
1978
|
+
types.jsxAttribute(
|
|
1979
|
+
types.jsxIdentifier('initialData'),
|
|
1980
|
+
types.jsxExpressionContainer(
|
|
1981
|
+
types.conditionalExpression(
|
|
1982
|
+
wrapInitialDataWithUrlFilterGuard(
|
|
1983
|
+
types.unaryExpression('!', types.identifier(vars.debouncedSearchQueryVar), true),
|
|
1984
|
+
usage
|
|
1985
|
+
),
|
|
1986
|
+
types.optionalMemberExpression(
|
|
1987
|
+
types.identifier('props'),
|
|
1988
|
+
types.identifier(vars.propsPrefix),
|
|
1989
|
+
false,
|
|
1990
|
+
true
|
|
1991
|
+
),
|
|
1992
|
+
types.identifier('undefined')
|
|
1993
|
+
)
|
|
1759
1994
|
)
|
|
1760
1995
|
)
|
|
1761
1996
|
)
|
|
1762
|
-
|
|
1997
|
+
}
|
|
1763
1998
|
|
|
1764
|
-
// Add key
|
|
1999
|
+
// Add key — sort is intentionally NOT included; see paginated+search updater.
|
|
1765
2000
|
dp.openingElement.attributes.push(
|
|
1766
2001
|
types.jsxAttribute(
|
|
1767
2002
|
types.jsxIdentifier('key'),
|
|
@@ -1849,9 +2084,8 @@ function updateDataProviderForPlain(dp: any, fileName: string, usage: DataSource
|
|
|
1849
2084
|
|
|
1850
2085
|
// Build useMemo dependencies including filter state IDs
|
|
1851
2086
|
const memoDeps: types.Expression[] = []
|
|
1852
|
-
usage.filterStateIds
|
|
1853
|
-
|
|
1854
|
-
})
|
|
2087
|
+
pushStateIdsAsDeps(memoDeps, new Set<string>(), usage.filterStateIds)
|
|
2088
|
+
pushUrlSearchParamMemoDeps(memoDeps, usage)
|
|
1855
2089
|
|
|
1856
2090
|
// Wrap params in useMemo with filter state dependencies
|
|
1857
2091
|
const memoizedParams = types.callExpression(types.identifier('useMemo'), [
|
|
@@ -2348,7 +2582,8 @@ export default dataSourceModule.getCount
|
|
|
2348
2582
|
function updateGetStaticProps(
|
|
2349
2583
|
chunks: any[],
|
|
2350
2584
|
registry: StateRegistry,
|
|
2351
|
-
dependencies: Record<string, any
|
|
2585
|
+
dependencies: Record<string, any>,
|
|
2586
|
+
folderPath?: string[]
|
|
2352
2587
|
): void {
|
|
2353
2588
|
const getStaticPropsChunk = chunks.find((c) => c.name === 'getStaticProps')
|
|
2354
2589
|
if (!getStaticPropsChunk || getStaticPropsChunk.type !== ChunkType.AST) {
|
|
@@ -2565,9 +2800,11 @@ function updateGetStaticProps(
|
|
|
2565
2800
|
|
|
2566
2801
|
// Add import dependency for the fetcher
|
|
2567
2802
|
if (!dependencies[fetcherImportName]) {
|
|
2803
|
+
const depth = (folderPath ? folderPath.length : 0) + 1
|
|
2804
|
+
const relativePrefix = '../'.repeat(depth)
|
|
2568
2805
|
dependencies[fetcherImportName] = {
|
|
2569
2806
|
type: 'local',
|
|
2570
|
-
path:
|
|
2807
|
+
path: `${relativePrefix}utils/data-sources/${fileName}`,
|
|
2571
2808
|
}
|
|
2572
2809
|
}
|
|
2573
2810
|
|
|
@@ -2781,9 +3018,11 @@ function updateGetStaticProps(
|
|
|
2781
3018
|
|
|
2782
3019
|
// Add import dependency for the fetcher
|
|
2783
3020
|
if (!dependencies[fetcherImportName]) {
|
|
3021
|
+
const depth = (folderPath ? folderPath.length : 0) + 1
|
|
3022
|
+
const relativePrefix = '../'.repeat(depth)
|
|
2784
3023
|
dependencies[fetcherImportName] = {
|
|
2785
3024
|
type: 'local',
|
|
2786
|
-
path:
|
|
3025
|
+
path: `${relativePrefix}utils/data-sources/${fileName}`,
|
|
2787
3026
|
}
|
|
2788
3027
|
}
|
|
2789
3028
|
}
|