@menapo/vi18n 0.0.0-stage → 0.1.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 (100) hide show
  1. package/LICENSE +49 -0
  2. package/README.md +110 -2
  3. package/dist/apply-Bre-lY9J.js +228 -0
  4. package/dist/captured-Bj8bO6vd.js +1034 -0
  5. package/dist/check-D34P-S--.js +1 -0
  6. package/dist/check-DyrAbv73.js +201 -0
  7. package/dist/cli.d.ts +1 -0
  8. package/dist/cli.js +41 -0
  9. package/dist/codec-DuvU2VQy.js +207 -0
  10. package/dist/collect-Cx4_SKOe.js +13 -0
  11. package/dist/collisions-CfpZU8e4.js +217 -0
  12. package/dist/compile-B5Mz6WJm.js +245 -0
  13. package/dist/compile-Cc97JaW2.js +2502 -0
  14. package/dist/concurrency-C9UUWuDC.js +96 -0
  15. package/dist/concurrency-DONL7t4V.js +1 -0
  16. package/dist/config.d.ts +21 -0
  17. package/dist/config.js +39 -0
  18. package/dist/dir-zw6bGMKQ.js +290 -0
  19. package/dist/duplicates-yuRljQ6h.js +185 -0
  20. package/dist/engine-B7p40M__.js +531 -0
  21. package/dist/engine.d.ts +43 -0
  22. package/dist/engine.js +1241 -0
  23. package/dist/exclude-Du-Zd_qI.js +26 -0
  24. package/dist/export-DGsMbcTZ.js +103 -0
  25. package/dist/extract-Dujlsgnk.js +298 -0
  26. package/dist/formats-KPIg-08V.js +781 -0
  27. package/dist/i18next-CrUnKFfh.js +1596 -0
  28. package/dist/i18next-Rgo-v9l0.js +1 -0
  29. package/dist/import-Cwdhcywt.js +225 -0
  30. package/dist/index.d.ts +156 -0
  31. package/dist/index.js +239 -0
  32. package/dist/init-BecCTvKi.js +792 -0
  33. package/dist/library-D5CWvTQs.js +1882 -0
  34. package/dist/lingui--PcOOYry.js +833 -0
  35. package/dist/lint.d.ts +105 -0
  36. package/dist/lint.js +971 -0
  37. package/dist/load-CQRz4PpM.js +2950 -0
  38. package/dist/load-gW-oJt1Q.js +747 -0
  39. package/dist/locales-DX4MOBFE.js +827 -0
  40. package/dist/lsp-Cq1Nuu-Q.js +31 -0
  41. package/dist/match-BGr8EXB3.js +89 -0
  42. package/dist/mcp-Cf0T7qPW.js +28 -0
  43. package/dist/merge-driver-CpiiSVIc.js +422 -0
  44. package/dist/messages-Cxa-5tR0.js +166 -0
  45. package/dist/migrate-CntqvrW4.js +1 -0
  46. package/dist/migrate-dPgq9uzy.js +1484 -0
  47. package/dist/openai-CTaMDcl-.js +346 -0
  48. package/dist/output-DsaHKHEt.js +164 -0
  49. package/dist/parse.d.ts +62 -0
  50. package/dist/parse.js +156 -0
  51. package/dist/pipeline-C7g11THL.js +2031 -0
  52. package/dist/pipeline-wxS0SApG.js +1 -0
  53. package/dist/project-Dwx7iPm6.js +381 -0
  54. package/dist/project-pXipT8iG.js +1046 -0
  55. package/dist/pseudo-DRv30iN5.js +145 -0
  56. package/dist/raw-C4Y6vyGY.js +4708 -0
  57. package/dist/react-intl-CaBVwJ9u.js +519 -0
  58. package/dist/restore-Btq66Tid.js +175 -0
  59. package/dist/rewrite-CiTRAICl.js +803 -0
  60. package/dist/rich-DCUKl5HM.js +97 -0
  61. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  62. package/dist/run-DYD_VXKy.js +694 -0
  63. package/dist/runtime-DTMmliT1.js +64 -0
  64. package/dist/scan-worker.d.ts +62 -0
  65. package/dist/scan-worker.js +1 -0
  66. package/dist/scoped-BMYpSthj.js +44 -0
  67. package/dist/server-BDkykMjb.js +11823 -0
  68. package/dist/server-qv3mz70d.js +10485 -0
  69. package/dist/server.d.ts +169 -0
  70. package/dist/server.js +1234 -0
  71. package/dist/sitemap-DV3o8QD7.js +159 -0
  72. package/dist/sources-D19czDSA.js +2242 -0
  73. package/dist/status-DZArW5aE.js +149 -0
  74. package/dist/stdio-CVaJfxA7.js +29 -0
  75. package/dist/stdio-FPOKQAgu.js +95 -0
  76. package/dist/sync-CcVzger_.js +658 -0
  77. package/dist/t-DC-c9zE5.js +454 -0
  78. package/dist/test.d.ts +116 -0
  79. package/dist/test.js +319 -0
  80. package/dist/translate-DZR3MkSA.js +299 -0
  81. package/dist/ts-BaU5yOf0.js +4863 -0
  82. package/dist/types-CgUCkFU1.d.ts +707 -0
  83. package/dist/types-D3QGM8XO.js +146 -0
  84. package/dist/types-DsdWBG0b.d.ts +558 -0
  85. package/dist/types-DtvQ7UdD.js +1948 -0
  86. package/dist/types-wSh9fCBY.js +11 -0
  87. package/dist/units-CQ5Q99RC.js +150 -0
  88. package/dist/url-CRfCOhUI.js +172 -0
  89. package/dist/url-CsBkRaJy.js +225 -0
  90. package/dist/validate-BxpupdAf.js +2615 -0
  91. package/dist/validate-D6DJepZv.js +365 -0
  92. package/dist/version-BlyLGlOa.js +28 -0
  93. package/dist/vite-scan-B_YW4X_a.js +242 -0
  94. package/dist/vite.d.ts +34 -0
  95. package/dist/vite.js +7558 -0
  96. package/dist/vue-i18n-CEMnpv7k.js +651 -0
  97. package/dist/which-C6PFI1Dm.js +116 -0
  98. package/dist/worker-84U6uYB3.js +1656 -0
  99. package/package.json +97 -3
  100. package/schemas/catalog.json +57 -0
@@ -0,0 +1,833 @@
1
+ import { o as isDiagnosticError } from "./types-D3QGM8XO.js";
2
+ import { n as printICU } from "./codec-DuvU2VQy.js";
3
+ import { h as parsePo } from "./library-D5CWvTQs.js";
4
+ import { l as langFromId, s as decodeEntities, u as parseModule } from "./worker-84U6uYB3.js";
5
+ import { _ as keyName, a as attributeExpression, b as stringValue, g as jsxNameNodes, h as jsxName, i as analyzeModule, l as identifierName, o as attributeString, p as jsxAttributes, s as childEntries, t as ModuleBuilder, u as inJsxChildren, v as memberParts, x as unwrap, y as objectProperties } from "./rewrite-CiTRAICl.js";
6
+ import { _ as linguiId, c as newNaming, d as richCall, f as selfClosingTags, l as reactTypes, o as elementHandler, p as siteHandlers, s as entry, t as MODULE_EXTENSIONS, u as renameTags, v as parseJson } from "./i18next-CrUnKFfh.js";
7
+ import { extname } from "node:path";
8
+ import { readFileSync } from "node:fs";
9
+ //#region src/cli/commands/migrate/lingui.ts
10
+ /**
11
+ * `vi18n migrate --from lingui` (INTERNALS §2.14): Lingui 3–5 macros and
12
+ * runtime.
13
+ *
14
+ * The source text is what the macros compile to, built the way Lingui
15
+ * builds it (`${name}` → `{name}`, other expressions → `{0}`, `plural()` /
16
+ * `select()` / `selectOrdinal()` and `<Plural>`… as ICU, JSX elements as
17
+ * `<0>…</0>`), so translations are found under the id Lingui gives it
18
+ * (`generateMessageId(message, context)`, or the explicit `id`). Catalogs
19
+ * are PO (`#. js-lingui-explicit-id` marks an explicit id), Lingui JSON or
20
+ * minimal JSON; a fuzzy translation is kept for review, not shipped. A
21
+ * `context` is a translator note and, when two messages share a text but
22
+ * not their translations, the stem of their manual ids (PLAN §5.6).
23
+ * `t`, `msg`/`defineMessage`, `<Trans>`, `<Plural>`, `i18n._()` become
24
+ * `t()`, `msg()`, `rich()`; macro imports, `useLingui()` bindings and
25
+ * `<I18nProvider>` go once unused.
26
+ */
27
+ function isOldSource(source) {
28
+ return source.startsWith("@lingui/");
29
+ }
30
+ const MACRO_SOURCES = /* @__PURE__ */ new Set([
31
+ "@lingui/macro",
32
+ "@lingui/core/macro",
33
+ "@lingui/react/macro"
34
+ ]);
35
+ var MessageText = class MessageText {
36
+ text = "";
37
+ shared;
38
+ constructor(shared) {
39
+ this.shared = shared ?? {
40
+ values: [],
41
+ elements: [],
42
+ consumed: [],
43
+ positional: 0,
44
+ problem: null
45
+ };
46
+ }
47
+ get values() {
48
+ return this.shared.values;
49
+ }
50
+ get elements() {
51
+ return this.shared.elements;
52
+ }
53
+ get problem() {
54
+ return this.shared.problem;
55
+ }
56
+ /** A part of the same message (a plural case): same values and counters, its own text. */
57
+ nested() {
58
+ return new MessageText(this.shared);
59
+ }
60
+ /** `{name}` for an identifier, `{0}`, `{1}`… for other expressions, as Lingui names them. */
61
+ argument(expression) {
62
+ const n = unwrap(expression);
63
+ if (n.type === "Identifier") {
64
+ const name = n.name;
65
+ if (!this.values.some((v) => v.name === name)) this.values.push({
66
+ name,
67
+ expression: n
68
+ });
69
+ return name;
70
+ }
71
+ const name = String(this.shared.positional++);
72
+ this.values.push({
73
+ name,
74
+ expression: n
75
+ });
76
+ return name;
77
+ }
78
+ fail(problem) {
79
+ this.shared.problem ??= problem;
80
+ }
81
+ built() {
82
+ return {
83
+ message: this.text,
84
+ values: this.values,
85
+ elements: this.elements,
86
+ consumed: this.shared.consumed,
87
+ problem: this.problem
88
+ };
89
+ }
90
+ };
91
+ /** Lingui's whitespace rule for JSX messages: a line break and the indentation after it are one space. */
92
+ function normalizeWhitespace(text) {
93
+ return text.replace(/(?:\\(?:\r\n|\r|\n))+\s+/g, " ").replace(/(?:\r\n|\r|\n)+\s+/g, " ").trim();
94
+ }
95
+ /** `=N` for numeric choice keys (`0`, `_0`), CLDR categories as they are, select values without `_`. */
96
+ function choiceKey(key) {
97
+ if (/^\d+$/.test(key)) return `=${key}`;
98
+ if (/^_\d+$/.test(key)) return `=${key.slice(1)}`;
99
+ if (key.startsWith("_")) return key.slice(1);
100
+ return key;
101
+ }
102
+ function createLinguiAdapter() {
103
+ /** locale → Lingui id → entry. */
104
+ const catalogs = /* @__PURE__ */ new Map();
105
+ /** v3 minimal JSON keys catalogs by message text: locale → text → translation. */
106
+ const byText = /* @__PURE__ */ new Map();
107
+ const namings = /* @__PURE__ */ new Map();
108
+ /** Source-catalog entries some call site found, for the unused-key report. */
109
+ const found = /* @__PURE__ */ new Set();
110
+ let sourceLocale = "";
111
+ const put = (locale, id, entryValue) => {
112
+ let map = catalogs.get(locale);
113
+ if (map === void 0) catalogs.set(locale, map = /* @__PURE__ */ new Map());
114
+ map.set(id, entryValue);
115
+ let texts = byText.get(locale);
116
+ if (texts === void 0) byText.set(locale, texts = /* @__PURE__ */ new Map());
117
+ if (entryValue.message !== null && !texts.has(entryValue.message)) texts.set(entryValue.message, entryValue);
118
+ };
119
+ const readCatalog = (file, env) => {
120
+ let text;
121
+ try {
122
+ text = readFileSync(file.file, "utf8");
123
+ } catch (error) {
124
+ env.report("catalog-parse", `cannot read: ${error.message}`, { file: file.file }, "error");
125
+ return;
126
+ }
127
+ if (extname(file.file) === ".po") {
128
+ const parsed = parsePo(text, {
129
+ locale: file.locale,
130
+ layout: "source",
131
+ isSourceLocale: false
132
+ });
133
+ for (const d of parsed.diagnostics) if (d.severity === "error") env.diagnostics.push({
134
+ ...d,
135
+ file: env.rel(file.file)
136
+ });
137
+ for (const e of parsed.catalog.entries) {
138
+ const explicit = e.contexts.some((c) => c.trim() === "js-lingui-explicit-id");
139
+ const idNote = e.contexts.map((c) => /^js-lingui-id:\s*(\S+)/.exec(c.trim())?.[1]).find((x) => x !== void 0);
140
+ const context = e.manualId ?? e.foreignContext;
141
+ const msgid = e.source ?? "";
142
+ const id = explicit ? msgid : idNote ?? linguiId(msgid, context);
143
+ put(file.locale, id, {
144
+ message: explicit ? file.locale === sourceLocale && e.translation !== "" ? e.translation : null : msgid,
145
+ translation: e.translation,
146
+ fuzzy: e.fuzzy,
147
+ context,
148
+ file: file.file,
149
+ line: e.line
150
+ });
151
+ }
152
+ return;
153
+ }
154
+ const parsed = parseJson(text);
155
+ if (parsed.problem !== null) {
156
+ env.report("catalog-parse", parsed.problem.message, {
157
+ file: file.file,
158
+ line: parsed.problem.line
159
+ }, "error");
160
+ return;
161
+ }
162
+ if (typeof parsed.value !== "object" || parsed.value === null || Array.isArray(parsed.value)) {
163
+ env.report("catalog-parse", "a Lingui JSON catalog is an object of messages by id", { file: file.file }, "error");
164
+ return;
165
+ }
166
+ const lines = text.split("\n");
167
+ for (const [id, value] of Object.entries(parsed.value)) {
168
+ const line = lines.findIndex((l) => l.includes(`${JSON.stringify(id)}:`)) + 1;
169
+ if (typeof value === "string") {
170
+ put(file.locale, id, {
171
+ message: null,
172
+ translation: value,
173
+ fuzzy: false,
174
+ context: null,
175
+ file: file.file,
176
+ line
177
+ });
178
+ let texts = byText.get(file.locale);
179
+ if (texts === void 0) byText.set(file.locale, texts = /* @__PURE__ */ new Map());
180
+ texts.set(id, {
181
+ message: id,
182
+ translation: value,
183
+ fuzzy: false,
184
+ context: null,
185
+ file: file.file,
186
+ line
187
+ });
188
+ } else if (typeof value === "object" && value !== null) {
189
+ const record = value;
190
+ put(file.locale, id, {
191
+ message: typeof record.message === "string" ? record.message : null,
192
+ translation: typeof record.translation === "string" ? record.translation : "",
193
+ fuzzy: record.fuzzy === true || Array.isArray(record.flags) && record.flags.includes("fuzzy"),
194
+ context: typeof record.context === "string" ? record.context : null,
195
+ file: file.file,
196
+ line
197
+ });
198
+ }
199
+ }
200
+ };
201
+ /** The catalog entry of a message in a locale: by id, else (v3) by its text. */
202
+ const lookup = (locale, id, message, context) => {
203
+ let entryFound = catalogs.get(locale)?.get(id);
204
+ if (entryFound === void 0 && message !== null) {
205
+ const byMessage = byText.get(locale)?.get(message);
206
+ if (byMessage !== void 0 && (byMessage.context ?? null) === context) entryFound = byMessage;
207
+ }
208
+ if (entryFound !== void 0 && locale === sourceLocale) {
209
+ found.add(entryFound);
210
+ const other = catalogs.get(locale)?.get(entryFound.message ?? "");
211
+ if (other !== void 0) found.add(other);
212
+ }
213
+ return entryFound;
214
+ };
215
+ /**
216
+ * Registers a message. `message` is the macro's text (null for a runtime
217
+ * lookup by explicit id, whose text comes from the source catalog).
218
+ */
219
+ const define = (env, input, origin, tags) => {
220
+ const id = input.id ?? linguiId(input.message ?? "", input.context);
221
+ if (env.message(id) !== void 0) {
222
+ lookup(sourceLocale, id, input.message, input.context);
223
+ return { key: id };
224
+ }
225
+ const sourceEntry = lookup(sourceLocale, id, input.message, input.context);
226
+ let source = input.message;
227
+ if (source === null) {
228
+ source = sourceEntry?.message ?? (sourceEntry?.translation || null);
229
+ if (source === null) return { problem: `${id} has no message in the code or the ${sourceLocale} catalog` };
230
+ }
231
+ const naming = newNaming();
232
+ const rename = (text, isSource) => {
233
+ if (tags === null) return text;
234
+ let nodes;
235
+ try {
236
+ nodes = env.codec.parse(text);
237
+ } catch (error) {
238
+ if (!isDiagnosticError(error)) throw error;
239
+ return { error: error.diagnostic.message };
240
+ }
241
+ return printICU(renameTags(nodes, naming, tags.pathOf, tags.elementAt, isSource));
242
+ };
243
+ const numbered = (text) => text.replace(/<(\/?)(\d+)(\/?)>/g, (_, a, n, b) => `<${a}t${n}${b}>`);
244
+ const sourceText = rename(numbered(source), true);
245
+ if (typeof sourceText !== "string") return { problem: `${id}: ${sourceText.error}` };
246
+ const translations = /* @__PURE__ */ new Map();
247
+ for (const locale of catalogs.keys()) {
248
+ if (locale === sourceLocale) continue;
249
+ const found = lookup(locale, id, input.message, input.context);
250
+ if (found === void 0 || found.translation === "") continue;
251
+ if (found.fuzzy) {
252
+ translations.set(locale, {
253
+ unconverted: found.translation,
254
+ reason: "Lingui marked it fuzzy",
255
+ file: found.file,
256
+ line: found.line
257
+ });
258
+ continue;
259
+ }
260
+ const converted = rename(numbered(found.translation), false);
261
+ translations.set(locale, typeof converted === "string" ? { icu: converted } : {
262
+ unconverted: found.translation,
263
+ reason: converted.error,
264
+ file: found.file,
265
+ line: found.line
266
+ });
267
+ }
268
+ const notes = [input.comment, input.context].filter((n) => n !== null && n !== "");
269
+ if (!env.define({
270
+ key: id,
271
+ named: input.id !== null,
272
+ source: sourceText,
273
+ context: notes.length === 0 ? null : notes.join("; "),
274
+ hint: input.context,
275
+ translations,
276
+ file: origin.file,
277
+ line: origin.line
278
+ })) return { problem: `${id} could not be converted` };
279
+ if (tags !== null) namings.set(id, naming);
280
+ return { key: id };
281
+ };
282
+ const planModule = (file, text, env) => {
283
+ let program;
284
+ try {
285
+ program = parseModule(text, langFromId(file), file).program;
286
+ } catch (error) {
287
+ if (!isDiagnosticError(error)) throw error;
288
+ env.diagnostics.push({
289
+ ...error.diagnostic,
290
+ file: env.rel(file)
291
+ });
292
+ return null;
293
+ }
294
+ const info = analyzeModule(program);
295
+ const builder = new ModuleBuilder({
296
+ file,
297
+ text,
298
+ info,
299
+ isOldSource
300
+ });
301
+ const tsx = extname(file) === ".tsx";
302
+ const lineOf = (offset) => text.slice(0, offset).split("\n").length;
303
+ const imported = (name, macro = null) => {
304
+ const locals = /* @__PURE__ */ new Set();
305
+ for (const declaration of info.imports) {
306
+ if (!isOldSource(declaration.source)) continue;
307
+ if (macro !== null && MACRO_SOURCES.has(declaration.source) !== macro) continue;
308
+ for (const s of declaration.specifiers) if (s.imported === name) locals.add(s.local);
309
+ }
310
+ return locals;
311
+ };
312
+ const macroT = imported("t", true);
313
+ const macroMsg = /* @__PURE__ */ new Set([...imported("msg", true), ...imported("defineMessage", true)]);
314
+ const choiceMacros = /* @__PURE__ */ new Map();
315
+ for (const [name, format] of [
316
+ ["plural", "plural"],
317
+ ["select", "select"],
318
+ ["selectOrdinal", "selectordinal"]
319
+ ]) for (const local of imported(name, true)) choiceMacros.set(local, format);
320
+ const transMacro = imported("Trans", true);
321
+ const choiceComponents = /* @__PURE__ */ new Map();
322
+ for (const [name, format] of [
323
+ ["Plural", "plural"],
324
+ ["Select", "select"],
325
+ ["SelectOrdinal", "selectordinal"]
326
+ ]) for (const local of imported(name, true)) choiceComponents.set(local, format);
327
+ const providers = imported("I18nProvider");
328
+ const runtimeI18n = imported("i18n");
329
+ const linguiT = /* @__PURE__ */ new Set();
330
+ const linguiUnderscore = /* @__PURE__ */ new Set();
331
+ const linguiInstances = /* @__PURE__ */ new Set();
332
+ const useLingui = imported("useLingui");
333
+ for (const binding of info.bindings) {
334
+ const init = binding.declarator?.init;
335
+ if (init === null || init === void 0) continue;
336
+ const call = unwrap(init);
337
+ const callee = call.type === "CallExpression" ? identifierName(call.callee) : null;
338
+ if (callee === null || !useLingui.has(callee)) continue;
339
+ builder.hooks.push(binding);
340
+ const slot = binding.slot;
341
+ const key = slot !== null && slot.type === "Property" ? keyName(slot) : null;
342
+ if (key === "t") linguiT.add(binding);
343
+ else if (key === "_") linguiUnderscore.add(binding);
344
+ else if (key === "i18n") linguiInstances.add(binding);
345
+ }
346
+ /** Locals holding `msg`/`defineMessage` results: their members are `msg()` refs now. */
347
+ const messageTables = /* @__PURE__ */ new Set();
348
+ for (const binding of info.bindings) {
349
+ const init = binding.declarator?.init;
350
+ if (init === null || init === void 0) continue;
351
+ let found = false;
352
+ const look = (n) => {
353
+ if (found) return;
354
+ if (n.type === "TaggedTemplateExpression" || n.type === "CallExpression") {
355
+ const callee = identifierName(n.type === "CallExpression" ? n.callee : n.tag);
356
+ if (callee !== null && macroMsg.has(callee)) found = true;
357
+ }
358
+ for (const [, child] of childEntries(n)) look(child);
359
+ };
360
+ look(init);
361
+ if (found) messageTables.add(binding.name);
362
+ }
363
+ const relativeImports = /* @__PURE__ */ new Set();
364
+ for (const declaration of info.imports) {
365
+ if (!declaration.source.startsWith(".")) continue;
366
+ for (const s of declaration.specifiers) relativeImports.add(s.local);
367
+ }
368
+ const resolve = (name, offset) => {
369
+ let best = null;
370
+ for (const b of info.bindings) {
371
+ if (b.name !== name || offset < b.scope.start || offset >= b.scope.end) continue;
372
+ if (best === null || b.scope.end - b.scope.start < best.scope.end - best.scope.start) best = b;
373
+ }
374
+ return best;
375
+ };
376
+ const isMacroT = (callee) => {
377
+ const n = unwrap(callee);
378
+ if (n.type === "CallExpression") return isMacroT(n.callee);
379
+ if (n.type !== "Identifier") return false;
380
+ const binding = resolve(n.name, n.start);
381
+ return binding !== null && (macroT.has(n.name) ? binding.kind === "import" : linguiT.has(binding));
382
+ };
383
+ const calleeNodes = (callee) => {
384
+ const n = unwrap(callee);
385
+ if (n.type === "CallExpression") return calleeNodes(n.callee);
386
+ return n.type === "Identifier" ? [n] : [];
387
+ };
388
+ /** Choice macros and components a message absorbed: not sites of their own. */
389
+ const absorbed = /* @__PURE__ */ new Set();
390
+ const template = (quasi, out) => {
391
+ const quasis = quasi.quasis;
392
+ const expressions = quasi.expressions;
393
+ for (let i = 0; i < quasis.length; i++) {
394
+ out.text += quasis[i].value.cooked ?? "";
395
+ const expression = expressions[i];
396
+ if (expression !== void 0) placeholder(expression, out);
397
+ }
398
+ };
399
+ const placeholder = (expression, out) => {
400
+ const n = unwrap(expression);
401
+ if (n.type === "CallExpression") {
402
+ const callee = identifierName(n.callee);
403
+ const format = callee === null ? void 0 : choiceMacros.get(callee);
404
+ if (format !== void 0) {
405
+ absorbed.add(n);
406
+ out.shared.consumed.push(unwrap(n.callee));
407
+ choice(n, format, out);
408
+ return;
409
+ }
410
+ }
411
+ out.text += `{${out.argument(n)}}`;
412
+ };
413
+ /** `plural(value, { one: "…", other: \`…\` })` as `{value, plural, one {…} other {…}}`. */
414
+ const choice = (call, format, out) => {
415
+ const [value, options] = call.arguments;
416
+ const props = options === void 0 ? null : objectProperties(options);
417
+ if (value === void 0 || props === null || props.spreads.length > 0) {
418
+ out.fail(`${format}() needs a value and an object literal of cases`);
419
+ return;
420
+ }
421
+ const name = out.argument(value);
422
+ const cases = [];
423
+ for (const [key, property] of props.properties) {
424
+ const v = unwrap(property.value);
425
+ if (key === "offset") {
426
+ cases.unshift(`offset:${String(v.value)}`);
427
+ continue;
428
+ }
429
+ const inner = out.nested();
430
+ const literal = stringValue(v);
431
+ if (literal !== null) inner.text = literal;
432
+ else if (v.type === "TemplateLiteral") template(v, inner);
433
+ else placeholder(v, inner);
434
+ cases.push(`${choiceKey(key)} {${inner.text}}`);
435
+ }
436
+ out.text += `{${name}, ${format}, ${cases.join(" ")}}`;
437
+ };
438
+ /** JSX children of `<Trans>` (or a choice component's JSX option). */
439
+ const jsx = (children, out) => {
440
+ for (const child of children) if (child.type === "JSXText") out.text += decodeEntities(child.value);
441
+ else if (child.type === "JSXExpressionContainer") {
442
+ const expression = unwrap(child.expression);
443
+ if (expression.type === "JSXEmptyExpression") continue;
444
+ const literal = stringValue(expression);
445
+ if (literal !== null) out.text += literal;
446
+ else if (expression.type === "TemplateLiteral") template(expression, out);
447
+ else placeholder(expression, out);
448
+ } else if (child.type === "JSXElement") {
449
+ const name = jsxName(child);
450
+ const format = choiceComponents.get(name);
451
+ if (format !== void 0) {
452
+ absorbed.add(child);
453
+ out.shared.consumed.push(...jsxNameNodes(child));
454
+ choiceComponent(child, format, out);
455
+ continue;
456
+ }
457
+ const index = out.elements.length;
458
+ out.elements.push(child);
459
+ if (child.children.filter((c) => !(c.type === "JSXText" && c.value.trim() === "" && c.value.includes("\n"))).length === 0) out.text += `<${index}/>`;
460
+ else {
461
+ out.text += `<${index}>`;
462
+ jsx(child.children, out);
463
+ out.text += `</${index}>`;
464
+ }
465
+ } else if (child.type === "JSXFragment") jsx(child.children, out);
466
+ };
467
+ /** `<Plural value={n} one="…" other={<b>…</b>} />` as an ICU plural. */
468
+ const choiceComponent = (element, format, out) => {
469
+ const attributes = jsxAttributes(element);
470
+ const value = attributeExpression(attributes.named.get("value")?.value ?? null);
471
+ if (value === null || attributes.spreads.length > 0) {
472
+ out.fail(`<${jsxName(element)}> needs a value={…} and no spread props`);
473
+ return;
474
+ }
475
+ const name = out.argument(value);
476
+ const cases = [];
477
+ for (const [key, { value: attribute }] of attributes.named) {
478
+ if (key === "value" || key === "id" || key === "comment" || key === "context" || key === "render") continue;
479
+ if (key === "offset") {
480
+ cases.unshift(`offset:${attributeString(attribute) ?? String(attributeExpression(attribute)?.value)}`);
481
+ continue;
482
+ }
483
+ const inner = out.nested();
484
+ const literal = attributeString(attribute);
485
+ if (literal !== null) inner.text = literal;
486
+ else {
487
+ const expression = attributeExpression(attribute);
488
+ if (expression === null) continue;
489
+ const n = unwrap(expression);
490
+ if (n.type === "JSXElement" || n.type === "JSXFragment") jsx([n], inner);
491
+ else if (n.type === "TemplateLiteral") template(n, inner);
492
+ else placeholder(n, inner);
493
+ }
494
+ cases.push(`${choiceKey(key)} {${inner.text}}`);
495
+ }
496
+ out.text += `{${name}, ${format}, ${cases.join(" ")}}`;
497
+ };
498
+ /** `{ id, message, context, comment }` of a macro descriptor (message a string or template). */
499
+ const descriptor = (node, out) => {
500
+ const props = objectProperties(node);
501
+ if (props === null || props.spreads.length > 0) return null;
502
+ const field = (name) => {
503
+ const property = props.properties.get(name);
504
+ if (property === void 0) return null;
505
+ return stringValue(property.value) ?? void 0;
506
+ };
507
+ const id = field("id");
508
+ const context = field("context");
509
+ const comment = field("comment");
510
+ if (id === void 0 || context === void 0 || comment === void 0) return null;
511
+ const messageProperty = props.properties.get("message");
512
+ if (messageProperty === void 0) return {
513
+ id,
514
+ message: null,
515
+ context,
516
+ comment
517
+ };
518
+ const value = unwrap(messageProperty.value);
519
+ const literal = stringValue(value);
520
+ if (literal !== null) out.text = literal;
521
+ else if (value.type === "TemplateLiteral") template(value, out);
522
+ else return null;
523
+ return {
524
+ id,
525
+ message: out.text,
526
+ context,
527
+ comment
528
+ };
529
+ };
530
+ const valuesCode = (r, values, extra = []) => {
531
+ const entries = [...extra, ...values.map(({ name, expression }) => entry(name, r.src(expression.start, expression.end)))];
532
+ return entries.length === 0 ? "" : `, { ${entries.join(", ")} }`;
533
+ };
534
+ /** A `t` macro (template or descriptor) or a standalone choice macro, as `t()`. */
535
+ const tSite = (node, built, input, consume) => {
536
+ if (built.problem !== null) {
537
+ builder.note(node.start, `${built.problem}; not rewritten`);
538
+ return;
539
+ }
540
+ const defined = define(env, {
541
+ ...input,
542
+ message: built.message
543
+ }, {
544
+ file,
545
+ line: lineOf(node.start)
546
+ }, null);
547
+ if ("problem" in defined) {
548
+ builder.note(node.start, `${defined.problem}; not rewritten`);
549
+ return;
550
+ }
551
+ builder.site(node, (r) => `${r.fn.t}(${r.message(defined.key)}${valuesCode(r, built.values)})`, {
552
+ keys: [defined.key],
553
+ uses: ["t"],
554
+ consume: [...consume, ...built.consumed]
555
+ });
556
+ };
557
+ /** `<Trans>` (or a choice component) whose message is `built`, as `rich()` or `t()`. */
558
+ const jsxSite = (element, built, input) => {
559
+ if (built.problem !== null) {
560
+ builder.note(element.start, `<${jsxName(element)}>: ${built.problem}; not rewritten`);
561
+ return;
562
+ }
563
+ const elements = built.elements;
564
+ const pathOf = (name) => /^t\d+$/.test(name) ? name.slice(1) : null;
565
+ const elementAt = (path) => elements[Number(path)] ?? null;
566
+ const defined = define(env, {
567
+ ...input,
568
+ message: built.message
569
+ }, {
570
+ file,
571
+ line: lineOf(element.start)
572
+ }, {
573
+ pathOf,
574
+ elementAt
575
+ });
576
+ if ("problem" in defined) {
577
+ builder.note(element.start, `${defined.problem}; <${jsxName(element)}> was not rewritten`);
578
+ return;
579
+ }
580
+ const message = env.message(defined.key);
581
+ const naming = namings.get(defined.key);
582
+ if (message === void 0 || naming === void 0) return;
583
+ const { handlers, missing } = siteHandlers(naming, elementAt);
584
+ if (missing.length > 0) {
585
+ builder.note(element.start, `<${jsxName(element)}>: a tag maps to no element here; not rewritten`, [defined.key]);
586
+ return;
587
+ }
588
+ const selfClosing = selfClosingTags(env.codec.parse(message.canonical));
589
+ const rich = handlers.size > 0;
590
+ const types = rich && tsx ? reactTypes(builder) : null;
591
+ builder.site(element, (r) => {
592
+ const handlerEntries = [...handlers].map(([name, handler]) => entry(name, elementHandler(handler, selfClosing.has(name), r, types)));
593
+ const values = built.values.map(({ name, expression }) => entry(name, r.src(expression.start, expression.end)));
594
+ const call = rich ? richCall(r, defined.key, [...handlerEntries, ...values], types) : `${r.fn.t}(${r.message(defined.key)}${values.length === 0 ? "" : `, { ${values.join(", ")} }`})`;
595
+ return inJsxChildren(info, element) ? `{${call}}` : call;
596
+ }, {
597
+ keys: [defined.key],
598
+ uses: [rich ? "rich" : "t"],
599
+ consume: [...jsxNameNodes(element), ...built.consumed]
600
+ });
601
+ };
602
+ const transElement = (element) => {
603
+ const attributes = jsxAttributes(element);
604
+ if (attributes.spreads.length > 0 || attributes.named.has("render") || attributes.named.has("component")) {
605
+ builder.note(element.start, "<Trans> with spread props, render or component was not rewritten");
606
+ return;
607
+ }
608
+ const read = (name) => attributes.named.has(name) ? attributeString(attributes.named.get(name)?.value ?? null) : null;
609
+ const out = new MessageText();
610
+ jsx(element.children, out);
611
+ out.text = normalizeWhitespace(out.text);
612
+ jsxSite(element, out.built(), {
613
+ id: read("id"),
614
+ context: read("context"),
615
+ comment: read("comment")
616
+ });
617
+ };
618
+ const choiceElement = (element, format) => {
619
+ const attributes = jsxAttributes(element);
620
+ const read = (name) => attributes.named.has(name) ? attributeString(attributes.named.get(name)?.value ?? null) : null;
621
+ const out = new MessageText();
622
+ choiceComponent(element, format, out);
623
+ jsxSite(element, out.built(), {
624
+ id: read("id"),
625
+ context: read("context"),
626
+ comment: read("comment")
627
+ });
628
+ };
629
+ /** `msg`/`defineMessage` as `msg()`: only without placeholders (a vi18n descriptor holds no values). */
630
+ const msgSite = (node, out, input) => {
631
+ if (out.problem !== null || out.values.length > 0) {
632
+ builder.note(node.start, out.problem ?? "msg/defineMessage with placeholders captures values, which a vi18n msg() descriptor does not: pass them to t() where it is rendered; not rewritten");
633
+ return;
634
+ }
635
+ const defined = define(env, {
636
+ ...input,
637
+ message: out.text
638
+ }, {
639
+ file,
640
+ line: lineOf(node.start)
641
+ }, null);
642
+ if ("problem" in defined) {
643
+ builder.note(node.start, `${defined.problem}; not rewritten`);
644
+ return;
645
+ }
646
+ const callee = node.type === "CallExpression" ? node.callee : node.tag;
647
+ builder.site(node, (r) => `${r.fn.msg}(${r.message(defined.key)})`, {
648
+ keys: [defined.key],
649
+ uses: ["msg"],
650
+ consume: calleeNodes(callee)
651
+ });
652
+ };
653
+ /** `i18n._(…)` / `i18n.t(…)` / `_(…)` at runtime. */
654
+ const runtimeCall = (node) => {
655
+ const callee = unwrap(node.callee);
656
+ let consume = [];
657
+ if (callee.type === "Identifier") {
658
+ const binding = resolve(callee.name, callee.start);
659
+ if (binding === null || !linguiUnderscore.has(binding)) return false;
660
+ consume = [callee];
661
+ } else {
662
+ const member = memberParts(callee);
663
+ if (member === null || member.property !== "_" && member.property !== "t") return false;
664
+ let root = unwrap(member.object);
665
+ while (root.type === "MemberExpression") root = unwrap(root.object);
666
+ const name = identifierName(root);
667
+ if (name === null) return false;
668
+ const binding = resolve(name, root.start);
669
+ if (!(runtimeI18n.has(name) || binding !== null && linguiInstances.has(binding))) return false;
670
+ if (unwrap(member.object).type === "Identifier") consume = [unwrap(member.object)];
671
+ }
672
+ const args = node.arguments;
673
+ const first = args[0];
674
+ if (first === void 0 || first.type === "SpreadElement") return false;
675
+ const values = args[1] ?? null;
676
+ const n = unwrap(first);
677
+ const id = stringValue(n);
678
+ let key = null;
679
+ if (id !== null) {
680
+ const defined = define(env, {
681
+ id,
682
+ message: null,
683
+ context: null,
684
+ comment: null
685
+ }, {
686
+ file,
687
+ line: lineOf(node.start)
688
+ }, null);
689
+ if ("problem" in defined) {
690
+ builder.note(node.start, `${defined.problem}; not rewritten`);
691
+ return true;
692
+ }
693
+ key = defined.key;
694
+ } else if (n.type === "ObjectExpression") {
695
+ const out = new MessageText();
696
+ const d = descriptor(n, out);
697
+ if (d === null || out.values.length > 0) {
698
+ builder.note(node.start, "a runtime descriptor vi18n migrate cannot read was not rewritten");
699
+ return true;
700
+ }
701
+ const defined = define(env, d, {
702
+ file,
703
+ line: lineOf(node.start)
704
+ }, null);
705
+ if ("problem" in defined) {
706
+ builder.note(node.start, `${defined.problem}; not rewritten`);
707
+ return true;
708
+ }
709
+ key = defined.key;
710
+ } else {
711
+ let root = n;
712
+ while (root.type === "MemberExpression") root = unwrap(root.object);
713
+ const name = identifierName(root);
714
+ if (!(name !== null && (messageTables.has(name) || relativeImports.has(name)) || isMsgMacro(n))) {
715
+ builder.note(node.start, "i18n._() with a message vi18n migrate cannot trace was not rewritten: pass a msg() descriptor to t()");
716
+ return true;
717
+ }
718
+ }
719
+ const resolvedKey = key;
720
+ builder.site(node, (r) => {
721
+ const message = resolvedKey === null ? r.src(first.start, first.end) : r.message(resolvedKey);
722
+ return `${r.fn.t}(${message}${values === null ? "" : `, ${r.src(values.start, values.end)}`})`;
723
+ }, {
724
+ keys: resolvedKey === null ? [] : [resolvedKey],
725
+ uses: ["t"],
726
+ consume
727
+ });
728
+ return true;
729
+ };
730
+ const isMsgMacro = (n) => {
731
+ const callee = n.type === "TaggedTemplateExpression" ? n.tag : n.type === "CallExpression" ? n.callee : null;
732
+ const name = callee === null ? null : identifierName(callee);
733
+ return name !== null && macroMsg.has(name);
734
+ };
735
+ const unwrapProvider = (element) => {
736
+ const opening = element.openingElement;
737
+ const closing = element.closingElement;
738
+ const children = element.children.filter((c) => !(c.type === "JSXText" && c.value.trim() === ""));
739
+ builder.consumeRange(opening);
740
+ builder.consume(jsxNameNodes(element));
741
+ const nested = inJsxChildren(info, element);
742
+ builder.replace(element, (r) => {
743
+ if (closing === null) return nested ? "" : "null";
744
+ if (nested) return r.src(opening.end, closing.start).trim();
745
+ if (children.length === 1 && children[0].type === "JSXElement") return r.src(children[0].start, children[0].end);
746
+ return `<>${r.src(opening.end, closing.start)}</>`;
747
+ });
748
+ };
749
+ const visit = (node) => {
750
+ if (node.type === "TaggedTemplateExpression") {
751
+ const tag = node.tag;
752
+ const name = identifierName(tag);
753
+ if (isMacroT(tag)) {
754
+ const out = new MessageText();
755
+ template(node.quasi, out);
756
+ tSite(node, out.built(), {
757
+ id: null,
758
+ context: null,
759
+ comment: null
760
+ }, calleeNodes(tag));
761
+ } else if (name !== null && macroMsg.has(name)) {
762
+ const out = new MessageText();
763
+ template(node.quasi, out);
764
+ msgSite(node, out, {
765
+ id: null,
766
+ context: null,
767
+ comment: null
768
+ });
769
+ }
770
+ } else if (node.type === "CallExpression") {
771
+ const callee = node.callee;
772
+ const name = identifierName(callee);
773
+ const arg = node.arguments[0];
774
+ if (isMacroT(callee) && unwrap(callee).type === "Identifier" && arg !== void 0 && unwrap(arg).type === "ObjectExpression") {
775
+ const out = new MessageText();
776
+ const d = descriptor(unwrap(arg), out);
777
+ if (d === null) builder.note(node.start, "t({…}) with a computed descriptor was not rewritten");
778
+ else tSite(node, {
779
+ ...out.built(),
780
+ message: d.message ?? ""
781
+ }, d, calleeNodes(callee));
782
+ } else if (name !== null && macroMsg.has(name) && arg !== void 0) {
783
+ const out = new MessageText();
784
+ const d = unwrap(arg).type === "ObjectExpression" ? descriptor(unwrap(arg), out) : null;
785
+ if (d === null) builder.note(node.start, `${name}() with a computed descriptor was not rewritten`);
786
+ else msgSite(node, out, d);
787
+ } else if (name !== null && choiceMacros.has(name) && !absorbed.has(node)) {
788
+ const out = new MessageText();
789
+ choice(node, choiceMacros.get(name), out);
790
+ tSite(node, out.built(), {
791
+ id: null,
792
+ context: null,
793
+ comment: null
794
+ }, calleeNodes(callee));
795
+ } else if (!runtimeCall(node)) {
796
+ const member = memberParts(callee);
797
+ if (member !== null && (member.property === "activate" || member.property === "load" || member.property === "loadAndActivate")) {
798
+ const object = identifierName(member.object);
799
+ if (object !== null && (runtimeI18n.has(object) || /^i18n$/.test(object))) builder.note(node.start, member.property === "activate" ? "i18n.activate(locale): use setLocale(locale) from @menapo/vi18n" : `i18n.${member.property}(): vi18n loads catalogs itself; delete this once nothing uses Lingui`);
800
+ } else if (name !== null && imported("setupI18n").has(name)) builder.note(node.start, "setupI18n(): use createTranslator(locale) or withLocale() from @menapo/vi18n/server");
801
+ }
802
+ } else if (node.type === "JSXElement") {
803
+ const name = jsxName(node);
804
+ if (transMacro.has(name)) transElement(node);
805
+ else if (choiceComponents.has(name) && !absorbed.has(node)) choiceElement(node, choiceComponents.get(name));
806
+ else if (providers.has(name)) unwrapProvider(node);
807
+ else if (imported("Trans", false).has(name)) builder.note(node.start, "<Trans id> from @lingui/react (the runtime component) was not rewritten: use t() or rich() with the message");
808
+ }
809
+ for (const [, child] of childEntries(node)) visit(child);
810
+ };
811
+ visit(program);
812
+ return builder.finish();
813
+ };
814
+ return {
815
+ catalogExtensions: [".po", ".json"],
816
+ catalogsRequired: false,
817
+ extensions: MODULE_EXTENSIONS,
818
+ mentions: (text) => text.includes("@lingui/"),
819
+ load(files, env) {
820
+ sourceLocale = env.config.sourceLocale;
821
+ const sorted = [...files].sort((a, b) => Number(b.locale === sourceLocale) - Number(a.locale === sourceLocale));
822
+ for (const file of sorted) readCatalog(file, env);
823
+ },
824
+ planFile: planModule,
825
+ unusedKeys() {
826
+ const out = [];
827
+ for (const [id, e] of catalogs.get(sourceLocale) ?? []) if (!found.has(e)) out.push(e.message !== null && e.message !== id ? `${id} (${e.message})` : id);
828
+ return out;
829
+ }
830
+ };
831
+ }
832
+ //#endregion
833
+ export { createLinguiAdapter };