@teleporthq/teleport-plugin-next-data-source 0.43.66 → 0.43.67

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.
Files changed (179) hide show
  1. package/__tests__/array-mapper-cache-wiring.test.ts +11 -0
  2. package/__tests__/data-source-request-locale.test.ts +313 -0
  3. package/__tests__/gated-data-provider.test.ts +88 -0
  4. package/__tests__/localized-columns.test.ts +217 -0
  5. package/__tests__/numbered-pagination.test.ts +15 -0
  6. package/__tests__/product-filter-fields.ts +230 -0
  7. package/__tests__/product-filter-sql.fixture.json +7 -0
  8. package/__tests__/refetch-loading-state.test.ts +23 -0
  9. package/__tests__/search-clear-button.test.ts +122 -0
  10. package/dist/cjs/content-localization.d.ts +37 -0
  11. package/dist/cjs/content-localization.d.ts.map +1 -0
  12. package/dist/cjs/content-localization.js +29 -0
  13. package/dist/cjs/content-localization.js.map +1 -0
  14. package/dist/cjs/count-effect.d.ts +11 -1
  15. package/dist/cjs/count-effect.d.ts.map +1 -1
  16. package/dist/cjs/count-effect.js +30 -2
  17. package/dist/cjs/count-effect.js.map +1 -1
  18. package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
  19. package/dist/cjs/fetchers/airtable.js +1 -1
  20. package/dist/cjs/fetchers/airtable.js.map +1 -1
  21. package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
  22. package/dist/cjs/fetchers/clickhouse.js +1 -1
  23. package/dist/cjs/fetchers/clickhouse.js.map +1 -1
  24. package/dist/cjs/fetchers/firestore.d.ts.map +1 -1
  25. package/dist/cjs/fetchers/firestore.js +1 -1
  26. package/dist/cjs/fetchers/firestore.js.map +1 -1
  27. package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
  28. package/dist/cjs/fetchers/mariadb.js +1 -1
  29. package/dist/cjs/fetchers/mariadb.js.map +1 -1
  30. package/dist/cjs/fetchers/mongodb.d.ts.map +1 -1
  31. package/dist/cjs/fetchers/mongodb.js +1 -1
  32. package/dist/cjs/fetchers/mongodb.js.map +1 -1
  33. package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
  34. package/dist/cjs/fetchers/mysql.js +1 -1
  35. package/dist/cjs/fetchers/mysql.js.map +1 -1
  36. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
  37. package/dist/cjs/fetchers/postgresql.js +2 -1
  38. package/dist/cjs/fetchers/postgresql.js.map +1 -1
  39. package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
  40. package/dist/cjs/fetchers/redshift.js +1 -1
  41. package/dist/cjs/fetchers/redshift.js.map +1 -1
  42. package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
  43. package/dist/cjs/fetchers/teleport.js +10 -3
  44. package/dist/cjs/fetchers/teleport.js.map +1 -1
  45. package/dist/cjs/fetchers/turso.d.ts.map +1 -1
  46. package/dist/cjs/fetchers/turso.js +1 -1
  47. package/dist/cjs/fetchers/turso.js.map +1 -1
  48. package/dist/cjs/index.d.ts +5 -0
  49. package/dist/cjs/index.d.ts.map +1 -1
  50. package/dist/cjs/index.js +60 -8
  51. package/dist/cjs/index.js.map +1 -1
  52. package/dist/cjs/loading-state.d.ts +16 -1
  53. package/dist/cjs/loading-state.d.ts.map +1 -1
  54. package/dist/cjs/loading-state.js +69 -3
  55. package/dist/cjs/loading-state.js.map +1 -1
  56. package/dist/cjs/localized-columns.d.ts +56 -0
  57. package/dist/cjs/localized-columns.d.ts.map +1 -0
  58. package/dist/cjs/localized-columns.js +124 -0
  59. package/dist/cjs/localized-columns.js.map +1 -0
  60. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  61. package/dist/cjs/pagination-plugin.js +123 -45
  62. package/dist/cjs/pagination-plugin.js.map +1 -1
  63. package/dist/cjs/product-filter-fields.d.ts +87 -0
  64. package/dist/cjs/product-filter-fields.d.ts.map +1 -0
  65. package/dist/cjs/product-filter-fields.js +161 -0
  66. package/dist/cjs/product-filter-fields.js.map +1 -0
  67. package/dist/cjs/request-locale.d.ts +49 -0
  68. package/dist/cjs/request-locale.d.ts.map +1 -0
  69. package/dist/cjs/request-locale.js +130 -0
  70. package/dist/cjs/request-locale.js.map +1 -0
  71. package/dist/cjs/search-clear.d.ts +31 -0
  72. package/dist/cjs/search-clear.d.ts.map +1 -0
  73. package/dist/cjs/search-clear.js +103 -0
  74. package/dist/cjs/search-clear.js.map +1 -0
  75. package/dist/cjs/transformations/index.d.ts +16 -2
  76. package/dist/cjs/transformations/index.d.ts.map +1 -1
  77. package/dist/cjs/transformations/index.js +14 -2
  78. package/dist/cjs/transformations/index.js.map +1 -1
  79. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  80. package/dist/cjs/utils.d.ts +11 -1
  81. package/dist/cjs/utils.d.ts.map +1 -1
  82. package/dist/cjs/utils.js +59 -15
  83. package/dist/cjs/utils.js.map +1 -1
  84. package/dist/esm/content-localization.d.ts +37 -0
  85. package/dist/esm/content-localization.d.ts.map +1 -0
  86. package/dist/esm/content-localization.js +24 -0
  87. package/dist/esm/content-localization.js.map +1 -0
  88. package/dist/esm/count-effect.d.ts +11 -1
  89. package/dist/esm/count-effect.d.ts.map +1 -1
  90. package/dist/esm/count-effect.js +28 -1
  91. package/dist/esm/count-effect.js.map +1 -1
  92. package/dist/esm/fetchers/airtable.d.ts.map +1 -1
  93. package/dist/esm/fetchers/airtable.js +1 -1
  94. package/dist/esm/fetchers/airtable.js.map +1 -1
  95. package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
  96. package/dist/esm/fetchers/clickhouse.js +1 -1
  97. package/dist/esm/fetchers/clickhouse.js.map +1 -1
  98. package/dist/esm/fetchers/firestore.d.ts.map +1 -1
  99. package/dist/esm/fetchers/firestore.js +1 -1
  100. package/dist/esm/fetchers/firestore.js.map +1 -1
  101. package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
  102. package/dist/esm/fetchers/mariadb.js +1 -1
  103. package/dist/esm/fetchers/mariadb.js.map +1 -1
  104. package/dist/esm/fetchers/mongodb.d.ts.map +1 -1
  105. package/dist/esm/fetchers/mongodb.js +1 -1
  106. package/dist/esm/fetchers/mongodb.js.map +1 -1
  107. package/dist/esm/fetchers/mysql.d.ts.map +1 -1
  108. package/dist/esm/fetchers/mysql.js +1 -1
  109. package/dist/esm/fetchers/mysql.js.map +1 -1
  110. package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
  111. package/dist/esm/fetchers/postgresql.js +2 -1
  112. package/dist/esm/fetchers/postgresql.js.map +1 -1
  113. package/dist/esm/fetchers/redshift.d.ts.map +1 -1
  114. package/dist/esm/fetchers/redshift.js +1 -1
  115. package/dist/esm/fetchers/redshift.js.map +1 -1
  116. package/dist/esm/fetchers/teleport.d.ts.map +1 -1
  117. package/dist/esm/fetchers/teleport.js +10 -3
  118. package/dist/esm/fetchers/teleport.js.map +1 -1
  119. package/dist/esm/fetchers/turso.d.ts.map +1 -1
  120. package/dist/esm/fetchers/turso.js +1 -1
  121. package/dist/esm/fetchers/turso.js.map +1 -1
  122. package/dist/esm/index.d.ts +5 -0
  123. package/dist/esm/index.d.ts.map +1 -1
  124. package/dist/esm/index.js +55 -7
  125. package/dist/esm/index.js.map +1 -1
  126. package/dist/esm/loading-state.d.ts +16 -1
  127. package/dist/esm/loading-state.d.ts.map +1 -1
  128. package/dist/esm/loading-state.js +69 -3
  129. package/dist/esm/loading-state.js.map +1 -1
  130. package/dist/esm/localized-columns.d.ts +56 -0
  131. package/dist/esm/localized-columns.d.ts.map +1 -0
  132. package/dist/esm/localized-columns.js +119 -0
  133. package/dist/esm/localized-columns.js.map +1 -0
  134. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  135. package/dist/esm/pagination-plugin.js +124 -46
  136. package/dist/esm/pagination-plugin.js.map +1 -1
  137. package/dist/esm/product-filter-fields.d.ts +87 -0
  138. package/dist/esm/product-filter-fields.d.ts.map +1 -0
  139. package/dist/esm/product-filter-fields.js +154 -0
  140. package/dist/esm/product-filter-fields.js.map +1 -0
  141. package/dist/esm/request-locale.d.ts +49 -0
  142. package/dist/esm/request-locale.d.ts.map +1 -0
  143. package/dist/esm/request-locale.js +98 -0
  144. package/dist/esm/request-locale.js.map +1 -0
  145. package/dist/esm/search-clear.d.ts +31 -0
  146. package/dist/esm/search-clear.d.ts.map +1 -0
  147. package/dist/esm/search-clear.js +74 -0
  148. package/dist/esm/search-clear.js.map +1 -0
  149. package/dist/esm/transformations/index.d.ts +16 -2
  150. package/dist/esm/transformations/index.d.ts.map +1 -1
  151. package/dist/esm/transformations/index.js +14 -2
  152. package/dist/esm/transformations/index.js.map +1 -1
  153. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  154. package/dist/esm/utils.d.ts +11 -1
  155. package/dist/esm/utils.d.ts.map +1 -1
  156. package/dist/esm/utils.js +59 -15
  157. package/dist/esm/utils.js.map +1 -1
  158. package/package.json +5 -5
  159. package/src/content-localization.ts +53 -0
  160. package/src/count-effect.ts +53 -2
  161. package/src/fetchers/airtable.ts +4 -2
  162. package/src/fetchers/clickhouse.ts +4 -2
  163. package/src/fetchers/firestore.ts +4 -2
  164. package/src/fetchers/mariadb.ts +4 -2
  165. package/src/fetchers/mongodb.ts +4 -2
  166. package/src/fetchers/mysql.ts +4 -2
  167. package/src/fetchers/postgresql.ts +16 -2
  168. package/src/fetchers/redshift.ts +4 -2
  169. package/src/fetchers/teleport.ts +48 -9
  170. package/src/fetchers/turso.ts +4 -2
  171. package/src/index.ts +79 -11
  172. package/src/loading-state.ts +134 -3
  173. package/src/localized-columns.ts +133 -0
  174. package/src/pagination-plugin.ts +177 -53
  175. package/src/product-filter-fields.ts +173 -0
  176. package/src/request-locale.ts +113 -0
  177. package/src/search-clear.ts +102 -0
  178. package/src/transformations/index.ts +30 -5
  179. package/src/utils.ts +83 -15
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Filtering and sorting a translatable column in the visitor's language.
3
+ *
4
+ * A translatable column of a platform table exists once per language:
5
+ * `slug` holds the main language and `es_slug` the Spanish copy (see
6
+ * `ContentLocalization`). The row transform resolves what a page DISPLAYS —
7
+ * a Spanish listing shows `es_name` and links to `es_slug` — but the SQL a
8
+ * fetch runs still names the base column, so without this file:
9
+ *
10
+ * - the details page a Spanish card links to (`/es/products/<es_slug>`)
11
+ * looks the row up with `slug = $1` and answers 404;
12
+ * - "sort by name" on the Spanish listing orders the Spanish names by
13
+ * their ENGLISH counterparts.
14
+ *
15
+ * The teleport fetcher (`fetchers/teleport.ts`) therefore embeds the helper
16
+ * emitted here:
17
+ *
18
+ * - equality on a translatable column matches the value in ANY language:
19
+ * `("slug" = $1 OR "es_slug" = $1)`. Deliberately every language, not the
20
+ * request's: the language switcher keeps the URL and changes the locale,
21
+ * so `/products/<es_slug>` (English page, Spanish slug) has to resolve as
22
+ * well as `/es/products/<slug>`. Only `=` (and the `in` it is normalised
23
+ * to) — a translatable column is never compared with `<`, and `!=` on one
24
+ * has no sensible cross-language reading;
25
+ * - ordering by a translatable column orders by the REQUEST's language,
26
+ * falling back to the base column exactly like the transform does:
27
+ * `COALESCE(NULLIF("es_name", ''), "name")`.
28
+ *
29
+ * Which columns take part is decided at GENERATION time from the table's
30
+ * schema and the project's languages — never by prefix-sniffing column names,
31
+ * which would mistake `og_title` for a translation. A copy the schema does not
32
+ * list (a language added before a column became translatable) is simply not
33
+ * referenced, so the SQL can only ever name columns that exist.
34
+ */
35
+ import type { ContentLocalization } from './content-localization';
36
+ /**
37
+ * Base column → `{ locale: copy column }`, for every translatable column of a
38
+ * table that has at least one per-language copy in the schema.
39
+ */
40
+ export type LocalizedColumnMap = Record<string, Record<string, string>>;
41
+ /**
42
+ * Pairs every column with the per-language copies the schema actually holds.
43
+ * Empty (no families) when the project has one language or the table has no
44
+ * translated column — the emitted helper is then inert.
45
+ */
46
+ export declare const collectLocalizedColumns: (columnNames: string[], localization: ContentLocalization | undefined) => LocalizedColumnMap;
47
+ /**
48
+ * The `localizedEqualityClause` / `localizedSortFieldSql` helpers a fetcher
49
+ * embeds, with the column map baked in.
50
+ *
51
+ * Emitted as plain source with no backtick and no `${`, because the fetcher
52
+ * interpolates it into a template literal. Every identifier it quotes comes
53
+ * from the baked map, so a request can only ever name a column the schema had.
54
+ */
55
+ export declare const generateLocalizedColumnsHelper: (families: LocalizedColumnMap) => string;
56
+ //# sourceMappingURL=localized-columns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localized-columns.d.ts","sourceRoot":"","sources":["../../src/localized-columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAEjE;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AAQvE;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,gBACrB,MAAM,EAAE,gBACP,mBAAmB,GAAG,SAAS,KAC5C,kBAmBF,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,aAAc,kBAAkB,KAAG,MA8C7E,CAAA"}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ /**
3
+ * Filtering and sorting a translatable column in the visitor's language.
4
+ *
5
+ * A translatable column of a platform table exists once per language:
6
+ * `slug` holds the main language and `es_slug` the Spanish copy (see
7
+ * `ContentLocalization`). The row transform resolves what a page DISPLAYS —
8
+ * a Spanish listing shows `es_name` and links to `es_slug` — but the SQL a
9
+ * fetch runs still names the base column, so without this file:
10
+ *
11
+ * - the details page a Spanish card links to (`/es/products/<es_slug>`)
12
+ * looks the row up with `slug = $1` and answers 404;
13
+ * - "sort by name" on the Spanish listing orders the Spanish names by
14
+ * their ENGLISH counterparts.
15
+ *
16
+ * The teleport fetcher (`fetchers/teleport.ts`) therefore embeds the helper
17
+ * emitted here:
18
+ *
19
+ * - equality on a translatable column matches the value in ANY language:
20
+ * `("slug" = $1 OR "es_slug" = $1)`. Deliberately every language, not the
21
+ * request's: the language switcher keeps the URL and changes the locale,
22
+ * so `/products/<es_slug>` (English page, Spanish slug) has to resolve as
23
+ * well as `/es/products/<slug>`. Only `=` (and the `in` it is normalised
24
+ * to) — a translatable column is never compared with `<`, and `!=` on one
25
+ * has no sensible cross-language reading;
26
+ * - ordering by a translatable column orders by the REQUEST's language,
27
+ * falling back to the base column exactly like the transform does:
28
+ * `COALESCE(NULLIF("es_name", ''), "name")`.
29
+ *
30
+ * Which columns take part is decided at GENERATION time from the table's
31
+ * schema and the project's languages — never by prefix-sniffing column names,
32
+ * which would mistake `og_title` for a translation. A copy the schema does not
33
+ * list (a language added before a column became translatable) is simply not
34
+ * referenced, so the SQL can only ever name columns that exist.
35
+ */
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.generateLocalizedColumnsHelper = exports.collectLocalizedColumns = void 0;
38
+ /** The only identifiers the generated SQL is allowed to quote — same rule as the search columns. */
39
+ var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
40
+ /** The column holding `column` in `locale` — the editor's `getI18nColumnName`. */
41
+ var localizedColumnName = function (locale, column) { return "".concat(locale, "_").concat(column); };
42
+ /**
43
+ * Pairs every column with the per-language copies the schema actually holds.
44
+ * Empty (no families) when the project has one language or the table has no
45
+ * translated column — the emitted helper is then inert.
46
+ */
47
+ var collectLocalizedColumns = function (columnNames, localization) {
48
+ var families = {};
49
+ if (!localization || localization.secondaryLocales.length === 0) {
50
+ return families;
51
+ }
52
+ var present = new Set(columnNames.filter(function (name) { return SQL_IDENTIFIER_PATTERN.test(name); }));
53
+ present.forEach(function (column) {
54
+ localization.secondaryLocales.forEach(function (locale) {
55
+ var copy = localizedColumnName(locale, column);
56
+ if (!present.has(copy)) {
57
+ return;
58
+ }
59
+ if (!families[column]) {
60
+ families[column] = {};
61
+ }
62
+ families[column][locale] = copy;
63
+ });
64
+ });
65
+ return families;
66
+ };
67
+ exports.collectLocalizedColumns = collectLocalizedColumns;
68
+ /**
69
+ * The `localizedEqualityClause` / `localizedSortFieldSql` helpers a fetcher
70
+ * embeds, with the column map baked in.
71
+ *
72
+ * Emitted as plain source with no backtick and no `${`, because the fetcher
73
+ * interpolates it into a template literal. Every identifier it quotes comes
74
+ * from the baked map, so a request can only ever name a column the schema had.
75
+ */
76
+ var generateLocalizedColumnsHelper = function (families) {
77
+ var lines = [
78
+ '// Per-language copies of the translatable columns, base column -> locale -> copy.',
79
+ '// Baked from the table schema and the project languages at generation time.',
80
+ "var LOCALIZED_COLUMNS = ".concat(JSON.stringify(families)),
81
+ '',
82
+ 'function quoteLocalizedColumn(column) {',
83
+ " return '\"' + column + '\"'",
84
+ '}',
85
+ '',
86
+ '// Equality on a translatable column matches the value in ANY language, so a',
87
+ '// localized slug resolves whichever locale the URL is under. Answers null',
88
+ '// for every other column and operand, leaving them to the ordinary branches.',
89
+ 'function localizedEqualityClause(field, operand, value, pushParam) {',
90
+ ' var copies = LOCALIZED_COLUMNS[field]',
91
+ " if (!copies || operand !== '=' || value === null || value === undefined) return null",
92
+ ' // An empty set means "no filter", as it does in the ordinary array branch.',
93
+ ' if (Array.isArray(value) && value.length === 0) return null',
94
+ ' var columns = [field]',
95
+ ' for (var locale in copies) {',
96
+ ' if (Object.prototype.hasOwnProperty.call(copies, locale)) columns.push(copies[locale])',
97
+ ' }',
98
+ ' var param = Array.isArray(value) ? pushParam(value.map(String)) : pushParam(value)',
99
+ ' var clauses = []',
100
+ ' for (var i = 0; i < columns.length; i++) {',
101
+ ' clauses.push(',
102
+ ' Array.isArray(value)',
103
+ " ? quoteLocalizedColumn(columns[i]) + '::text = ANY(' + param + '::text[])'",
104
+ " : quoteLocalizedColumn(columns[i]) + ' = ' + param",
105
+ ' )',
106
+ ' }',
107
+ " return '(' + clauses.join(' OR ') + ')'",
108
+ '}',
109
+ '',
110
+ "// ORDER BY a translatable column in the request's language, falling back to",
111
+ '// the base column for a row with no translation — the same resolution the',
112
+ '// row transform applies to what is displayed. Null when the field or the',
113
+ '// locale has no copy, so the ordinary sort runs.',
114
+ 'function localizedSortFieldSql(field, locale) {',
115
+ ' var copies = LOCALIZED_COLUMNS[field]',
116
+ " var copy = copies && typeof locale === 'string' ? copies[locale] : null",
117
+ ' if (!copy) return null',
118
+ " return 'COALESCE(NULLIF(' + quoteLocalizedColumn(copy) + \", ''), \" + quoteLocalizedColumn(field) + ')'",
119
+ '}',
120
+ ];
121
+ return lines.join('\n');
122
+ };
123
+ exports.generateLocalizedColumnsHelper = generateLocalizedColumnsHelper;
124
+ //# sourceMappingURL=localized-columns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localized-columns.js","sourceRoot":"","sources":["../../src/localized-columns.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;;;AAUH,oGAAoG;AACpG,IAAM,sBAAsB,GAAG,0BAA0B,CAAA;AAEzD,kFAAkF;AAClF,IAAM,mBAAmB,GAAG,UAAC,MAAc,EAAE,MAAc,IAAa,OAAA,UAAG,MAAM,cAAI,MAAM,CAAE,EAArB,CAAqB,CAAA;AAE7F;;;;GAIG;AACI,IAAM,uBAAuB,GAAG,UACrC,WAAqB,EACrB,YAA6C;IAE7C,IAAM,QAAQ,GAAuB,EAAE,CAAA;IACvC,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE;QAC/D,OAAO,QAAQ,CAAA;KAChB;IACD,IAAM,OAAO,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,UAAC,IAAI,IAAK,OAAA,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAjC,CAAiC,CAAC,CAAC,CAAA;IACxF,OAAO,CAAC,OAAO,CAAC,UAAC,MAAM;QACrB,YAAY,CAAC,gBAAgB,CAAC,OAAO,CAAC,UAAC,MAAM;YAC3C,IAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;YAChD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;gBACtB,OAAM;aACP;YACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;gBACrB,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAA;aACtB;YACD,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;QACjC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IACF,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAtBY,QAAA,uBAAuB,2BAsBnC;AAED;;;;;;;GAOG;AACI,IAAM,8BAA8B,GAAG,UAAC,QAA4B;IACzE,IAAM,KAAK,GAAG;QACZ,oFAAoF;QACpF,8EAA8E;QAC9E,kCAA2B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAE;QACrD,EAAE;QACF,yCAAyC;QACzC,+BAA+B;QAC/B,GAAG;QACH,EAAE;QACF,8EAA8E;QAC9E,4EAA4E;QAC5E,+EAA+E;QAC/E,sEAAsE;QACtE,yCAAyC;QACzC,wFAAwF;QACxF,+EAA+E;QAC/E,+DAA+D;QAC/D,yBAAyB;QACzB,gCAAgC;QAChC,4FAA4F;QAC5F,KAAK;QACL,sFAAsF;QACtF,oBAAoB;QACpB,8CAA8C;QAC9C,mBAAmB;QACnB,4BAA4B;QAC5B,oFAAoF;QACpF,4DAA4D;QAC5D,OAAO;QACP,KAAK;QACL,2CAA2C;QAC3C,GAAG;QACH,EAAE;QACF,8EAA8E;QAC9E,4EAA4E;QAC5E,2EAA2E;QAC3E,mDAAmD;QACnD,iDAAiD;QACjD,yCAAyC;QACzC,2EAA2E;QAC3E,0BAA0B;QAC1B,4GAA4G;QAC5G,GAAG;KACJ,CAAA;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC,CAAA;AA9CY,QAAA,8BAA8B,kCA8C1C"}
@@ -1 +1 @@
1
- {"version":3,"file":"pagination-plugin.d.ts","sourceRoot":"","sources":["../../src/pagination-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,sBAAsB,EAGvB,MAAM,4BAA4B,CAAA;AA4iCnC,eAAO,MAAM,qCAAqC,EAAE,sBAAsB,CAAC,EAAE,CA40B5E,CAAA"}
1
+ {"version":3,"file":"pagination-plugin.d.ts","sourceRoot":"","sources":["../../src/pagination-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,sBAAsB,EAGvB,MAAM,4BAA4B,CAAA;AA4jCnC,eAAO,MAAM,qCAAqC,EAAE,sBAAsB,CAAC,EAAE,CAo1B5E,CAAA"}
@@ -84,10 +84,13 @@ var config_1 = require("./cache/config");
84
84
  var ast_1 = require("./cache/ast");
85
85
  var pagination_controls_1 = require("./pagination-controls");
86
86
  var count_effect_1 = require("./count-effect");
87
+ var search_clear_1 = require("./search-clear");
87
88
  var numbered_pagination_1 = require("./numbered-pagination");
88
89
  var infinite_scroll_1 = require("./infinite-scroll");
89
90
  var page_reset_1 = require("./page-reset");
90
91
  var page_url_sync_1 = require("./page-url-sync");
92
+ var content_localization_1 = require("./content-localization");
93
+ var request_locale_1 = require("./request-locale");
91
94
  // tslint:disable-next-line:no-any
92
95
  function parseSearchDefaultValue(raw) {
93
96
  if (!raw || typeof raw !== 'object') {
@@ -557,6 +560,7 @@ function getStateVarsForUsage(usage) {
557
560
  combinedStateVar: "ds_".concat(idx, "_state"),
558
561
  setCombinedStateVar: "setDs_".concat(idx, "_state"),
559
562
  skipDebounceRefVar: "ds_".concat(idx, "_skipDebounce"),
563
+ countSeqRefVar: "ds_".concat(idx, "_countSeq"),
560
564
  propsPrefix: "".concat(usage.dataSourceIdentifier, "_ds_").concat(idx),
561
565
  };
562
566
  }
@@ -769,7 +773,7 @@ function pushFilterSortPageResetEffect(stateDeclarations, effectStatements, usag
769
773
  // ==================== MAIN PLUGIN ====================
770
774
  var createNextArrayMapperPaginationPlugin = function () {
771
775
  var paginationPlugin = function (structure) { return __awaiter(void 0, void 0, void 0, function () {
772
- var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, registry, getStaticPropsChunk, isPage, stateDeclarations, effectStatements, needsUseRouter, hasRouterDecl, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, loadingStateDeclarations, usagesWithLoadingState, usageByProviderNode, cacheDeclarations, sharedOptions, cachedScopes, dataProvidersWithoutRepeaters, searchInputs, searchEnabledUsages;
776
+ var uidl, chunks, dependencies, options, componentChunk, variableDeclaration, declarator, arrowFunction, blockStatement, registry, getStaticPropsChunk, isPage, localized, stateDeclarations, effectStatements, needsUseRouter, returnIndex, insertIndex, dataProviders, dataProvidersWithRepeaters, usageIndexByDataSourceId, loadingStateDeclarations, usagesWithLoadingState, usageByProviderNode, cacheDeclarations, sharedOptions, cachedScopes, dataProvidersWithoutRepeaters, searchInputs, searchEnabledUsages, usageBySearchInput;
773
777
  var _a, _b;
774
778
  return __generator(this, function (_c) {
775
779
  uidl = structure.uidl, chunks = structure.chunks, dependencies = structure.dependencies, options = structure.options;
@@ -799,6 +803,7 @@ var createNextArrayMapperPaginationPlugin = function () {
799
803
  }
800
804
  getStaticPropsChunk = chunks.find(function (chunk) { return chunk.name === 'getStaticProps'; });
801
805
  isPage = !!getStaticPropsChunk;
806
+ localized = (0, content_localization_1.isLocalizedProject)(options);
802
807
  // Add React dependencies
803
808
  if (!dependencies.useState) {
804
809
  dependencies.useState = {
@@ -960,12 +965,14 @@ var createNextArrayMapperPaginationPlugin = function () {
960
965
  (0, filter_utils_1.pushPropIdsAsDeps)(countEffectDeps, countEffectSeen, usage.filterPropIds);
961
966
  // Same goes for URL-driven filters (already documented above).
962
967
  pushUrlSearchParamMemoDeps(countEffectDeps, usage);
968
+ stateDeclarations.push((0, count_effect_1.buildCountSeqDeclaration)(vars.countSeqRefVar));
963
969
  effectStatements.push((0, count_effect_1.buildCountFetchEffect)({
964
970
  fileName: fileName,
965
971
  perPage: usage.perPage,
966
972
  setMaxPagesVar: vars.setMaxPagesStateVar,
967
973
  urlParams: urlParams,
968
974
  deps: countEffectDeps,
975
+ countSeqRefVar: vars.countSeqRefVar,
969
976
  }));
970
977
  // Two-way URL sync for the search input when bound to a URL param.
971
978
  pushSearchUrlSyncEffects(effectStatements, usage, vars);
@@ -1025,12 +1032,14 @@ var createNextArrayMapperPaginationPlugin = function () {
1025
1032
  (0, filter_utils_1.pushStateIdsAsDeps)(countDeps, countDepsSeen, usage.filterStateIds);
1026
1033
  (0, filter_utils_1.pushPropIdsAsDeps)(countDeps, countDepsSeen, usage.filterPropIds);
1027
1034
  pushUrlSearchParamMemoDeps(countDeps, usage);
1035
+ stateDeclarations.push((0, count_effect_1.buildCountSeqDeclaration)(vars.countSeqRefVar));
1028
1036
  effectStatements.push((0, count_effect_1.buildCountFetchEffect)({
1029
1037
  fileName: fileName,
1030
1038
  perPage: usage.perPage,
1031
1039
  setMaxPagesVar: vars.setMaxPagesStateVar,
1032
1040
  urlParams: countUrlParams,
1033
1041
  deps: countDeps,
1042
+ countSeqRefVar: vars.countSeqRefVar,
1034
1043
  }));
1035
1044
  }
1036
1045
  // Visible page numbers, when this mapper draws a numbered strip.
@@ -1087,35 +1096,10 @@ var createNextArrayMapperPaginationPlugin = function () {
1087
1096
  });
1088
1097
  // Insert state declarations at the beginning
1089
1098
  stateDeclarations.reverse().forEach(function (s) { return blockStatement.body.unshift(s); });
1090
- needsUseRouter = registry.usages.some(function (u) { return hasUrlSearchParamFilters(u) || usageHasSearchUrlSync(u) || usageHasPageUrlSync(u); });
1099
+ needsUseRouter = localized ||
1100
+ registry.usages.some(function (u) { return hasUrlSearchParamFilters(u) || usageHasSearchUrlSync(u) || usageHasPageUrlSync(u); });
1091
1101
  if (needsUseRouter) {
1092
- if (!dependencies.useRouter) {
1093
- // Match the shape the sibling Next.js plugins use (i18n locale mapper,
1094
- // search-params plugin) so the deduped import line is identical and
1095
- // the dependency-resolver merges instead of emitting a second one.
1096
- dependencies.useRouter = {
1097
- type: 'library',
1098
- path: 'next/router',
1099
- version: '^12.1.10',
1100
- meta: { namedImport: true },
1101
- };
1102
- }
1103
- hasRouterDecl = blockStatement.body.some(function (statement) {
1104
- return statement.type === 'VariableDeclaration' &&
1105
- statement.declarations.some(function (decl) {
1106
- var _a;
1107
- return decl.id.type === 'Identifier' &&
1108
- decl.id.name === 'router' &&
1109
- ((_a = decl.init) === null || _a === void 0 ? void 0 : _a.type) === 'CallExpression' &&
1110
- decl.init.callee.type === 'Identifier' &&
1111
- decl.init.callee.name === 'useRouter';
1112
- });
1113
- });
1114
- if (!hasRouterDecl) {
1115
- blockStatement.body.unshift(types.variableDeclaration('const', [
1116
- types.variableDeclarator(types.identifier('router'), types.callExpression(types.identifier('useRouter'), [])),
1117
- ]));
1118
- }
1102
+ (0, request_locale_1.ensureRouterDeclaration)(blockStatement.body, dependencies);
1119
1103
  }
1120
1104
  // Both the search write-back and the page ⇄ URL effect write the URL through
1121
1105
  // the shared `URLQueryWriter`, so the ref + helper they close over must be in
@@ -1157,16 +1141,16 @@ var createNextArrayMapperPaginationPlugin = function () {
1157
1141
  var fileName = (0, utils_1.generateSafeFileName)(usage.resourceDefinition.dataSourceType, usage.resourceDefinition.tableName, usage.resourceDefinition.dataSourceId);
1158
1142
  // Update DataProvider based on category
1159
1143
  if (usage.category === 'paginated+search') {
1160
- updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations);
1144
+ updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized);
1161
1145
  }
1162
1146
  else if (usage.category === 'paginated-only') {
1163
- updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations);
1147
+ updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized);
1164
1148
  }
1165
1149
  else if (usage.category === 'search-only') {
1166
- updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations);
1150
+ updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations, localized);
1167
1151
  }
1168
1152
  else if (usage.category === 'plain') {
1169
- updateDataProviderForPlain(dp, fileName, usage);
1153
+ updateDataProviderForPlain(dp, fileName, usage, localized);
1170
1154
  }
1171
1155
  // Show the array mapper's loading state while a category/sort/search
1172
1156
  // change refetches, instead of leaving the previous rows on screen with
@@ -1226,7 +1210,7 @@ var createNextArrayMapperPaginationPlugin = function () {
1226
1210
  });
1227
1211
  searchInputs = findAllSearchInputsInJSX(blockStatement);
1228
1212
  searchEnabledUsages = registry.usages.filter(function (u) { return u.searchEnabled; });
1229
- // Match by order - search input 0 -> searchEnabledUsages[0], etc.
1213
+ usageBySearchInput = new Map();
1230
1214
  searchInputs.forEach(function (input, idx) {
1231
1215
  if (idx >= searchEnabledUsages.length) {
1232
1216
  return;
@@ -1234,6 +1218,15 @@ var createNextArrayMapperPaginationPlugin = function () {
1234
1218
  var usage = searchEnabledUsages[idx];
1235
1219
  var vars = getStateVarsForUsage(usage);
1236
1220
  wireSearchInput(input.node, vars);
1221
+ usageBySearchInput.set(input.node, usage);
1222
+ });
1223
+ // STEP 4.5: Wire the clear buttons, each to the input it shares a parent with.
1224
+ (0, search_clear_1.findAllSearchClearButtonsInJSX)(blockStatement).forEach(function (button) {
1225
+ var input = (0, search_clear_1.findSiblingSearchInput)(button, searchInputs.map(function (entry) { return entry.node; }));
1226
+ var usage = input ? usageBySearchInput.get(input) : undefined;
1227
+ if (usage) {
1228
+ (0, search_clear_1.wireSearchClearButton)(button, getStateVarsForUsage(usage));
1229
+ }
1237
1230
  });
1238
1231
  // STEP 5: Wire pagination widgets
1239
1232
  //
@@ -1255,7 +1248,7 @@ var createNextArrayMapperPaginationPlugin = function () {
1255
1248
  });
1256
1249
  // STEP 6: Update getStaticProps if this is a page
1257
1250
  if (isPage) {
1258
- updateGetStaticProps(chunks, registry, dependencies, (_b = uidl.outputOptions) === null || _b === void 0 ? void 0 : _b.folderPath);
1251
+ updateGetStaticProps(chunks, registry, dependencies, (_b = uidl.outputOptions) === null || _b === void 0 ? void 0 : _b.folderPath, localized);
1259
1252
  }
1260
1253
  return [2 /*return*/, structure];
1261
1254
  });
@@ -1305,6 +1298,20 @@ function insertSentinelAfterProvider(blockStatement, provider, sentinel) {
1305
1298
  if (node.alternate) {
1306
1299
  traverse(node.alternate);
1307
1300
  }
1301
+ // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
1302
+ // panel, a gated section: the provider sits on the RIGHT of a logical
1303
+ // expression (or in a ternary's `test`), which is not a child, a body or
1304
+ // an argument. Left unvisited, the products list under the first tab of
1305
+ // a shop was never paginated or seeded, and rendered nothing.
1306
+ if (node.left) {
1307
+ traverse(node.left);
1308
+ }
1309
+ if (node.right) {
1310
+ traverse(node.right);
1311
+ }
1312
+ if (node.test) {
1313
+ traverse(node.test);
1314
+ }
1308
1315
  if (node.expression) {
1309
1316
  traverse(node.expression);
1310
1317
  }
@@ -1342,6 +1349,20 @@ function findAllDataProvidersInJSX(blockStatement) {
1342
1349
  if (node.alternate) {
1343
1350
  traverse(node.alternate);
1344
1351
  }
1352
+ // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
1353
+ // panel, a gated section: the provider sits on the RIGHT of a logical
1354
+ // expression (or in a ternary's `test`), which is not a child, a body or
1355
+ // an argument. Left unvisited, the products list under the first tab of
1356
+ // a shop was never paginated or seeded, and rendered nothing.
1357
+ if (node.left) {
1358
+ traverse(node.left);
1359
+ }
1360
+ if (node.right) {
1361
+ traverse(node.right);
1362
+ }
1363
+ if (node.test) {
1364
+ traverse(node.test);
1365
+ }
1345
1366
  if (node.expression) {
1346
1367
  traverse(node.expression);
1347
1368
  }
@@ -1424,6 +1445,20 @@ function findAllSearchInputsInJSX(blockStatement) {
1424
1445
  if (node.alternate) {
1425
1446
  traverse(node.alternate);
1426
1447
  }
1448
+ // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
1449
+ // panel, a gated section: the provider sits on the RIGHT of a logical
1450
+ // expression (or in a ternary's `test`), which is not a child, a body or
1451
+ // an argument. Left unvisited, the products list under the first tab of
1452
+ // a shop was never paginated or seeded, and rendered nothing.
1453
+ if (node.left) {
1454
+ traverse(node.left);
1455
+ }
1456
+ if (node.right) {
1457
+ traverse(node.right);
1458
+ }
1459
+ if (node.test) {
1460
+ traverse(node.test);
1461
+ }
1427
1462
  if (node.expression) {
1428
1463
  traverse(node.expression);
1429
1464
  }
@@ -1479,6 +1514,20 @@ function findPaginationNodesWithOwners(blockStatement, usageByProviderNode) {
1479
1514
  if (node.alternate) {
1480
1515
  traverse(node.alternate);
1481
1516
  }
1517
+ // `{activeTabIndex === 0 && (<div>…<DataProvider/>…</div>)}` — a tab
1518
+ // panel, a gated section: the provider sits on the RIGHT of a logical
1519
+ // expression (or in a ternary's `test`), which is not a child, a body or
1520
+ // an argument. Left unvisited, the products list under the first tab of
1521
+ // a shop was never paginated or seeded, and rendered nothing.
1522
+ if (node.left) {
1523
+ traverse(node.left);
1524
+ }
1525
+ if (node.right) {
1526
+ traverse(node.right);
1527
+ }
1528
+ if (node.test) {
1529
+ traverse(node.test);
1530
+ }
1482
1531
  if (node.expression) {
1483
1532
  traverse(node.expression);
1484
1533
  }
@@ -1521,7 +1570,7 @@ dp, usage, paramsProps, memoDeps, cacheDeclarations) {
1521
1570
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.identifier(hoisted.paramsIdentifier))));
1522
1571
  return hoisted.cachedIdentifier;
1523
1572
  }
1524
- function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations) {
1573
+ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDeclarations, localized) {
1525
1574
  var attrs = dp.openingElement.attributes;
1526
1575
  // Remove existing params, key, initialData, fetchData, persistDataDuringLoading
1527
1576
  dp.openingElement.attributes = attrs.filter(function (attr) {
@@ -1550,6 +1599,9 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDe
1550
1599
  (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
1551
1600
  }
1552
1601
  pushUrlSearchParamMemoDeps(memoDeps, usage);
1602
+ if (localized) {
1603
+ (0, request_locale_1.appendClientLocaleParam)(paramsProps, memoDeps);
1604
+ }
1553
1605
  var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1554
1606
  // Add initialData. Skip the server-prefetched data entirely when a dynamic
1555
1607
  // state-bound sort is active — the prefetch ran without sort parameters, so
@@ -1601,7 +1653,7 @@ function updateDataProviderForPaginatedSearch(dp, usage, vars, fileName, cacheDe
1601
1653
  // Add persistDataDuringLoading
1602
1654
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
1603
1655
  }
1604
- function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations) {
1656
+ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDeclarations, localized) {
1605
1657
  var attrs = dp.openingElement.attributes;
1606
1658
  dp.openingElement.attributes = attrs.filter(function (attr) {
1607
1659
  var _a;
@@ -1625,6 +1677,9 @@ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDec
1625
1677
  (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
1626
1678
  }
1627
1679
  pushUrlSearchParamMemoDeps(memoDeps, usage);
1680
+ if (localized) {
1681
+ (0, request_locale_1.appendClientLocaleParam)(paramsProps, memoDeps);
1682
+ }
1628
1683
  // Add params
1629
1684
  var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1630
1685
  // Add initialData. See paginated+search updater for why we skip prefetch
@@ -1650,7 +1705,7 @@ function updateDataProviderForPaginationOnly(dp, usage, vars, fileName, cacheDec
1650
1705
  dp.openingElement.attributes.push(buildFetchDataForUsage(fileName, usage));
1651
1706
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
1652
1707
  }
1653
- function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations) {
1708
+ function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclarations, localized) {
1654
1709
  var _a;
1655
1710
  var attrs = dp.openingElement.attributes;
1656
1711
  dp.openingElement.attributes = attrs.filter(function (attr) {
@@ -1677,6 +1732,9 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclara
1677
1732
  (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, seenDeps, usage.dynamicSort.depStateIds);
1678
1733
  }
1679
1734
  pushUrlSearchParamMemoDeps(memoDeps, usage);
1735
+ if (localized) {
1736
+ (0, request_locale_1.appendClientLocaleParam)(paramsProps, memoDeps);
1737
+ }
1680
1738
  var cachedInitialData = pushParamsAttribute(dp, usage, paramsProps, memoDeps, cacheDeclarations);
1681
1739
  // Add initialData. See paginated+search updater for why we skip prefetch
1682
1740
  // reuse when a dynamic state-bound sort is active.
@@ -1692,7 +1750,7 @@ function updateDataProviderForSearchOnly(dp, usage, vars, fileName, cacheDeclara
1692
1750
  dp.openingElement.attributes.push(createFetchDataAttribute(fileName, ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined));
1693
1751
  dp.openingElement.attributes.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
1694
1752
  }
1695
- function updateDataProviderForPlain(dp, fileName, usage) {
1753
+ function updateDataProviderForPlain(dp, fileName, usage, localized) {
1696
1754
  var _a, _b, _c, _d;
1697
1755
  var attrs = dp.openingElement.attributes;
1698
1756
  var clientCache = ((_a = usage.cache) === null || _a === void 0 ? void 0 : _a.client) ? usage.cache : undefined;
@@ -1714,10 +1772,16 @@ function updateDataProviderForPlain(dp, fileName, usage) {
1714
1772
  if (!hasPersistData) {
1715
1773
  attrs.push(types.jsxAttribute(types.jsxIdentifier('persistDataDuringLoading'), types.jsxExpressionContainer(types.booleanLiteral(true))));
1716
1774
  }
1717
- // Find the params attribute
1775
+ // Find the params attribute. A plain list may have none at all — but the
1776
+ // fetch it was just given sends `params` regardless, and in a localized
1777
+ // project those have to carry the locale, so the attribute is created.
1718
1778
  var paramsAttrIndex = attrs.findIndex(function (attr) { return attr.type === 'JSXAttribute' && attr.name.name === 'params'; });
1719
1779
  if (paramsAttrIndex === -1) {
1720
- return;
1780
+ if (!localized) {
1781
+ return;
1782
+ }
1783
+ attrs.push(types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(types.objectExpression([]))));
1784
+ paramsAttrIndex = attrs.length - 1;
1721
1785
  }
1722
1786
  var paramsAttr = attrs[paramsAttrIndex];
1723
1787
  // Check if params is already wrapped in useMemo
@@ -1740,6 +1804,9 @@ function updateDataProviderForPlain(dp, fileName, usage) {
1740
1804
  (0, filter_utils_1.pushStateIdsAsDeps)(memoDeps, memoSeen, usage.filterStateIds);
1741
1805
  (0, filter_utils_1.pushPropIdsAsDeps)(memoDeps, memoSeen, usage.filterPropIds);
1742
1806
  pushUrlSearchParamMemoDeps(memoDeps, usage);
1807
+ if (localized && paramsExpression.type === 'ObjectExpression') {
1808
+ (0, request_locale_1.appendClientLocaleParam)(paramsExpression.properties, memoDeps);
1809
+ }
1743
1810
  // Wrap params in useMemo with filter state dependencies
1744
1811
  var memoizedParams = types.callExpression(types.identifier('useMemo'), [
1745
1812
  types.arrowFunctionExpression([], paramsExpression),
@@ -1771,11 +1838,18 @@ function stabilizeDataProviderWithoutRepeater(dp) {
1771
1838
  if (!paramsExpression) {
1772
1839
  return;
1773
1840
  }
1774
- // Wrap params in useMemo with empty dependencies array
1775
- // This ensures the object reference stays stable across re-renders
1841
+ // Wrap params in useMemo with an empty dependencies array so the object
1842
+ // reference stays stable across re-renders. The one thing that may vary is
1843
+ // the locale a client-fetched item carries (put there by the data-source
1844
+ // plugin): a locale switch must rebuild the params, or the item would keep
1845
+ // its previous language.
1846
+ var memoDeps = [];
1847
+ if (paramsExpression.type === 'ObjectExpression' && (0, request_locale_1.hasLocaleParam)(paramsExpression.properties)) {
1848
+ memoDeps.push((0, request_locale_1.buildClientLocaleExpression)());
1849
+ }
1776
1850
  var memoizedParams = types.callExpression(types.identifier('useMemo'), [
1777
1851
  types.arrowFunctionExpression([], paramsExpression),
1778
- types.arrayExpression([]),
1852
+ types.arrayExpression(memoDeps),
1779
1853
  ]);
1780
1854
  // Replace the params attribute
1781
1855
  attrs[paramsAttrIndex] = types.jsxAttribute(types.jsxIdentifier('params'), types.jsxExpressionContainer(memoizedParams));
@@ -2087,7 +2161,7 @@ function ensureAPIRouteExists(extractedResources, usage, dataSources, transformO
2087
2161
  };
2088
2162
  }
2089
2163
  }
2090
- function updateGetStaticProps(chunks, registry, dependencies, folderPath) {
2164
+ function updateGetStaticProps(chunks, registry, dependencies, folderPath, localized) {
2091
2165
  var _a;
2092
2166
  var getStaticPropsChunk = chunks.find(function (c) { return c.name === 'getStaticProps'; });
2093
2167
  if (!getStaticPropsChunk || getStaticPropsChunk.type !== teleport_types_1.ChunkType.AST) {
@@ -2182,6 +2256,10 @@ function updateGetStaticProps(chunks, registry, dependencies, folderPath) {
2182
2256
  })),
2183
2257
  ])));
2184
2258
  }
2259
+ // The prefetch is per locale — getStaticProps runs once per language.
2260
+ if (localized) {
2261
+ fetchParams.push((0, request_locale_1.buildServerLocaleParam)());
2262
+ }
2185
2263
  // Check if this fetch already exists
2186
2264
  var existingFetchIndex = arrayPattern.elements.findIndex(function (el) { return (el === null || el === void 0 ? void 0 : el.type) === 'Identifier' && el.name === vars.propsPrefix; });
2187
2265
  if (existingFetchIndex === -1) {