@gtkx/cli 1.4.0 → 1.6.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.
Files changed (206) hide show
  1. package/README.md +1 -1
  2. package/dist/builder.d.ts +1 -0
  3. package/dist/builder.d.ts.map +1 -1
  4. package/dist/builder.js +2 -2
  5. package/dist/builder.js.map +1 -1
  6. package/dist/cli.js +4 -0
  7. package/dist/cli.js.map +1 -1
  8. package/dist/codegen/freshness.d.ts.map +1 -1
  9. package/dist/codegen/freshness.js +2 -1
  10. package/dist/codegen/freshness.js.map +1 -1
  11. package/dist/codegen/reference.d.ts +17 -0
  12. package/dist/codegen/reference.d.ts.map +1 -0
  13. package/dist/codegen/reference.js +37 -0
  14. package/dist/codegen/reference.js.map +1 -0
  15. package/dist/codegen/report.d.ts.map +1 -1
  16. package/dist/codegen/report.js +17 -3
  17. package/dist/codegen/report.js.map +1 -1
  18. package/dist/codegen/run-codegen.d.ts +4 -0
  19. package/dist/codegen/run-codegen.d.ts.map +1 -1
  20. package/dist/codegen/run-codegen.js +93 -27
  21. package/dist/codegen/run-codegen.js.map +1 -1
  22. package/dist/commands/codegen.js +1 -1
  23. package/dist/commands/codegen.js.map +1 -1
  24. package/dist/commands/docs.d.ts.map +1 -1
  25. package/dist/commands/docs.js +10 -16
  26. package/dist/commands/docs.js.map +1 -1
  27. package/dist/commands/mcp.d.ts +13 -0
  28. package/dist/commands/mcp.d.ts.map +1 -0
  29. package/dist/commands/mcp.js +68 -0
  30. package/dist/commands/mcp.js.map +1 -0
  31. package/dist/deploy/freedesktop/localize.d.ts +6 -0
  32. package/dist/deploy/freedesktop/localize.d.ts.map +1 -0
  33. package/dist/deploy/freedesktop/localize.js +107 -0
  34. package/dist/deploy/freedesktop/localize.js.map +1 -0
  35. package/dist/deploy/install-hints.d.ts.map +1 -1
  36. package/dist/deploy/install-hints.js +5 -0
  37. package/dist/deploy/install-hints.js.map +1 -1
  38. package/dist/deploy/notices/gtkx.js +1 -1
  39. package/dist/deploy/notices/gtkx.js.map +1 -1
  40. package/dist/deploy/notices/libraries.d.ts.map +1 -1
  41. package/dist/deploy/notices/libraries.js +2 -1
  42. package/dist/deploy/notices/libraries.js.map +1 -1
  43. package/dist/deploy/payload/launcher.d.ts.map +1 -1
  44. package/dist/deploy/payload/launcher.js +2 -0
  45. package/dist/deploy/payload/launcher.js.map +1 -1
  46. package/dist/deploy/payload/stage.d.ts +1 -1
  47. package/dist/deploy/payload/stage.d.ts.map +1 -1
  48. package/dist/deploy/payload/stage.js +20 -11
  49. package/dist/deploy/payload/stage.js.map +1 -1
  50. package/dist/deploy/run-deploy.d.ts.map +1 -1
  51. package/dist/deploy/run-deploy.js +87 -15
  52. package/dist/deploy/run-deploy.js.map +1 -1
  53. package/dist/deploy/settings/libraries.d.ts.map +1 -1
  54. package/dist/deploy/settings/libraries.js +7 -9
  55. package/dist/deploy/settings/libraries.js.map +1 -1
  56. package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
  57. package/dist/deploy/targets/flatpak-source.js +21 -10
  58. package/dist/deploy/targets/flatpak-source.js.map +1 -1
  59. package/dist/deploy/tools.d.ts +6 -1
  60. package/dist/deploy/tools.d.ts.map +1 -1
  61. package/dist/deploy/tools.js +27 -4
  62. package/dist/deploy/tools.js.map +1 -1
  63. package/dist/dev/locale-dir.d.ts +3 -0
  64. package/dist/dev/locale-dir.d.ts.map +1 -0
  65. package/dist/dev/locale-dir.js +17 -0
  66. package/dist/dev/locale-dir.js.map +1 -0
  67. package/dist/dev/runner-main.d.ts.map +1 -1
  68. package/dist/dev/runner-main.js +4 -1
  69. package/dist/dev/runner-main.js.map +1 -1
  70. package/dist/dev/runner.d.ts.map +1 -1
  71. package/dist/dev/runner.js +6 -0
  72. package/dist/dev/runner.js.map +1 -1
  73. package/dist/dev/vite-dev-server.d.ts +1 -0
  74. package/dist/dev/vite-dev-server.d.ts.map +1 -1
  75. package/dist/dev/vite-dev-server.js.map +1 -1
  76. package/dist/i18n/binding-extractor.d.ts +30 -0
  77. package/dist/i18n/binding-extractor.d.ts.map +1 -0
  78. package/dist/i18n/binding-extractor.js +1324 -0
  79. package/dist/i18n/binding-extractor.js.map +1 -0
  80. package/dist/i18n/catalog-template.d.ts +3 -0
  81. package/dist/i18n/catalog-template.d.ts.map +1 -0
  82. package/dist/i18n/catalog-template.js +18 -0
  83. package/dist/i18n/catalog-template.js.map +1 -0
  84. package/dist/i18n/catalogs.d.ts +21 -0
  85. package/dist/i18n/catalogs.d.ts.map +1 -0
  86. package/dist/i18n/catalogs.js +139 -0
  87. package/dist/i18n/catalogs.js.map +1 -0
  88. package/dist/i18n/extractor-plugin.d.ts +8 -0
  89. package/dist/i18n/extractor-plugin.d.ts.map +1 -0
  90. package/dist/i18n/extractor-plugin.js +247 -0
  91. package/dist/i18n/extractor-plugin.js.map +1 -0
  92. package/dist/i18n/metadata-templates.d.ts +11 -0
  93. package/dist/i18n/metadata-templates.d.ts.map +1 -0
  94. package/dist/i18n/metadata-templates.js +11 -0
  95. package/dist/i18n/metadata-templates.js.map +1 -0
  96. package/dist/i18n/source-messages.d.ts +23 -0
  97. package/dist/i18n/source-messages.d.ts.map +1 -0
  98. package/dist/i18n/source-messages.js +403 -0
  99. package/dist/i18n/source-messages.js.map +1 -0
  100. package/dist/i18n/types.d.ts +10 -0
  101. package/dist/i18n/types.d.ts.map +1 -0
  102. package/dist/i18n/types.js +108 -0
  103. package/dist/i18n/types.js.map +1 -0
  104. package/dist/internal/agent-rules.d.ts +7 -0
  105. package/dist/internal/agent-rules.d.ts.map +1 -0
  106. package/dist/internal/agent-rules.js +106 -0
  107. package/dist/internal/agent-rules.js.map +1 -0
  108. package/dist/internal/docs-elements.d.ts +8 -0
  109. package/dist/internal/docs-elements.d.ts.map +1 -0
  110. package/dist/internal/docs-elements.js +12 -0
  111. package/dist/internal/docs-elements.js.map +1 -0
  112. package/dist/internal/source-imports.d.ts +5 -1
  113. package/dist/internal/source-imports.d.ts.map +1 -1
  114. package/dist/internal/source-imports.js +25 -47
  115. package/dist/internal/source-imports.js.map +1 -1
  116. package/dist/mcp/client-config.d.ts +22 -0
  117. package/dist/mcp/client-config.d.ts.map +1 -0
  118. package/dist/mcp/client-config.js +94 -0
  119. package/dist/mcp/client-config.js.map +1 -0
  120. package/dist/mcp/client.d.ts +0 -1
  121. package/dist/mcp/client.d.ts.map +1 -1
  122. package/dist/mcp/client.js +11 -39
  123. package/dist/mcp/client.js.map +1 -1
  124. package/dist/mcp/handlers.d.ts +2 -1
  125. package/dist/mcp/handlers.d.ts.map +1 -1
  126. package/dist/mcp/handlers.js.map +1 -1
  127. package/dist/settings/render.d.ts +4 -1
  128. package/dist/settings/render.d.ts.map +1 -1
  129. package/dist/settings/render.js +11 -2
  130. package/dist/settings/render.js.map +1 -1
  131. package/dist/settings/schema.d.ts.map +1 -1
  132. package/dist/settings/schema.js +3 -1
  133. package/dist/settings/schema.js.map +1 -1
  134. package/dist/vite-plugins/animated.d.ts +5 -0
  135. package/dist/vite-plugins/animated.d.ts.map +1 -0
  136. package/dist/vite-plugins/animated.js +325 -0
  137. package/dist/vite-plugins/animated.js.map +1 -0
  138. package/dist/vite-plugins/asset-imports.d.ts.map +1 -1
  139. package/dist/vite-plugins/asset-imports.js +4 -14
  140. package/dist/vite-plugins/asset-imports.js.map +1 -1
  141. package/dist/vite-plugins/i18n.d.ts +5 -0
  142. package/dist/vite-plugins/i18n.d.ts.map +1 -0
  143. package/dist/vite-plugins/i18n.js +88 -0
  144. package/dist/vite-plugins/i18n.js.map +1 -0
  145. package/dist/vite-plugins/index.d.ts +1 -1
  146. package/dist/vite-plugins/index.d.ts.map +1 -1
  147. package/dist/vite-plugins/index.js +5 -1
  148. package/dist/vite-plugins/index.js.map +1 -1
  149. package/dist/vite-plugins/resources.d.ts.map +1 -1
  150. package/dist/vite-plugins/resources.js +12 -57
  151. package/dist/vite-plugins/resources.js.map +1 -1
  152. package/dist/vite-plugins/self-contained.d.ts.map +1 -1
  153. package/dist/vite-plugins/self-contained.js +128 -135
  154. package/dist/vite-plugins/self-contained.js.map +1 -1
  155. package/dist/vite-plugins/settings-worker-env.js +2 -2
  156. package/dist/vite-plugins/settings-worker-env.js.map +1 -1
  157. package/dist/vite-plugins/settings.d.ts.map +1 -1
  158. package/dist/vite-plugins/settings.js +2 -1
  159. package/dist/vite-plugins/settings.js.map +1 -1
  160. package/package.json +11 -9
  161. package/src/builder.ts +3 -2
  162. package/src/cli.ts +5 -0
  163. package/src/codegen/freshness.ts +2 -1
  164. package/src/codegen/reference.ts +59 -0
  165. package/src/codegen/report.ts +18 -3
  166. package/src/codegen/run-codegen.ts +134 -31
  167. package/src/commands/codegen.ts +1 -1
  168. package/src/commands/docs.ts +10 -28
  169. package/src/commands/mcp.ts +81 -0
  170. package/src/deploy/freedesktop/localize.ts +144 -0
  171. package/src/deploy/install-hints.ts +5 -0
  172. package/src/deploy/notices/gtkx.ts +1 -1
  173. package/src/deploy/notices/libraries.ts +2 -1
  174. package/src/deploy/payload/launcher.ts +2 -0
  175. package/src/deploy/payload/stage.ts +35 -10
  176. package/src/deploy/run-deploy.ts +128 -13
  177. package/src/deploy/settings/libraries.ts +7 -10
  178. package/src/deploy/targets/flatpak-source.ts +33 -10
  179. package/src/deploy/tools.ts +36 -3
  180. package/src/dev/locale-dir.ts +22 -0
  181. package/src/dev/runner-main.ts +4 -1
  182. package/src/dev/runner.ts +8 -0
  183. package/src/dev/vite-dev-server.ts +1 -0
  184. package/src/i18n/binding-extractor.ts +1969 -0
  185. package/src/i18n/catalog-template.ts +24 -0
  186. package/src/i18n/catalogs.ts +216 -0
  187. package/src/i18n/extractor-plugin.ts +413 -0
  188. package/src/i18n/metadata-templates.ts +29 -0
  189. package/src/i18n/source-messages.ts +608 -0
  190. package/src/i18n/types.ts +154 -0
  191. package/src/internal/agent-rules.ts +135 -0
  192. package/src/internal/docs-elements.ts +21 -0
  193. package/src/internal/source-imports.ts +31 -60
  194. package/src/mcp/client-config.ts +130 -0
  195. package/src/mcp/client.ts +17 -44
  196. package/src/mcp/handlers.ts +7 -6
  197. package/src/settings/render.ts +16 -2
  198. package/src/settings/schema.ts +3 -1
  199. package/src/vite-plugins/animated.ts +486 -0
  200. package/src/vite-plugins/asset-imports.ts +5 -17
  201. package/src/vite-plugins/i18n.ts +146 -0
  202. package/src/vite-plugins/index.ts +5 -0
  203. package/src/vite-plugins/resources.ts +12 -74
  204. package/src/vite-plugins/self-contained.ts +150 -169
  205. package/src/vite-plugins/settings-worker-env.ts +2 -2
  206. package/src/vite-plugins/settings.ts +2 -1
@@ -0,0 +1,1324 @@
1
+ import { types as babel, parseAsync, traverse } from "@babel/core";
2
+ import { extname } from "node:path";
3
+ const API_NAMES = [
4
+ "IcuTrans",
5
+ "IcuTransWithoutContext",
6
+ "Trans",
7
+ "TransWithoutContext",
8
+ "Translation",
9
+ "getFixedT",
10
+ "getI18n",
11
+ "init",
12
+ "t",
13
+ "useTranslation",
14
+ "withTranslation",
15
+ ];
16
+ const API_NAME_SET = new Set(API_NAMES);
17
+ const GTKX_MODULE = "@gtkx/i18n";
18
+ const I18NEXT_MODULE = "i18next";
19
+ const REACT_I18NEXT_MODULE = "react-i18next";
20
+ const SUPPORTED_MODULES = new Set([GTKX_MODULE, I18NEXT_MODULE, REACT_I18NEXT_MODULE]);
21
+ const I18NEXT_APIS = new Set(["getFixedT", "init", "t"]);
22
+ const CONTEXT_SEPARATOR = "\u{4}";
23
+ const EMPTY_INFO = {};
24
+ const GLOBAL_T = { fixed: false, info: EMPTY_INFO, kind: "t" };
25
+ const oneChild = (path, key) => {
26
+ const value = path.get(key);
27
+ return Array.isArray(value) || value.node === null ? undefined : value;
28
+ };
29
+ const childPaths = (path, key) => {
30
+ const value = path.get(key);
31
+ return Array.isArray(value)
32
+ ? value.filter((candidate) => candidate.node !== null)
33
+ : [];
34
+ };
35
+ const isTransparentExpression = (path) => [
36
+ path.isParenthesizedExpression(),
37
+ path.isTSAsExpression(),
38
+ path.isTSInstantiationExpression(),
39
+ path.isTSNonNullExpression(),
40
+ path.isTSSatisfiesExpression(),
41
+ path.isTSTypeAssertion(),
42
+ ].includes(true);
43
+ const unwrapPath = (path) => {
44
+ let current = path;
45
+ while (isTransparentExpression(current)) {
46
+ const expression = oneChild(current, "expression");
47
+ if (expression === undefined) {
48
+ return current;
49
+ }
50
+ current = expression;
51
+ }
52
+ return current;
53
+ };
54
+ const staticPropertyName = (path) => {
55
+ if (path.isIdentifier()) {
56
+ return path.node.name;
57
+ }
58
+ if (path.isStringLiteral()) {
59
+ return path.node.value;
60
+ }
61
+ return path.isNumericLiteral() ? String(path.node.value) : undefined;
62
+ };
63
+ const memberProperty = (path) => {
64
+ const member = unwrapPath(path);
65
+ if (!member.isMemberExpression() && !member.isOptionalMemberExpression()) {
66
+ return;
67
+ }
68
+ const property = oneChild(member, "property");
69
+ if (property === undefined) {
70
+ return;
71
+ }
72
+ return !member.node.computed && property.isIdentifier()
73
+ ? property.node.name
74
+ : staticPropertyName(property);
75
+ };
76
+ const constantDeclaratorBinding = (path, resolving) => {
77
+ if (!path.isIdentifier()) {
78
+ return;
79
+ }
80
+ const binding = path.scope.getBinding(path.node.name);
81
+ if (!binding?.constant) {
82
+ return;
83
+ }
84
+ return resolving.has(binding) || !binding.path.isVariableDeclarator()
85
+ ? undefined
86
+ : binding;
87
+ };
88
+ const literalIdentifierString = (path, resolving) => {
89
+ const binding = constantDeclaratorBinding(path, resolving);
90
+ if (binding === undefined) {
91
+ return;
92
+ }
93
+ const init = oneChild(binding.path, "init");
94
+ if (init === undefined) {
95
+ return;
96
+ }
97
+ resolving.add(binding);
98
+ const resolved = literalString(init, resolving);
99
+ resolving.delete(binding);
100
+ return resolved;
101
+ };
102
+ const literalMemberString = (path, resolving) => {
103
+ const object = oneChild(path, "object");
104
+ const property = memberProperty(path);
105
+ if (property === undefined || object?.isIdentifier() !== true) {
106
+ return;
107
+ }
108
+ const binding = constantDeclaratorBinding(object, resolving);
109
+ if (binding === undefined) {
110
+ return;
111
+ }
112
+ const member = objectProperty(oneChild(binding.path, "init"), property);
113
+ if (member === undefined) {
114
+ return;
115
+ }
116
+ resolving.add(binding);
117
+ const resolved = literalString(member, resolving);
118
+ resolving.delete(binding);
119
+ return resolved;
120
+ };
121
+ const directLiteralString = (value) => {
122
+ if (value.isStringLiteral()) {
123
+ return value.node.value;
124
+ }
125
+ if (!value.isTemplateLiteral() || value.node.expressions.length > 0) {
126
+ return;
127
+ }
128
+ const quasi = value.node.quasis[0];
129
+ return quasi?.value.cooked ?? quasi?.value.raw;
130
+ };
131
+ const literalString = (path, resolving = new Set()) => {
132
+ if (path === undefined) {
133
+ return;
134
+ }
135
+ const value = unwrapPath(path);
136
+ const direct = directLiteralString(value);
137
+ if (direct !== undefined) {
138
+ return direct;
139
+ }
140
+ return value.isIdentifier()
141
+ ? literalIdentifierString(value, resolving)
142
+ : literalMemberString(value, resolving);
143
+ };
144
+ const isStaticObjectProperty = (path, resolving) => {
145
+ if (!path.isSpreadElement()) {
146
+ return true;
147
+ }
148
+ return isStaticObject(oneChild(path, "argument"), resolving);
149
+ };
150
+ const isStaticObject = (path, resolving = new Set()) => {
151
+ if (path === undefined) {
152
+ return false;
153
+ }
154
+ const value = unwrapPath(path);
155
+ const binding = constantDeclaratorBinding(value, resolving);
156
+ if (binding !== undefined) {
157
+ resolving.add(binding);
158
+ const isResult = isStaticObject(oneChild(binding.path, "init"), resolving);
159
+ resolving.delete(binding);
160
+ return isResult;
161
+ }
162
+ return value.isObjectExpression() &&
163
+ childPaths(value, "properties").every((property) => isStaticObjectProperty(property, resolving));
164
+ };
165
+ const propertyKeyName = (property) => {
166
+ const key = oneChild(property, "key");
167
+ if (key === undefined) {
168
+ return;
169
+ }
170
+ const propertyName = property.node.computed
171
+ ? literalString(key)
172
+ : staticPropertyName(key);
173
+ if (propertyName === undefined && property.node.computed) {
174
+ throw new Error("Translation options cannot contain dynamic computed properties");
175
+ }
176
+ return propertyName;
177
+ };
178
+ const spreadPropertyValue = (property, name, resolving) => {
179
+ const argument = oneChild(property, "argument");
180
+ const spread = objectProperty(argument, name, resolving);
181
+ if (spread !== undefined) {
182
+ return spread;
183
+ }
184
+ if (!isStaticObject(argument)) {
185
+ throw new Error("Translation options cannot contain dynamic object spreads");
186
+ }
187
+ return undefined;
188
+ };
189
+ const directPropertyValue = (property, name) => {
190
+ if (!property.isObjectMethod() && !property.isObjectProperty()) {
191
+ return;
192
+ }
193
+ if (propertyKeyName(property) !== name) {
194
+ return;
195
+ }
196
+ if (property.isObjectMethod()) {
197
+ return property;
198
+ }
199
+ const key = oneChild(property, "key");
200
+ return property.node.shorthand ? key : oneChild(property, "value");
201
+ };
202
+ const objectPropertyEntry = (property, name, resolving) => property.isSpreadElement()
203
+ ? spreadPropertyValue(property, name, resolving)
204
+ : directPropertyValue(property, name);
205
+ const objectExpressionProperty = (value, name, resolving) => {
206
+ for (const property of childPaths(value, "properties").toReversed()) {
207
+ const result = objectPropertyEntry(property, name, resolving);
208
+ if (result !== undefined) {
209
+ return result;
210
+ }
211
+ }
212
+ return undefined;
213
+ };
214
+ const objectProperty = (path, name, resolving = new Set()) => {
215
+ if (path === undefined) {
216
+ return;
217
+ }
218
+ const value = unwrapPath(path);
219
+ const binding = constantDeclaratorBinding(value, resolving);
220
+ if (binding !== undefined) {
221
+ resolving.add(binding);
222
+ const resolved = objectProperty(oneChild(binding.path, "init"), name, resolving);
223
+ resolving.delete(binding);
224
+ return resolved;
225
+ }
226
+ if (!value.isObjectExpression()) {
227
+ return;
228
+ }
229
+ return objectExpressionProperty(value, name, resolving);
230
+ };
231
+ const resolvedNamespaceIdentifier = (value, resolving) => {
232
+ const binding = constantDeclaratorBinding(value, resolving);
233
+ if (binding === undefined) {
234
+ return;
235
+ }
236
+ resolving.add(binding);
237
+ const resolved = namespaceValues(oneChild(binding.path, "init"), resolving);
238
+ resolving.delete(binding);
239
+ return resolved;
240
+ };
241
+ const namespaceValues = (path, resolving = new Set()) => {
242
+ const direct = literalString(path);
243
+ if (direct !== undefined) {
244
+ return [direct];
245
+ }
246
+ if (path === undefined) {
247
+ return [];
248
+ }
249
+ const value = unwrapPath(path);
250
+ const resolved = resolvedNamespaceIdentifier(value, resolving);
251
+ if (resolved !== undefined) {
252
+ return resolved;
253
+ }
254
+ if (!value.isArrayExpression()) {
255
+ return [];
256
+ }
257
+ const values = childPaths(value, "elements").map((element) => literalString(element, resolving));
258
+ return values.every((candidate) => candidate !== undefined) ? values : [];
259
+ };
260
+ const normalizedInfo = (namespaces, keyPrefix) => {
261
+ const info = {};
262
+ const defaultNs = namespaces[0];
263
+ if (defaultNs !== undefined) {
264
+ info.defaultNs = defaultNs;
265
+ if (namespaces.length > 1) {
266
+ info.namespaces = namespaces;
267
+ }
268
+ }
269
+ if (keyPrefix !== undefined && keyPrefix.length > 0) {
270
+ info.keyPrefix = keyPrefix;
271
+ }
272
+ return info;
273
+ };
274
+ const isAbsentArgument = (path) => {
275
+ if (path.isNullLiteral()) {
276
+ return true;
277
+ }
278
+ return path.isIdentifier() &&
279
+ path.node.name === "undefined" &&
280
+ path.scope.getBinding("undefined") === undefined;
281
+ };
282
+ const staticNamespaces = (path, file) => {
283
+ if (path === undefined || isAbsentArgument(path)) {
284
+ return [];
285
+ }
286
+ const namespaces = namespaceValues(path);
287
+ if (namespaces.length === 0) {
288
+ throw new Error(`Translation namespaces must be static strings in ${file}`);
289
+ }
290
+ return namespaces;
291
+ };
292
+ const staticKeyPrefix = (path, file) => {
293
+ if (path === undefined || isAbsentArgument(path)) {
294
+ return;
295
+ }
296
+ const keyPrefix = literalString(path);
297
+ if (keyPrefix === undefined) {
298
+ throw new Error(`Translation key prefixes must be static strings in ${file}`);
299
+ }
300
+ return keyPrefix;
301
+ };
302
+ const callArguments = (path) => childPaths(path, "arguments");
303
+ const scopeFromCall = (input) => {
304
+ const args = callArguments(input.path);
305
+ const options = input.optionsIndex === undefined ? undefined : args[input.optionsIndex];
306
+ const optionPrefix = staticKeyPrefix(objectProperty(options, "keyPrefix"), input.file);
307
+ const positional = input.prefixIndex === undefined ? undefined : args[input.prefixIndex];
308
+ const positionalPrefix = staticKeyPrefix(positional, input.file);
309
+ return normalizedInfo(staticNamespaces(args[input.namespaceIndex], input.file), optionPrefix ?? positionalPrefix);
310
+ };
311
+ const isApiName = (name) => API_NAME_SET.has(name);
312
+ const isApiAvailable = (moduleName, api) => {
313
+ if (moduleName === GTKX_MODULE) {
314
+ return true;
315
+ }
316
+ if (moduleName === I18NEXT_MODULE) {
317
+ return I18NEXT_APIS.has(api);
318
+ }
319
+ return moduleName === REACT_I18NEXT_MODULE && !I18NEXT_APIS.has(api);
320
+ };
321
+ const apiSemantic = (api) => api === "t" ? GLOBAL_T : { api, kind: "api" };
322
+ const spanKey = (node) => typeof node.start === "number" && typeof node.end === "number"
323
+ ? `${String(node.start)}:${String(node.end)}`
324
+ : undefined;
325
+ const pathLocation = (path, file) => {
326
+ const start = path.node.loc?.start;
327
+ return start === undefined
328
+ ? { file }
329
+ : { column: start.column, file, line: start.line };
330
+ };
331
+ const patternLocalName = (path) => {
332
+ const value = unwrapPath(path);
333
+ if (value.isIdentifier()) {
334
+ return value.node.name;
335
+ }
336
+ const left = value.isAssignmentPattern() ? oneChild(value, "left") : undefined;
337
+ return left?.isIdentifier() === true ? left.node.name : undefined;
338
+ };
339
+ const scopeWithPrefix = (info, keyPrefix) => {
340
+ const scoped = { ...info };
341
+ if (keyPrefix.length === 0) {
342
+ delete scoped.keyPrefix;
343
+ }
344
+ else {
345
+ scoped.keyPrefix = keyPrefix;
346
+ }
347
+ return scoped;
348
+ };
349
+ const isClassPath = (path) => path.isClass();
350
+ const isCallPath = (path) => path.isCallExpression() || path.isOptionalCallExpression();
351
+ const isMemberPath = (path) => path.isMemberExpression() || path.isOptionalMemberExpression();
352
+ const isStaticStringArray = (path) => path !== undefined && literalString(path) === undefined && namespaceValues(path).length > 0;
353
+ const analyzeProgram = (program, file) => new BindingAnalyzer(file).analyze(program);
354
+ const parserPlugins = (path) => {
355
+ const extension = extname(path).toLowerCase();
356
+ const plugins = ["decorators-legacy"];
357
+ if ([".cts", ".mts", ".ts", ".tsx"].includes(extension)) {
358
+ plugins.push("typescript");
359
+ }
360
+ if ([".cjs", ".js", ".jsx", ".mjs", ".tsx"].includes(extension)) {
361
+ plugins.push("jsx");
362
+ }
363
+ return plugins;
364
+ };
365
+ const extractBindings = async (code, path) => {
366
+ const ast = await parseAsync(code, {
367
+ babelrc: false,
368
+ configFile: false,
369
+ filename: path,
370
+ parserOpts: {
371
+ plugins: parserPlugins(path),
372
+ sourceType: "unambiguous",
373
+ },
374
+ });
375
+ if (ast === null) {
376
+ throw new Error(`Unable to parse ${path}`);
377
+ }
378
+ let extraction;
379
+ traverse(ast, {
380
+ Program(programPath) {
381
+ extraction = analyzeProgram(programPath, path);
382
+ programPath.stop();
383
+ },
384
+ });
385
+ if (extraction === undefined) {
386
+ throw new Error(`Unable to analyze ${path}`);
387
+ }
388
+ return extraction;
389
+ };
390
+ class BindingAnalyzer {
391
+ bindingCache = new Map();
392
+ blockedGetFixedT = new Set();
393
+ calls = new Map();
394
+ errors = new Set();
395
+ file;
396
+ hocClasses = new Map();
397
+ messages = [];
398
+ resolving = new Set();
399
+ specialBindings = new Map();
400
+ trans = new Map();
401
+ constructor(file) {
402
+ this.file = file;
403
+ }
404
+ runSemanticPass(program) {
405
+ program.traverse({
406
+ CallExpression: this.registerCallSemantics.bind(this),
407
+ JSXElement: this.registerJsxElement.bind(this),
408
+ OptionalCallExpression: this.registerCallSemantics.bind(this),
409
+ VariableDeclarator: this.registerBlockedGetFixedT.bind(this),
410
+ });
411
+ }
412
+ runTranslationPass(program) {
413
+ program.traverse({
414
+ CallExpression: this.registerTranslationCall.bind(this),
415
+ OptionalCallExpression: this.registerTranslationCall.bind(this),
416
+ TaggedTemplateExpression: this.registerTaggedTranslation.bind(this),
417
+ });
418
+ }
419
+ importSemantic(binding) {
420
+ const path = binding.path;
421
+ const declaration = path.parentPath;
422
+ if (!declaration.isImportDeclaration()) {
423
+ return;
424
+ }
425
+ if (this.isTypeImport(path, declaration)) {
426
+ return;
427
+ }
428
+ const moduleName = declaration.node.source.value;
429
+ if (path.isImportNamespaceSpecifier()) {
430
+ return SUPPORTED_MODULES.has(moduleName) ? { kind: "namespace", module: moduleName } : undefined;
431
+ }
432
+ return this.importSpecifierSemantic(path, moduleName);
433
+ }
434
+ isTypeImport(path, declaration) {
435
+ return declaration.node.importKind === "type" ||
436
+ (path.isImportSpecifier() && path.node.importKind === "type");
437
+ }
438
+ importSpecifierSemantic(path, moduleName) {
439
+ if (path.isImportDefaultSpecifier()) {
440
+ return moduleName === I18NEXT_MODULE
441
+ ? { kind: "namespace", module: moduleName }
442
+ : undefined;
443
+ }
444
+ return path.isImportSpecifier()
445
+ ? this.namedImportSemantic(path, moduleName)
446
+ : undefined;
447
+ }
448
+ namedImportSemantic(path, moduleName) {
449
+ const imported = path.node.imported;
450
+ const name = babel.isIdentifier(imported) ? imported.name : imported.value;
451
+ return isApiName(name) && isApiAvailable(moduleName, name)
452
+ ? apiSemantic(name)
453
+ : undefined;
454
+ }
455
+ semanticMember(object, property) {
456
+ if (object.kind === "namespace") {
457
+ return this.namespaceMember(object.module, property);
458
+ }
459
+ return this.localSemanticMember(object, property);
460
+ }
461
+ namespaceMember(moduleName, property) {
462
+ return isApiName(property) && isApiAvailable(moduleName, property)
463
+ ? apiSemantic(property)
464
+ : undefined;
465
+ }
466
+ localSemanticMember(object, property) {
467
+ if (object.kind === "translationContext") {
468
+ return property === "i18n"
469
+ ? { instance: true, kind: "translationObject", t: GLOBAL_T }
470
+ : undefined;
471
+ }
472
+ return object.kind === "translationObject"
473
+ ? this.translationObjectMember(object, property)
474
+ : undefined;
475
+ }
476
+ translationObjectMember(object, property) {
477
+ if (["0", "t"].includes(property)) {
478
+ return object.t;
479
+ }
480
+ if (!object.instance && ["1", "i18n"].includes(property)) {
481
+ return { instance: true, kind: "translationObject", t: GLOBAL_T };
482
+ }
483
+ return object.instance && ["getFixedT", "init"].includes(property)
484
+ ? { api: property, kind: "api" }
485
+ : undefined;
486
+ }
487
+ resolvePath(input) {
488
+ const path = unwrapPath(input);
489
+ if (path.isIdentifier()) {
490
+ return this.resolveIdentifierPath(path);
491
+ }
492
+ if (isMemberPath(path)) {
493
+ return this.resolveMemberPath(path);
494
+ }
495
+ if (isCallPath(path)) {
496
+ return this.resolveCallPath(path);
497
+ }
498
+ return path.isAwaitExpression()
499
+ ? this.resolveAwaitPath(path)
500
+ : undefined;
501
+ }
502
+ resolveIdentifierPath(path) {
503
+ const binding = path.scope.getBinding(path.node.name);
504
+ return binding === undefined ? undefined : this.resolveBinding(binding);
505
+ }
506
+ resolveMemberPath(path) {
507
+ const objectPath = oneChild(path, "object");
508
+ const property = memberProperty(path);
509
+ if (objectPath === undefined || property === undefined) {
510
+ return;
511
+ }
512
+ const object = this.resolvePath(objectPath);
513
+ return object === undefined ? undefined : this.semanticMember(object, property);
514
+ }
515
+ resolveCallPath(path) {
516
+ const callee = oneChild(path, "callee");
517
+ if (callee === undefined) {
518
+ return;
519
+ }
520
+ const required = this.requireSemantic(path, callee);
521
+ if (required !== undefined) {
522
+ return required;
523
+ }
524
+ const called = this.resolvePath(callee);
525
+ return called?.kind === "api" ? this.apiCallSemantic(called.api, path) : undefined;
526
+ }
527
+ requireSemantic(path, callee) {
528
+ if (!callee.isIdentifier() || callee.node.name !== "require") {
529
+ return;
530
+ }
531
+ if (callee.scope.getBinding(callee.node.name) !== undefined) {
532
+ return;
533
+ }
534
+ const moduleName = literalString(callArguments(path)[0]);
535
+ return moduleName !== undefined && SUPPORTED_MODULES.has(moduleName)
536
+ ? { kind: "namespace", module: moduleName }
537
+ : undefined;
538
+ }
539
+ apiCallSemantic(api, path) {
540
+ switch (api) {
541
+ case "useTranslation": {
542
+ return this.useTranslationSemantic(path);
543
+ }
544
+ case "getI18n": {
545
+ return { instance: true, kind: "translationObject", t: GLOBAL_T };
546
+ }
547
+ case "getFixedT": {
548
+ return this.fixedTranslationSemantic(path);
549
+ }
550
+ case "withTranslation": {
551
+ return this.translationEnhancerSemantic(path);
552
+ }
553
+ case "init": {
554
+ return { kind: "initPromise" };
555
+ }
556
+ case "IcuTrans":
557
+ case "IcuTransWithoutContext":
558
+ case "Trans":
559
+ case "TransWithoutContext":
560
+ case "Translation":
561
+ case "t": {
562
+ return;
563
+ }
564
+ }
565
+ }
566
+ useTranslationSemantic(path) {
567
+ const info = scopeFromCall({ file: this.file, namespaceIndex: 0, optionsIndex: 1, path });
568
+ return {
569
+ instance: false,
570
+ kind: "translationObject",
571
+ t: { fixed: true, info, kind: "t" },
572
+ };
573
+ }
574
+ fixedTranslationSemantic(path) {
575
+ const info = scopeFromCall({
576
+ file: this.file,
577
+ namespaceIndex: 1,
578
+ path,
579
+ prefixIndex: 2,
580
+ });
581
+ return { fixed: true, info, kind: "t" };
582
+ }
583
+ translationEnhancerSemantic(path) {
584
+ const info = scopeFromCall({ file: this.file, namespaceIndex: 0, optionsIndex: 1, path });
585
+ return { info, kind: "enhancer" };
586
+ }
587
+ resolveAwaitPath(path) {
588
+ const argument = oneChild(path, "argument");
589
+ const awaited = argument === undefined ? undefined : this.resolvePath(argument);
590
+ return awaited?.kind === "initPromise" ? GLOBAL_T : undefined;
591
+ }
592
+ resolveBinding(binding) {
593
+ const special = this.specialBindings.get(binding);
594
+ if (special !== undefined) {
595
+ return special;
596
+ }
597
+ return binding.constant ? this.resolveConstantBinding(binding) : undefined;
598
+ }
599
+ resolveConstantBinding(binding) {
600
+ const cached = this.bindingCache.get(binding);
601
+ if (cached !== undefined) {
602
+ return cached ?? undefined;
603
+ }
604
+ if (this.resolving.has(binding)) {
605
+ return;
606
+ }
607
+ this.resolving.add(binding);
608
+ const resolved = this.importSemantic(binding) ?? this.declaratorSemantic(binding);
609
+ this.resolving.delete(binding);
610
+ this.bindingCache.set(binding, resolved ?? null);
611
+ return resolved;
612
+ }
613
+ declaratorSemantic(binding) {
614
+ const path = binding.path;
615
+ if (!path.isVariableDeclarator()) {
616
+ return;
617
+ }
618
+ const id = oneChild(path, "id");
619
+ const init = oneChild(path, "init");
620
+ if (id === undefined || init === undefined) {
621
+ return;
622
+ }
623
+ if (id.isIdentifier() && id.node.name === binding.identifier.name) {
624
+ return this.resolvePath(init);
625
+ }
626
+ return this.patternDeclaratorSemantic(id, init, binding.identifier.name);
627
+ }
628
+ patternDeclaratorSemantic(id, init, name) {
629
+ if (id.isObjectPattern()) {
630
+ return this.objectPatternSemantic(id, init, name);
631
+ }
632
+ return id.isArrayPattern()
633
+ ? this.arrayPatternSemantic(id, init, name)
634
+ : undefined;
635
+ }
636
+ objectPatternSemantic(id, init, name) {
637
+ const object = this.resolvePath(init);
638
+ if (object === undefined) {
639
+ return;
640
+ }
641
+ for (const property of childPaths(id, "properties")) {
642
+ const resolved = this.patternPropertySemantic(property, object, name);
643
+ if (resolved !== undefined) {
644
+ return resolved;
645
+ }
646
+ }
647
+ return undefined;
648
+ }
649
+ patternPropertySemantic(property, object, name) {
650
+ if (!property.isObjectProperty()) {
651
+ return;
652
+ }
653
+ const key = oneChild(property, "key");
654
+ const value = oneChild(property, "value");
655
+ if (key === undefined || value === undefined || patternLocalName(value) !== name) {
656
+ return;
657
+ }
658
+ const propertyName = staticPropertyName(key);
659
+ return propertyName === undefined ? undefined : this.semanticMember(object, propertyName);
660
+ }
661
+ arrayPatternSemantic(id, init, name) {
662
+ const object = this.resolvePath(init);
663
+ if (object?.kind !== "translationObject") {
664
+ return;
665
+ }
666
+ const elements = childPaths(id, "elements");
667
+ if (elements[0] !== undefined && patternLocalName(elements[0]) === name) {
668
+ return object.t;
669
+ }
670
+ return !object.instance && elements[1] !== undefined && patternLocalName(elements[1]) === name
671
+ ? { instance: true, kind: "translationObject", t: GLOBAL_T }
672
+ : undefined;
673
+ }
674
+ jsxSemantic(path) {
675
+ if (path.isJSXIdentifier()) {
676
+ const binding = path.scope.getBinding(path.node.name);
677
+ return binding === undefined ? undefined : this.resolveBinding(binding);
678
+ }
679
+ return path.isJSXMemberExpression()
680
+ ? this.jsxMemberSemantic(path)
681
+ : undefined;
682
+ }
683
+ jsxMemberSemantic(path) {
684
+ const object = oneChild(path, "object");
685
+ const property = oneChild(path, "property");
686
+ if (object === undefined || property?.isJSXIdentifier() !== true) {
687
+ return;
688
+ }
689
+ const base = this.jsxSemantic(object);
690
+ return base === undefined
691
+ ? undefined
692
+ : this.semanticMember(base, property.node.name);
693
+ }
694
+ resolveFunction(input) {
695
+ if (input === undefined) {
696
+ return;
697
+ }
698
+ const path = unwrapPath(input);
699
+ if (path.isFunction() || path.isClass()) {
700
+ return path;
701
+ }
702
+ if (!path.isIdentifier()) {
703
+ return;
704
+ }
705
+ return this.resolveFunctionBinding(path.scope.getBinding(path.node.name));
706
+ }
707
+ resolveFunctionBinding(binding) {
708
+ if (binding?.path.isFunctionDeclaration() === true || binding?.path.isClassDeclaration() === true) {
709
+ return binding.path;
710
+ }
711
+ return binding?.path.isVariableDeclarator() === true
712
+ ? this.resolveFunction(oneChild(binding.path, "init"))
713
+ : undefined;
714
+ }
715
+ registerPatternBinding(path, semantic) {
716
+ const name = patternLocalName(path);
717
+ const binding = name === undefined ? undefined : path.scope.getBinding(name);
718
+ if (binding === undefined) {
719
+ return;
720
+ }
721
+ const existing = this.specialBindings.get(binding);
722
+ if (existing !== undefined && JSON.stringify(existing) !== JSON.stringify(semantic)) {
723
+ this.errors.add(`A translation binding cannot be reused with conflicting scopes in ${this.file}`);
724
+ return;
725
+ }
726
+ this.specialBindings.set(binding, semantic);
727
+ }
728
+ registerSemanticPattern(path, semantic) {
729
+ const target = this.semanticPatternTarget(path);
730
+ if (target.isIdentifier()) {
731
+ this.registerPatternBinding(target, semantic);
732
+ return;
733
+ }
734
+ if (target.isObjectPattern()) {
735
+ this.registerObjectSemanticPattern(target, semantic);
736
+ }
737
+ }
738
+ semanticPatternTarget(path) {
739
+ const target = unwrapPath(path);
740
+ return target.isAssignmentPattern()
741
+ ? oneChild(target, "left") ?? target
742
+ : target;
743
+ }
744
+ registerObjectSemanticPattern(pattern, semantic) {
745
+ for (const property of childPaths(pattern, "properties")) {
746
+ this.registerSemanticProperty(property, semantic);
747
+ }
748
+ }
749
+ registerSemanticProperty(property, semantic) {
750
+ if (!property.isObjectProperty()) {
751
+ return;
752
+ }
753
+ const key = oneChild(property, "key");
754
+ const value = oneChild(property, "value");
755
+ const name = key === undefined ? undefined : staticPropertyName(key);
756
+ const member = name === undefined ? undefined : this.semanticMember(semantic, name);
757
+ if (value !== undefined && member !== undefined) {
758
+ this.registerSemanticPattern(value, member);
759
+ }
760
+ }
761
+ registerTParameter(fn, index, t) {
762
+ const parameter = childPaths(fn, "params")[index];
763
+ if (parameter !== undefined) {
764
+ this.registerPatternBinding(parameter, t);
765
+ }
766
+ }
767
+ registerHocComponent(component, info) {
768
+ const target = this.resolveFunction(component);
769
+ if (target === undefined) {
770
+ return;
771
+ }
772
+ const t = { fixed: true, info, kind: "t" };
773
+ if (target.isClass()) {
774
+ this.hocClasses.set(target.node, t);
775
+ return;
776
+ }
777
+ this.registerHocParameter(childPaths(target, "params")[0], t);
778
+ }
779
+ registerHocParameter(parameter, t) {
780
+ if (parameter === undefined) {
781
+ return;
782
+ }
783
+ const unwrapped = unwrapPath(parameter);
784
+ if (unwrapped.isIdentifier() || unwrapped.isAssignmentPattern()) {
785
+ this.registerPatternBinding(unwrapped, { instance: false, kind: "translationObject", t });
786
+ return;
787
+ }
788
+ if (unwrapped.isObjectPattern()) {
789
+ this.registerHocObjectPattern(unwrapped, t);
790
+ }
791
+ }
792
+ registerHocObjectPattern(pattern, t) {
793
+ for (const property of childPaths(pattern, "properties")) {
794
+ this.registerHocProperty(property, t);
795
+ }
796
+ }
797
+ registerHocProperty(property, t) {
798
+ if (!property.isObjectProperty()) {
799
+ return;
800
+ }
801
+ const key = oneChild(property, "key");
802
+ const value = oneChild(property, "value");
803
+ if (key !== undefined && value !== undefined && staticPropertyName(key) === "t") {
804
+ this.registerPatternBinding(value, t);
805
+ }
806
+ }
807
+ jsxAttribute(element, name) {
808
+ const opening = oneChild(element, "openingElement");
809
+ if (opening === undefined) {
810
+ return;
811
+ }
812
+ for (const attribute of childPaths(opening, "attributes").toReversed()) {
813
+ const value = this.jsxAttributeValue(attribute, name);
814
+ if (value !== undefined) {
815
+ return value;
816
+ }
817
+ }
818
+ return undefined;
819
+ }
820
+ jsxAttributeValue(attribute, name) {
821
+ if (attribute.isJSXSpreadAttribute()) {
822
+ return objectProperty(oneChild(attribute, "argument"), name);
823
+ }
824
+ if (!attribute.isJSXAttribute()) {
825
+ return;
826
+ }
827
+ const attributeName = oneChild(attribute, "name");
828
+ if (attributeName?.isJSXIdentifier() !== true || attributeName.node.name !== name) {
829
+ return;
830
+ }
831
+ const value = oneChild(attribute, "value");
832
+ return value?.isJSXExpressionContainer() === true ? oneChild(value, "expression") : value;
833
+ }
834
+ scopeFromJsx(element) {
835
+ return normalizedInfo(staticNamespaces(this.jsxAttribute(element, "ns"), this.file), staticKeyPrefix(this.jsxAttribute(element, "keyPrefix"), this.file));
836
+ }
837
+ addComponentMessage(input) {
838
+ const componentKeys = this.componentKeys(input);
839
+ if (componentKeys === undefined) {
840
+ return;
841
+ }
842
+ if (!this.validateIcu(input, componentKeys)) {
843
+ return;
844
+ }
845
+ const t = this.componentT(input.tPath);
846
+ if (!t.isValid) {
847
+ return;
848
+ }
849
+ const context = this.componentContext(input);
850
+ if (!context.isValid) {
851
+ return;
852
+ }
853
+ const scope = this.componentScope(input, componentKeys.rawKeys, t.value, context.value);
854
+ this.addScopedComponentMessages(input, componentKeys, scope);
855
+ }
856
+ addScopedComponentMessages(input, componentKeys, scope) {
857
+ if (this.componentHasCount(input)) {
858
+ this.addPluralComponentMessages(input, componentKeys.fallback, scope);
859
+ }
860
+ else {
861
+ this.addPointComponentMessages(input, componentKeys, scope);
862
+ }
863
+ }
864
+ componentKeys(input) {
865
+ const optionDefault = objectProperty(input.optionsPath, "defaultValue");
866
+ const fallback = literalString(input.defaultPath) ?? literalString(optionDefault);
867
+ const explicitKeys = this.explicitComponentKeys(input.keyPath);
868
+ if (explicitKeys === undefined) {
869
+ return;
870
+ }
871
+ const rawKeys = explicitKeys.length > 0 ? explicitKeys : this.fallbackKeys(fallback);
872
+ if (rawKeys.length === 0) {
873
+ this.errors.add(`Translation components require a static key or default in ${this.file}`);
874
+ return;
875
+ }
876
+ return { fallback, rawKeys };
877
+ }
878
+ explicitComponentKeys(keyPath) {
879
+ if (keyPath === undefined) {
880
+ return [];
881
+ }
882
+ const keys = namespaceValues(keyPath);
883
+ if (keys.length === 0) {
884
+ this.errors.add(`Translation component keys must be static strings in ${this.file}`);
885
+ return;
886
+ }
887
+ return keys;
888
+ }
889
+ fallbackKeys(fallback) {
890
+ return fallback === undefined ? [] : [fallback];
891
+ }
892
+ validateIcu(input, keys) {
893
+ if (!input.isIcu) {
894
+ return true;
895
+ }
896
+ if (keys.rawKeys.length > 1) {
897
+ this.errors.add(`ICU translations require one static key in ${this.file}`);
898
+ return false;
899
+ }
900
+ return this.validateIcuFallback(input, keys.fallback);
901
+ }
902
+ validateIcuFallback(input, fallback) {
903
+ if (fallback === undefined) {
904
+ this.errors.add(`ICU translations require a static defaultTranslation in ${this.file}`);
905
+ return false;
906
+ }
907
+ const withoutInterpolation = fallback.replaceAll(/\{\{[^{}]+\}\}/gu, "");
908
+ if (/\{[^{}]+\}/u.test(withoutInterpolation)) {
909
+ this.errors.add(`GTKX gettext catalogs do not support ICU MessageFormat syntax in ${this.file}`);
910
+ return false;
911
+ }
912
+ return this.validateIcuCount(input, fallback);
913
+ }
914
+ validateIcuCount(input, fallback) {
915
+ const hasValueCount = objectProperty(input.valuesPath, "count") !== undefined;
916
+ if (hasValueCount || /\{\{\s*count\s*\}\}/u.test(fallback)) {
917
+ this.errors.add(`ICU translations cannot infer a GNU gettext plural pair in ${this.file}`);
918
+ return false;
919
+ }
920
+ return true;
921
+ }
922
+ componentT(path) {
923
+ if (path === undefined) {
924
+ return { isValid: true };
925
+ }
926
+ const semantic = this.resolvePath(path);
927
+ if (semantic?.kind !== "t") {
928
+ this.errors.add(`Translation component t props must have a statically known scope in ${this.file}`);
929
+ return { isValid: false };
930
+ }
931
+ return { isValid: true, value: semantic };
932
+ }
933
+ componentContext(input) {
934
+ const optionContext = objectProperty(input.optionsPath, "context");
935
+ const contextPath = input.contextPath ?? optionContext;
936
+ if (contextPath === undefined) {
937
+ return { isValid: true };
938
+ }
939
+ const context = literalString(contextPath);
940
+ if (context === undefined) {
941
+ this.errors.add(`Translation contexts must be static strings in ${this.file}`);
942
+ return { isValid: false };
943
+ }
944
+ return { isValid: true, value: context };
945
+ }
946
+ componentScope(input, rawKeys, t, context) {
947
+ const prefix = t?.info.keyPrefix;
948
+ const baseKeys = rawKeys.map((key) => prefix === undefined ? key : `${prefix}.${key}`);
949
+ const keys = baseKeys.map((key) => context === undefined
950
+ ? key
951
+ : `${key}${CONTEXT_SEPARATOR}${context}`);
952
+ const explicitNs = staticNamespaces(input.namespacePath, this.file)[0];
953
+ return { keys, ns: explicitNs ?? t?.info.defaultNs };
954
+ }
955
+ componentHasCount(input) {
956
+ return input.countPath !== undefined ||
957
+ objectProperty(input.optionsPath, "count") !== undefined ||
958
+ objectProperty(input.valuesPath, "count") !== undefined;
959
+ }
960
+ addPluralComponentMessages(input, fallback, scope) {
961
+ if (input.isIcu) {
962
+ this.errors.add(`ICU translations cannot infer a GNU gettext plural pair in ${this.file}`);
963
+ return;
964
+ }
965
+ if (this.hasUnsupportedPluralOptions(input.optionsPath)) {
966
+ this.errors.add(`GNU gettext catalogs only support cardinal one/other source pairs in ${this.file}`);
967
+ return;
968
+ }
969
+ const defaults = this.pluralDefaults(input.optionsPath, fallback);
970
+ if (defaults === undefined) {
971
+ return;
972
+ }
973
+ for (const key of scope.keys) {
974
+ this.addPluralPair({ key, ns: scope.ns, path: input.path, ...defaults });
975
+ }
976
+ }
977
+ pluralDefaults(options, fallback) {
978
+ const singular = literalString(objectProperty(options, "defaultValue_one")) ?? fallback;
979
+ const plural = literalString(objectProperty(options, "defaultValue_other"));
980
+ if (singular === undefined || plural === undefined || singular === plural) {
981
+ this.errors.add(`Count-based Trans calls require distinct static singular and plural defaults in ${this.file}`);
982
+ return;
983
+ }
984
+ return { plural, singular };
985
+ }
986
+ hasUnsupportedPluralOptions(options) {
987
+ return objectProperty(options, "ordinal") !== undefined ||
988
+ objectProperty(options, "defaultValue_zero") !== undefined;
989
+ }
990
+ addPluralPair(input) {
991
+ this.addMessage({
992
+ defaultValue: input.singular,
993
+ hasExplicitDefault: true,
994
+ key: `${input.key}_one`,
995
+ ns: input.ns,
996
+ path: input.path,
997
+ });
998
+ this.addMessage({
999
+ defaultValue: input.plural,
1000
+ hasExplicitDefault: true,
1001
+ key: `${input.key}_other`,
1002
+ ns: input.ns,
1003
+ path: input.path,
1004
+ });
1005
+ }
1006
+ addPointComponentMessages(input, componentKeys, scope) {
1007
+ for (const [index, key] of scope.keys.entries()) {
1008
+ const defaultValue = componentKeys.fallback ?? componentKeys.rawKeys[index] ?? key;
1009
+ this.addMessage({
1010
+ defaultValue,
1011
+ hasExplicitDefault: componentKeys.fallback !== undefined,
1012
+ key,
1013
+ ns: scope.ns,
1014
+ path: input.path,
1015
+ });
1016
+ }
1017
+ }
1018
+ addMessage(input) {
1019
+ this.messages.push({
1020
+ defaultValue: input.defaultValue,
1021
+ explicitDefault: input.hasExplicitDefault,
1022
+ key: input.key,
1023
+ location: pathLocation(input.path, this.file),
1024
+ ...(input.ns !== undefined && { ns: input.ns }),
1025
+ });
1026
+ }
1027
+ markTranslationCallback(callback, info) {
1028
+ const fn = this.resolveFunction(callback);
1029
+ if (fn !== undefined && !fn.isClass()) {
1030
+ this.registerTParameter(fn, 0, { fixed: true, info, kind: "t" });
1031
+ this.registerTranslationContextParameter(fn);
1032
+ }
1033
+ }
1034
+ registerTranslationContextParameter(fn) {
1035
+ const parameter = childPaths(fn, "params")[1];
1036
+ if (parameter !== undefined) {
1037
+ this.registerSemanticPattern(parameter, { kind: "translationContext" });
1038
+ }
1039
+ }
1040
+ registerCallSemantics(path) {
1041
+ const callee = oneChild(path, "callee");
1042
+ const called = callee === undefined ? undefined : this.resolvePath(callee);
1043
+ this.registerHocCall(path, called);
1044
+ this.registerTranslationCallbackCall(path, called);
1045
+ this.registerInitCallback(path, called);
1046
+ this.registerInitPromiseCallback(path, callee);
1047
+ this.registerCallableComponent(path, called);
1048
+ }
1049
+ registerHocCall(path, called) {
1050
+ if (called?.kind === "enhancer") {
1051
+ this.registerHocComponent(callArguments(path)[0], called.info);
1052
+ }
1053
+ }
1054
+ registerTranslationCallbackCall(path, called) {
1055
+ if (called?.kind !== "api" || called.api !== "Translation") {
1056
+ return;
1057
+ }
1058
+ const props = callArguments(path)[0];
1059
+ const info = normalizedInfo(staticNamespaces(objectProperty(props, "ns"), this.file), staticKeyPrefix(objectProperty(props, "keyPrefix"), this.file));
1060
+ this.markTranslationCallback(objectProperty(props, "children"), info);
1061
+ }
1062
+ registerInitCallback(path, called) {
1063
+ if (called?.kind !== "api" || called.api !== "init") {
1064
+ return;
1065
+ }
1066
+ const callback = callArguments(path).findLast((argument) => this.resolveFunction(argument) !== undefined);
1067
+ const fn = this.resolveFunction(callback);
1068
+ if (fn !== undefined && !fn.isClass()) {
1069
+ this.registerTParameter(fn, 1, GLOBAL_T);
1070
+ }
1071
+ }
1072
+ registerInitPromiseCallback(path, callee) {
1073
+ if (callee === undefined) {
1074
+ return;
1075
+ }
1076
+ if (memberProperty(callee) !== "then") {
1077
+ return;
1078
+ }
1079
+ const receiver = oneChild(unwrapPath(callee), "object");
1080
+ const received = receiver === undefined ? undefined : this.resolvePath(receiver);
1081
+ if (received?.kind !== "initPromise") {
1082
+ return;
1083
+ }
1084
+ this.registerInitPromiseFunction(this.resolveFunction(callArguments(path)[0]));
1085
+ }
1086
+ registerInitPromiseFunction(fn) {
1087
+ if (fn === undefined || fn.isClass()) {
1088
+ return;
1089
+ }
1090
+ this.registerTParameter(fn, 0, GLOBAL_T);
1091
+ }
1092
+ registerCallableComponent(path, called) {
1093
+ if (called?.kind !== "api") {
1094
+ return;
1095
+ }
1096
+ if (["IcuTrans", "IcuTransWithoutContext"].includes(called.api)) {
1097
+ this.addComponentMessage(this.callableIcuInput(path));
1098
+ }
1099
+ if (["Trans", "TransWithoutContext"].includes(called.api)) {
1100
+ this.addComponentMessage(this.callableTransInput(path));
1101
+ }
1102
+ }
1103
+ callableIcuInput(path) {
1104
+ const props = callArguments(path)[0];
1105
+ return {
1106
+ defaultPath: objectProperty(props, "defaultTranslation"),
1107
+ isIcu: true,
1108
+ keyPath: objectProperty(props, "i18nKey"),
1109
+ namespacePath: objectProperty(props, "ns"),
1110
+ path,
1111
+ tPath: objectProperty(props, "t"),
1112
+ valuesPath: objectProperty(props, "values"),
1113
+ };
1114
+ }
1115
+ callableTransInput(path) {
1116
+ const props = callArguments(path)[0];
1117
+ return {
1118
+ contextPath: objectProperty(props, "context"),
1119
+ countPath: objectProperty(props, "count"),
1120
+ defaultPath: objectProperty(props, "defaults") ?? objectProperty(props, "children"),
1121
+ isIcu: false,
1122
+ keyPath: objectProperty(props, "i18nKey"),
1123
+ namespacePath: objectProperty(props, "ns"),
1124
+ optionsPath: objectProperty(props, "tOptions"),
1125
+ path,
1126
+ tPath: objectProperty(props, "t"),
1127
+ valuesPath: objectProperty(props, "values"),
1128
+ };
1129
+ }
1130
+ registerBlockedGetFixedT(path) {
1131
+ const callee = this.possibleGetFixedCallee(path);
1132
+ if (callee === undefined) {
1133
+ return;
1134
+ }
1135
+ const semantic = this.resolvePath(callee);
1136
+ if (this.isGetFixedApi(semantic)) {
1137
+ return;
1138
+ }
1139
+ const key = spanKey(path.node);
1140
+ if (key !== undefined) {
1141
+ this.blockedGetFixedT.add(key);
1142
+ }
1143
+ }
1144
+ possibleGetFixedCallee(path) {
1145
+ const init = oneChild(path, "init");
1146
+ if (!init?.isCallExpression()) {
1147
+ return;
1148
+ }
1149
+ const callee = oneChild(init, "callee");
1150
+ return callee !== undefined && memberProperty(callee) === "getFixedT"
1151
+ ? callee
1152
+ : undefined;
1153
+ }
1154
+ isGetFixedApi(semantic) {
1155
+ return semantic?.kind === "api" && semantic.api === "getFixedT";
1156
+ }
1157
+ registerJsxElement(path) {
1158
+ const opening = oneChild(path, "openingElement");
1159
+ const name = opening === undefined ? undefined : oneChild(opening, "name");
1160
+ const component = name === undefined ? undefined : this.jsxSemantic(name);
1161
+ if (opening === undefined || component?.kind !== "api") {
1162
+ return;
1163
+ }
1164
+ this.registerApiJsx(path, opening, component.api);
1165
+ }
1166
+ registerApiJsx(path, opening, api) {
1167
+ if (["Trans", "TransWithoutContext"].includes(api)) {
1168
+ this.registerTransJsx(path, opening);
1169
+ return;
1170
+ }
1171
+ if (api === "Translation") {
1172
+ this.registerTranslationJsx(path);
1173
+ return;
1174
+ }
1175
+ if (["IcuTrans", "IcuTransWithoutContext"].includes(api)) {
1176
+ this.addComponentMessage(this.jsxIcuInput(path));
1177
+ }
1178
+ }
1179
+ registerTransJsx(path, opening) {
1180
+ if (!this.validateTransJsxProps(path, opening)) {
1181
+ return;
1182
+ }
1183
+ const key = spanKey(path.node);
1184
+ if (key === undefined) {
1185
+ return;
1186
+ }
1187
+ const scope = this.transJsxScope(path);
1188
+ if (!scope.isValid) {
1189
+ return;
1190
+ }
1191
+ this.trans.set(key, scope.value ?? null);
1192
+ }
1193
+ validateTransJsxProps(path, opening) {
1194
+ const hasSpread = childPaths(opening, "attributes").some((attribute) => attribute.isJSXSpreadAttribute());
1195
+ if (hasSpread) {
1196
+ this.errors.add(`Trans JSX props must be written explicitly for static extraction in ${this.file}`);
1197
+ return false;
1198
+ }
1199
+ if (isStaticStringArray(this.jsxAttribute(path, "i18nKey"))) {
1200
+ this.errors.add(`Trans JSX i18nKey arrays are not supported by static extraction in ${this.file}`);
1201
+ return false;
1202
+ }
1203
+ return true;
1204
+ }
1205
+ transJsxScope(path) {
1206
+ const tProp = this.jsxAttribute(path, "t");
1207
+ if (tProp === undefined) {
1208
+ return { isValid: true };
1209
+ }
1210
+ const semantic = this.resolvePath(tProp);
1211
+ if (semantic?.kind !== "t") {
1212
+ this.errors.add(`Trans t props must have a statically known scope in ${this.file}`);
1213
+ return { isValid: false };
1214
+ }
1215
+ if (!this.validatePrefixedTransNamespace(path, semantic)) {
1216
+ return { isValid: false };
1217
+ }
1218
+ return { isValid: true, value: semantic.info };
1219
+ }
1220
+ validatePrefixedTransNamespace(path, t) {
1221
+ if (t.info.keyPrefix === undefined || this.jsxAttribute(path, "ns") === undefined) {
1222
+ return true;
1223
+ }
1224
+ this.errors.add(`Prefixed Trans components cannot also override ns during static extraction in ${this.file}`);
1225
+ return false;
1226
+ }
1227
+ registerTranslationJsx(path) {
1228
+ const info = this.scopeFromJsx(path);
1229
+ this.markTranslationCallback(this.jsxAttribute(path, "children"), info);
1230
+ for (const child of childPaths(path, "children")) {
1231
+ if (child.isJSXExpressionContainer()) {
1232
+ this.markTranslationCallback(oneChild(child, "expression"), info);
1233
+ }
1234
+ }
1235
+ }
1236
+ jsxIcuInput(path) {
1237
+ return {
1238
+ defaultPath: this.jsxAttribute(path, "defaultTranslation"),
1239
+ isIcu: true,
1240
+ keyPath: this.jsxAttribute(path, "i18nKey"),
1241
+ namespacePath: this.jsxAttribute(path, "ns"),
1242
+ path,
1243
+ tPath: this.jsxAttribute(path, "t"),
1244
+ valuesPath: this.jsxAttribute(path, "values"),
1245
+ };
1246
+ }
1247
+ classT(callee) {
1248
+ if (memberProperty(callee) !== "t") {
1249
+ return;
1250
+ }
1251
+ const object = oneChild(unwrapPath(callee), "object");
1252
+ if (object === undefined || memberProperty(object) !== "props") {
1253
+ return;
1254
+ }
1255
+ const receiver = oneChild(unwrapPath(object), "object");
1256
+ if (receiver?.isThisExpression() !== true) {
1257
+ return;
1258
+ }
1259
+ const owner = callee.findParent(isClassPath);
1260
+ return owner === null ? undefined : this.hocClasses.get(owner.node);
1261
+ }
1262
+ optionsKeyPrefix(call) {
1263
+ for (const argument of callArguments(call).slice(1)) {
1264
+ const keyPrefix = objectProperty(argument, "keyPrefix");
1265
+ if (keyPrefix !== undefined) {
1266
+ return keyPrefix;
1267
+ }
1268
+ }
1269
+ return undefined;
1270
+ }
1271
+ registerTranslationCall(path) {
1272
+ const callee = oneChild(path, "callee");
1273
+ if (callee === undefined) {
1274
+ return;
1275
+ }
1276
+ const resolved = this.resolvePath(callee);
1277
+ const t = resolved?.kind === "t" ? resolved : this.classT(callee);
1278
+ const key = spanKey(path.node);
1279
+ if (t === undefined || key === undefined) {
1280
+ return;
1281
+ }
1282
+ const info = this.translationCallScope(path, t);
1283
+ if (info !== undefined) {
1284
+ this.calls.set(key, { info, key });
1285
+ }
1286
+ }
1287
+ translationCallScope(path, t) {
1288
+ const keyPrefix = this.optionsKeyPrefix(path);
1289
+ if (keyPrefix === undefined) {
1290
+ return t.info;
1291
+ }
1292
+ const literalPrefix = literalString(keyPrefix);
1293
+ if (literalPrefix === undefined) {
1294
+ this.errors.add(`Translation keyPrefix options must be static strings in ${this.file}`);
1295
+ return;
1296
+ }
1297
+ if (!t.fixed) {
1298
+ this.errors.add(`keyPrefix options require a fixed translation function in ${this.file}`);
1299
+ return;
1300
+ }
1301
+ return scopeWithPrefix(t.info, literalPrefix);
1302
+ }
1303
+ registerTaggedTranslation(path) {
1304
+ const tag = oneChild(path, "tag");
1305
+ const resolved = tag === undefined ? undefined : this.resolvePath(tag);
1306
+ if (resolved?.kind === "t") {
1307
+ this.errors.add(`Tagged-template translations are not compatible with strict GTKX codegen in ${this.file}`);
1308
+ }
1309
+ }
1310
+ analyze(program) {
1311
+ this.runSemanticPass(program);
1312
+ this.bindingCache.clear();
1313
+ this.runTranslationPass(program);
1314
+ return {
1315
+ blockedGetFixedT: this.blockedGetFixedT,
1316
+ calls: this.calls,
1317
+ errors: [...this.errors],
1318
+ messages: this.messages,
1319
+ trans: this.trans,
1320
+ };
1321
+ }
1322
+ }
1323
+ export { extractBindings };
1324
+ //# sourceMappingURL=binding-extractor.js.map