@teleporthq/teleport-plugin-next-data-source 0.43.14 → 0.43.17

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.
@@ -109,3 +109,29 @@ export const pushStateIdsAsDeps = (
109
109
  deps.push(types.identifier(id))
110
110
  }
111
111
  }
112
+
113
+ // Appends each id in `propIds` as a `props.<id>` MemberExpression onto `deps`,
114
+ // skipping any already tracked in `seen` (keyed by `props.<id>` so a prop and a
115
+ // same-named state var never collide). Mutates both `deps` and `seen`.
116
+ //
117
+ // Prop-bound filter destinations resolve to `props.<id>` on the VALUE side (see
118
+ // `convertFilterDestinationToExpression` in teleport-plugin-common), so the
119
+ // useMemo/useEffect dependency MUST reference the exact same `props.<id>`
120
+ // member. Emitting a bare `<id>` identifier (as `pushStateIdsAsDeps` does for
121
+ // state) references an undeclared symbol — props are never destructured into
122
+ // the component scope — which crashed prerender with
123
+ // `ReferenceError: <id> is not defined`.
124
+ export const pushPropIdsAsDeps = (
125
+ deps: types.Expression[],
126
+ seen: Set<string>,
127
+ propIds: string[]
128
+ ): void => {
129
+ for (const id of propIds) {
130
+ const key = `props.${id}`
131
+ if (seen.has(key)) {
132
+ continue
133
+ }
134
+ seen.add(key)
135
+ deps.push(types.memberExpression(types.identifier('props'), types.identifier(id)))
136
+ }
137
+ }
@@ -11,7 +11,7 @@ import { ASTUtils } from '@teleporthq/teleport-plugin-common'
11
11
  import { generateSafeFileName } from './utils'
12
12
  import { generateDataSourceFetcherWithCore } from './data-source-fetchers'
13
13
  import { appendSortsParam, DynamicSortAST, extractDynamicSort } from './sort-utils'
14
- import { appendFiltersParam, pushStateIdsAsDeps } from './filter-utils'
14
+ import { appendFiltersParam, pushStateIdsAsDeps, pushPropIdsAsDeps } from './filter-utils'
15
15
 
16
16
  // ----- searchDefaultValue support -----
17
17
  //
@@ -109,10 +109,17 @@ interface DataSourceUsage {
109
109
  // tslint:disable-next-line:no-any
110
110
  filters: any[]
111
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.
112
+ // Only `state` references land here; their dep/guard expression is a bare
113
+ // identifier. `prop` refs go to `filterPropIds` (dep expression `props.<id>`)
114
+ // and `urlSearchParams` refs to `filterUrlSearchParamKeys` (dep expression
115
+ // `router.query.<key>`), because each resolves to a different scope.
115
116
  filterStateIds: string[]
117
+ // Prop IDs from dynamic filter destinations. Kept separate from
118
+ // `filterStateIds` so their dep/guard expression is `props.<id>` (matching the
119
+ // VALUE side in `convertFilterDestinationToExpression`) rather than a bare
120
+ // identifier — a bare `<id>` is undefined in the component scope and crashes
121
+ // prerender with `ReferenceError: <id> is not defined`.
122
+ filterPropIds: string[]
116
123
  // URL search-param keys referenced by filter destinations (e.g.
117
124
  // `'categoryFilter'`). Drives `const router = useRouter()` injection and
118
125
  // the corresponding `router.query.<key>` entries in `useMemo` deps so the
@@ -249,7 +256,11 @@ function pushUrlSearchParamMemoDeps(memoDeps: types.Expression[], usage: DataSou
249
256
  // callers fall back to the existing `props.X` expression unchanged so
250
257
  // non-filtered pages still benefit from the SSR prefetch.
251
258
  function buildNoUrlFilterGuard(usage: DataSourceUsage): types.Expression | null {
252
- if (usage.filterUrlSearchParamKeys.length === 0 && usage.filterStateIds.length === 0) {
259
+ if (
260
+ usage.filterUrlSearchParamKeys.length === 0 &&
261
+ usage.filterStateIds.length === 0 &&
262
+ usage.filterPropIds.length === 0
263
+ ) {
253
264
  return null
254
265
  }
255
266
  const guards: types.Expression[] = []
@@ -274,6 +285,18 @@ function buildNoUrlFilterGuard(usage: DataSourceUsage): types.Expression | null
274
285
  for (const id of usage.filterStateIds) {
275
286
  guards.push(types.unaryExpression('!', types.identifier(id), true))
276
287
  }
288
+ // Prop-bound filter destinations read `props.<id>` (a constant per page
289
+ // instance), so the guard is `!props.<id>` — mirroring the value expression
290
+ // exactly so the bare-identifier ReferenceError can't reappear here.
291
+ for (const id of usage.filterPropIds) {
292
+ guards.push(
293
+ types.unaryExpression(
294
+ '!',
295
+ types.memberExpression(types.identifier('props'), types.identifier(id)),
296
+ true
297
+ )
298
+ )
299
+ }
277
300
  return guards.reduce((acc, next) => types.logicalExpression('&&', acc, next))
278
301
  }
279
302
 
@@ -417,6 +440,7 @@ function buildStateRegistry(uidlNode: any): StateRegistry {
417
440
  // `urlSearchParams` refs resolve to `router.query.<key>` and need a
418
441
  // `useRouter()` declaration injected separately.
419
442
  const filterStateIds: string[] = []
443
+ const filterPropIds: string[] = []
420
444
  for (const f of filters) {
421
445
  if (!ASTUtils.isUIDLDynamicReference(f.destination)) {
422
446
  continue
@@ -430,6 +454,13 @@ function buildStateRegistry(uidlNode: any): StateRegistry {
430
454
  if (destinationContent.referenceType === 'urlSearchParams') {
431
455
  continue
432
456
  }
457
+ // `prop` destinations resolve to `props.<id>` (see
458
+ // buildFilterDestinationExpression / convertFilterDestinationToExpression),
459
+ // so they must NOT be emitted as bare deps. Route them to filterPropIds.
460
+ if (destinationContent.referenceType === 'prop') {
461
+ filterPropIds.push(destinationContent.id)
462
+ continue
463
+ }
433
464
  filterStateIds.push(destinationContent.id)
434
465
  }
435
466
  const filterUrlSearchParamKeys: string[] = collectFilterUrlSearchParamKeys(filters)
@@ -465,6 +496,7 @@ function buildStateRegistry(uidlNode: any): StateRegistry {
465
496
  dynamicSort,
466
497
  filters,
467
498
  filterStateIds,
499
+ filterPropIds,
468
500
  filterUrlSearchParamKeys,
469
501
  category: 'plain',
470
502
  }
@@ -1010,7 +1042,9 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1010
1042
  // deps, ds_0_maxPages stays at the original count and the "Next"
1011
1043
  // button stays enabled past the actual last page of the filtered
1012
1044
  // results — letting the user click into empty pages.
1013
- pushStateIdsAsDeps(countEffectDeps, new Set<string>(), usage.filterStateIds)
1045
+ const countEffectSeen = new Set<string>()
1046
+ pushStateIdsAsDeps(countEffectDeps, countEffectSeen, usage.filterStateIds)
1047
+ pushPropIdsAsDeps(countEffectDeps, countEffectSeen, usage.filterPropIds)
1014
1048
  // Same goes for URL-driven filters (already documented above).
1015
1049
  pushUrlSearchParamMemoDeps(countEffectDeps, usage)
1016
1050
  effectStatements.push(
@@ -1165,7 +1199,9 @@ export const createNextArrayMapperPaginationPlugin: ComponentPluginFactory<{}> =
1165
1199
  types.arrayExpression(
1166
1200
  ((): types.Expression[] => {
1167
1201
  const deps: types.Expression[] = []
1168
- pushStateIdsAsDeps(deps, new Set<string>(), usage.filterStateIds)
1202
+ const depsSeen = new Set<string>()
1203
+ pushStateIdsAsDeps(deps, depsSeen, usage.filterStateIds)
1204
+ pushPropIdsAsDeps(deps, depsSeen, usage.filterPropIds)
1169
1205
  pushUrlSearchParamMemoDeps(deps, usage)
1170
1206
  return deps
1171
1207
  })()
@@ -1698,6 +1734,7 @@ function updateDataProviderForPaginatedSearch(
1698
1734
  const memoDeps: types.Expression[] = [types.identifier(vars.combinedStateVar)]
1699
1735
  const seenDeps = new Set<string>([vars.combinedStateVar])
1700
1736
  pushStateIdsAsDeps(memoDeps, seenDeps, usage.filterStateIds)
1737
+ pushPropIdsAsDeps(memoDeps, seenDeps, usage.filterPropIds)
1701
1738
  if (usage.dynamicSort) {
1702
1739
  pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds)
1703
1740
  }
@@ -1836,6 +1873,7 @@ function updateDataProviderForPaginationOnly(
1836
1873
  const memoDeps: types.Expression[] = [types.identifier(vars.pageStateVar)]
1837
1874
  const seenDeps = new Set<string>([vars.pageStateVar])
1838
1875
  pushStateIdsAsDeps(memoDeps, seenDeps, usage.filterStateIds)
1876
+ pushPropIdsAsDeps(memoDeps, seenDeps, usage.filterPropIds)
1839
1877
  if (usage.dynamicSort) {
1840
1878
  pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds)
1841
1879
  }
@@ -1954,6 +1992,7 @@ function updateDataProviderForSearchOnly(
1954
1992
  const memoDeps: types.Expression[] = [types.identifier(vars.debouncedSearchQueryVar)]
1955
1993
  const seenDeps = new Set<string>([vars.debouncedSearchQueryVar])
1956
1994
  pushStateIdsAsDeps(memoDeps, seenDeps, usage.filterStateIds)
1995
+ pushPropIdsAsDeps(memoDeps, seenDeps, usage.filterPropIds)
1957
1996
  if (usage.dynamicSort) {
1958
1997
  pushStateIdsAsDeps(memoDeps, seenDeps, usage.dynamicSort.depStateIds)
1959
1998
  }
@@ -2084,7 +2123,9 @@ function updateDataProviderForPlain(dp: any, fileName: string, usage: DataSource
2084
2123
 
2085
2124
  // Build useMemo dependencies including filter state IDs
2086
2125
  const memoDeps: types.Expression[] = []
2087
- pushStateIdsAsDeps(memoDeps, new Set<string>(), usage.filterStateIds)
2126
+ const memoSeen = new Set<string>()
2127
+ pushStateIdsAsDeps(memoDeps, memoSeen, usage.filterStateIds)
2128
+ pushPropIdsAsDeps(memoDeps, memoSeen, usage.filterPropIds)
2088
2129
  pushUrlSearchParamMemoDeps(memoDeps, usage)
2089
2130
 
2090
2131
  // Wrap params in useMemo with filter state dependencies