@fluenti/cli 0.1.3 → 0.2.1

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 (37) hide show
  1. package/dist/cli.cjs +9 -9
  2. package/dist/cli.cjs.map +1 -1
  3. package/dist/cli.js +206 -209
  4. package/dist/cli.js.map +1 -1
  5. package/dist/compile-runner.d.ts +7 -0
  6. package/dist/compile-runner.d.ts.map +1 -0
  7. package/dist/compile.d.ts.map +1 -1
  8. package/dist/{compile-BJdEF9QX.js → config-loader-BgAoTfxH.js} +123 -93
  9. package/dist/config-loader-BgAoTfxH.js.map +1 -0
  10. package/dist/config-loader-D3RGkK_r.cjs +16 -0
  11. package/dist/config-loader-D3RGkK_r.cjs.map +1 -0
  12. package/dist/config-loader.d.ts +7 -0
  13. package/dist/config-loader.d.ts.map +1 -0
  14. package/dist/index.cjs +1 -1
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +29 -6
  19. package/dist/index.js.map +1 -1
  20. package/dist/translate.d.ts +2 -1
  21. package/dist/translate.d.ts.map +1 -1
  22. package/dist/{tsx-extractor-DZrY1LMS.js → tsx-extractor-CcFjsYI-.js} +1 -1
  23. package/dist/{tsx-extractor-DZrY1LMS.js.map → tsx-extractor-CcFjsYI-.js.map} +1 -1
  24. package/dist/tsx-extractor-D__s_cP8.cjs +2 -0
  25. package/dist/{tsx-extractor-LEAVCuX9.cjs.map → tsx-extractor-D__s_cP8.cjs.map} +1 -1
  26. package/dist/vue-extractor.cjs +3 -0
  27. package/dist/vue-extractor.cjs.map +1 -0
  28. package/dist/{vue-extractor-iUl6SUkv.js → vue-extractor.js} +60 -67
  29. package/dist/vue-extractor.js.map +1 -0
  30. package/package.json +27 -2
  31. package/dist/compile-BJdEF9QX.js.map +0 -1
  32. package/dist/compile-d4Q8bND5.cjs +0 -16
  33. package/dist/compile-d4Q8bND5.cjs.map +0 -1
  34. package/dist/tsx-extractor-LEAVCuX9.cjs +0 -2
  35. package/dist/vue-extractor-BlHc3vzt.cjs +0 -3
  36. package/dist/vue-extractor-BlHc3vzt.cjs.map +0 -1
  37. package/dist/vue-extractor-iUl6SUkv.js.map +0 -1
@@ -1,19 +1,12 @@
1
- import { t as e } from "./tsx-extractor-DZrY1LMS.js";
2
- import { parse as t } from "@vue/compiler-sfc";
3
- import { createMessageId as n } from "@fluenti/core/internal";
4
- //#region \0rolldown/runtime.js
5
- var r = Object.defineProperty, i = /* @__PURE__ */ ((e, t) => {
6
- let n = {};
7
- for (var i in e) r(n, i, {
8
- get: e[i],
9
- enumerable: !0
10
- });
11
- return t || r(n, Symbol.toStringTag, { value: "Module" }), n;
12
- })({ extractFromVue: () => g }), a = 1, o = 2, s = 7, c = 6;
13
- function l(e) {
14
- return e.filter((e) => e.type === o).map((e) => (e.content ?? "").trim()).join("");
1
+ import { t as e } from "./tsx-extractor-CcFjsYI-.js";
2
+ import { createMessageId as t } from "@fluenti/core/internal";
3
+ import { parse as n } from "@vue/compiler-sfc";
4
+ //#region src/vue-extractor.ts
5
+ var r = 1, i = 2, a = 7, o = 6;
6
+ function s(e) {
7
+ return e.filter((e) => e.type === i).map((e) => (e.content ?? "").trim()).join("");
15
8
  }
16
- function u(e, t) {
9
+ function c(e, t) {
17
10
  let n = e.split("|").map((e) => e.trim()), r = [
18
11
  "one",
19
12
  "other",
@@ -25,7 +18,7 @@ function u(e, t) {
25
18
  else for (let e = 0; e < n.length && e < r.length; e++) i.push(`${r[e]} {${n[e]}}`);
26
19
  return `{${t}, plural, ${i.join(" ")}}`;
27
20
  }
28
- function d(e) {
21
+ function l(e) {
29
22
  let t = e.count ?? "count", n = [
30
23
  "zero",
31
24
  "one",
@@ -40,61 +33,61 @@ function d(e) {
40
33
  }
41
34
  return r.length === 0 ? "" : `{${t}, plural, ${i ? `offset:${i} ` : ""}${r.join(" ")}}`;
42
35
  }
43
- function f(e, t, r) {
44
- if (e.type === a) {
45
- let i = e.props?.find((e) => e.type === s && m(e) === "t");
46
- if (i) {
47
- let a = new Set(["plural"]), o = (i.modifiers ?? []).map((e) => typeof e == "string" ? e : e.content), s = o.includes("plural"), c = o.filter((e) => !a.has(e)), d = i.arg?.content, f = d ? [d, ...c].join(".") : void 0, p = l(e.children ?? []);
48
- if (s) {
49
- let e = u(p, i.exp?.content ?? "count"), a = f ?? n(e);
50
- r.push({
36
+ function u(e, n, i) {
37
+ if (e.type === r) {
38
+ let r = e.props?.find((e) => e.type === a && f(e) === "t");
39
+ if (r) {
40
+ let a = new Set(["plural"]), o = (r.modifiers ?? []).map((e) => typeof e == "string" ? e : e.content), l = o.includes("plural"), u = o.filter((e) => !a.has(e)), d = r.arg?.content, f = d ? [d, ...u].join(".") : void 0, p = s(e.children ?? []);
41
+ if (l) {
42
+ let e = c(p, r.exp?.content ?? "count"), a = f ?? t(e);
43
+ i.push({
51
44
  id: a,
52
45
  message: e,
53
46
  origin: {
54
- file: t,
55
- line: i.loc.start.line,
56
- column: i.loc.start.column
47
+ file: n,
48
+ line: r.loc.start.line,
49
+ column: r.loc.start.column
57
50
  }
58
51
  });
59
52
  } else if (p) {
60
- let e = f ?? n(p);
61
- r.push({
53
+ let e = f ?? t(p);
54
+ i.push({
62
55
  id: e,
63
56
  message: p,
64
57
  origin: {
65
- file: t,
66
- line: i.loc.start.line,
67
- column: i.loc.start.column
58
+ file: n,
59
+ line: r.loc.start.line,
60
+ column: r.loc.start.column
68
61
  }
69
62
  });
70
63
  }
71
64
  }
72
65
  if (e.tag === "Trans") {
73
- let i = e.props?.find((e) => e.type === c && m(e) === "message"), a = e.props?.find((e) => e.type === c && m(e) === "id"), o = e.props?.find((e) => e.type === c && m(e) === "context"), s = e.props?.find((e) => e.type === c && m(e) === "comment"), l = o?.value?.content, u = s?.value?.content;
74
- if (i?.value) {
75
- let o = i.value.content, s = a?.value?.content ?? n(o, l);
76
- r.push({
66
+ let r = e.props?.find((e) => e.type === o && f(e) === "message"), a = e.props?.find((e) => e.type === o && f(e) === "id"), s = e.props?.find((e) => e.type === o && f(e) === "context"), c = e.props?.find((e) => e.type === o && f(e) === "comment"), l = s?.value?.content, u = c?.value?.content;
67
+ if (r?.value) {
68
+ let o = r.value.content, s = a?.value?.content ?? t(o, l);
69
+ i.push({
77
70
  id: s,
78
71
  message: o,
79
72
  ...l === void 0 ? {} : { context: l },
80
73
  ...u === void 0 ? {} : { comment: u },
81
74
  origin: {
82
- file: t,
75
+ file: n,
83
76
  line: e.loc.start.line,
84
77
  column: e.loc.start.column
85
78
  }
86
79
  });
87
80
  } else if (e.children && e.children.length > 0) {
88
- let i = p(e.children);
89
- if (i.message) {
90
- let o = a?.value?.content ?? n(i.message, l);
91
- r.push({
81
+ let r = d(e.children);
82
+ if (r.message) {
83
+ let o = a?.value?.content ?? t(r.message, l);
84
+ i.push({
92
85
  id: o,
93
- message: i.message,
86
+ message: r.message,
94
87
  ...l === void 0 ? {} : { context: l },
95
88
  ...u === void 0 ? {} : { comment: u },
96
89
  origin: {
97
- file: t,
90
+ file: n,
98
91
  line: e.loc.start.line,
99
92
  column: e.loc.start.column
100
93
  }
@@ -103,20 +96,20 @@ function f(e, t, r) {
103
96
  }
104
97
  }
105
98
  if (e.tag === "Plural") {
106
- let i = {}, a, o;
107
- for (let t of e.props ?? []) t.type === c && t.value && (i[m(t)] = t.value.content), t.type === s && m(t) === "bind" && t.arg?.content === "value" && t.exp && (a = t.exp.content), t.type === s && m(t) === "bind" && t.arg?.content === "offset" && t.exp && (o = t.exp.content);
108
- let l = a ?? i.count ?? "count", u = o ?? i.offset, f = d({
109
- ...i,
110
- count: l,
111
- ...u === void 0 ? {} : { offset: u }
99
+ let r = {}, s, c;
100
+ for (let t of e.props ?? []) t.type === o && t.value && (r[f(t)] = t.value.content), t.type === a && f(t) === "bind" && t.arg?.content === "value" && t.exp && (s = t.exp.content), t.type === a && f(t) === "bind" && t.arg?.content === "offset" && t.exp && (c = t.exp.content);
101
+ let u = s ?? r.count ?? "count", d = c ?? r.offset, p = l({
102
+ ...r,
103
+ count: u,
104
+ ...d === void 0 ? {} : { offset: d }
112
105
  });
113
- if (f) {
114
- let a = i.id ?? n(f);
115
- r.push({
106
+ if (p) {
107
+ let a = r.id ?? t(p);
108
+ i.push({
116
109
  id: a,
117
- message: f,
110
+ message: p,
118
111
  origin: {
119
- file: t,
112
+ file: n,
120
113
  line: e.loc.start.line,
121
114
  column: e.loc.start.column
122
115
  }
@@ -124,27 +117,27 @@ function f(e, t, r) {
124
117
  }
125
118
  }
126
119
  }
127
- if (e.children) for (let n of e.children) f(n, t, r);
120
+ if (e.children) for (let t of e.children) u(t, n, i);
128
121
  }
129
- function p(e) {
122
+ function d(e) {
130
123
  let t = 0, n = !1;
131
124
  return {
132
125
  message: e.map((e) => {
133
- if (e.type === o) return (e.content ?? "").trim() ? e.content ?? "" : "";
134
- if (e.type === a && e.tag) {
126
+ if (e.type === i) return (e.content ?? "").trim() ? e.content ?? "" : "";
127
+ if (e.type === r && e.tag) {
135
128
  n = !0;
136
129
  let r = t++;
137
- return `<${r}>${p(e.children ?? []).message}</${r}>`;
130
+ return `<${r}>${d(e.children ?? []).message}</${r}>`;
138
131
  }
139
132
  return "";
140
133
  }).join("").trim(),
141
134
  hasElements: n
142
135
  };
143
136
  }
144
- function m(e) {
137
+ function f(e) {
145
138
  return typeof e.name == "string" ? e.name : e.name.content;
146
139
  }
147
- function h(t, n) {
140
+ function p(t, n) {
148
141
  let r = [], i = /\{\{([\s\S]*?)\}\}/g, a;
149
142
  for (; (a = i.exec(t)) !== null;) {
150
143
  let i = a[1]?.trim();
@@ -162,9 +155,9 @@ function h(t, n) {
162
155
  }
163
156
  return r;
164
157
  }
165
- function g(n, r) {
166
- let i = [], { descriptor: a } = t(n, { filename: r });
167
- if (a.template?.ast && f(a.template.ast, r, i), a.template?.content) {
158
+ function m(t, r) {
159
+ let i = [], { descriptor: a } = n(t, { filename: r });
160
+ if (a.template?.ast && u(a.template.ast, r, i), a.template?.content) {
168
161
  let t = e(a.template.content, r), n = a.template.loc.start.line - 1, o = new Set(i.map((e) => e.id));
169
162
  for (let e of t) o.has(e.id) || i.push({
170
163
  ...e,
@@ -173,7 +166,7 @@ function g(n, r) {
173
166
  line: e.origin.line + n
174
167
  }
175
168
  });
176
- let s = h(a.template.content, r);
169
+ let s = p(a.template.content, r);
177
170
  for (let e of s) o.has(e.id) || i.push({
178
171
  ...e,
179
172
  origin: {
@@ -205,6 +198,6 @@ function g(n, r) {
205
198
  return i;
206
199
  }
207
200
  //#endregion
208
- export { i as n, g as t };
201
+ export { m as extractFromVue };
209
202
 
210
- //# sourceMappingURL=vue-extractor-iUl6SUkv.js.map
203
+ //# sourceMappingURL=vue-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vue-extractor.js","names":[],"sources":["../src/vue-extractor.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core'\nimport { parse as parseSFC } from '@vue/compiler-sfc'\nimport { createMessageId } from '@fluenti/core/internal'\nimport { extractFromTsx } from './tsx-extractor'\n\n// Vue template AST node types\nconst ELEMENT_NODE = 1\nconst TEXT_NODE = 2\nconst DIRECTIVE_PROP = 7\nconst ATTRIBUTE_PROP = 6\n\ninterface LocInfo {\n line: number\n column: number\n offset: number\n}\n\ninterface SourceLoc {\n start: LocInfo\n end: LocInfo\n source: string\n}\n\ninterface TemplateNode {\n type: number\n tag?: string\n tagType?: number\n props?: TemplateProp[]\n children?: TemplateNode[]\n content?: string\n loc: SourceLoc\n}\n\ninterface TemplateProp {\n type: number\n name: string | { content: string }\n rawName?: string\n arg?: { content: string; isStatic: boolean }\n exp?: { content: string }\n modifiers?: Array<{ content: string } | string>\n value?: { content: string }\n nameLoc?: SourceLoc\n loc: SourceLoc\n}\n\nfunction getTextContent(children: TemplateNode[]): string {\n return children\n .filter((c) => c.type === TEXT_NODE)\n .map((c) => (c.content ?? '').trim())\n .join('')\n}\n\nfunction buildPluralICUFromPipe(text: string, countVar: string): string {\n const forms = text.split('|').map((s) => s.trim())\n const categories = ['one', 'other', 'zero', 'few', 'many']\n const options: string[] = []\n\n if (forms.length === 2) {\n options.push(`one {${forms[0]}}`)\n options.push(`other {${forms[1]}}`)\n } else {\n for (let i = 0; i < forms.length && i < categories.length; i++) {\n options.push(`${categories[i]} {${forms[i]}}`)\n }\n }\n\n return `{${countVar}, plural, ${options.join(' ')}}`\n}\n\nfunction buildPluralICUFromProps(props: Record<string, string>): string {\n const countVar = props['count'] ?? 'count'\n const categories = ['zero', 'one', 'two', 'few', 'many', 'other']\n const options: string[] = []\n const offset = props['offset']\n\n for (const cat of categories) {\n if (props[cat] !== undefined) {\n const key = cat === 'zero' ? '=0' : cat\n options.push(`${key} {${props[cat]}}`)\n }\n }\n\n if (options.length === 0) return ''\n const offsetPrefix = offset ? `offset:${offset} ` : ''\n return `{${countVar}, plural, ${offsetPrefix}${options.join(' ')}}`\n}\n\nfunction walkTemplate(\n node: TemplateNode,\n filename: string,\n messages: ExtractedMessage[],\n): void {\n if (node.type === ELEMENT_NODE) {\n const vtDirective = node.props?.find(\n (p) => p.type === DIRECTIVE_PROP && getPropName(p) === 't',\n )\n\n if (vtDirective) {\n const RESERVED_MODIFIERS = new Set(['plural'])\n const modifiers = (vtDirective.modifiers ?? []).map(\n (m: string | { content: string }) => (typeof m === 'string' ? m : m.content),\n )\n const isPlural = modifiers.includes('plural')\n // Reconstruct dotted ID: v-t:checkout.title → arg=\"checkout\", modifier=\"title\" → \"checkout.title\"\n // Non-reserved modifiers are treated as ID path segments\n const idSegments = modifiers.filter((m: string) => !RESERVED_MODIFIERS.has(m))\n const argContent = vtDirective.arg?.content\n const explicitId = argContent\n ? [argContent, ...idSegments].join('.')\n : undefined\n const textContent = getTextContent(node.children ?? [])\n\n if (isPlural) {\n const countVar = vtDirective.exp?.content ?? 'count'\n const message = buildPluralICUFromPipe(textContent, countVar)\n const id = explicitId ?? createMessageId(message)\n messages.push({\n id,\n message,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n } else if (textContent) {\n const id = explicitId ?? createMessageId(textContent)\n messages.push({\n id,\n message: textContent,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n }\n }\n\n if (node.tag === 'Trans') {\n const messageProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'message',\n )\n const idProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'id',\n )\n const contextProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'context',\n )\n const commentProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'comment',\n )\n const context = contextProp?.value?.content\n const comment = commentProp?.value?.content\n\n if (messageProp?.value) {\n // Old API: <Trans message=\"...\" />\n const message = messageProp.value.content\n const id = idProp?.value?.content ?? createMessageId(message, context)\n messages.push({\n id,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n } else if (node.children && node.children.length > 0) {\n // New API: <Trans>content with <a>rich text</a></Trans>\n const richText = extractRichTextFromTemplateChildren(node.children)\n if (richText.message) {\n const id = idProp?.value?.content ?? createMessageId(richText.message, context)\n messages.push({\n id,\n message: richText.message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.tag === 'Plural') {\n const propsMap: Record<string, string> = {}\n let valueExpr: string | undefined\n let offsetExpr: string | undefined\n for (const prop of node.props ?? []) {\n if (prop.type === ATTRIBUTE_PROP && prop.value) {\n propsMap[getPropName(prop)] = prop.value.content\n }\n // Handle :value=\"expr\" binding (directive prop)\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'value' && prop.exp) {\n valueExpr = prop.exp.content\n }\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'offset' && prop.exp) {\n offsetExpr = prop.exp.content\n }\n }\n\n // Use :value binding expression as count variable, fall back to 'count' static prop\n const countVar = valueExpr ?? propsMap['count'] ?? 'count'\n const offset = offsetExpr ?? propsMap['offset']\n const pluralMessage = buildPluralICUFromProps({\n ...propsMap,\n count: countVar,\n ...(offset !== undefined ? { offset } : {}),\n })\n if (pluralMessage) {\n const id = propsMap['id'] ?? createMessageId(pluralMessage)\n messages.push({\n id,\n message: pluralMessage,\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, filename, messages)\n }\n }\n}\n\nfunction extractRichTextFromTemplateChildren(\n children: TemplateNode[],\n): { message: string; hasElements: boolean } {\n let elementIndex = 0\n let hasElements = false\n\n const parts = children.map((child) => {\n if (child.type === TEXT_NODE) {\n return (child.content ?? '').trim() ? child.content ?? '' : ''\n }\n if (child.type === ELEMENT_NODE && child.tag) {\n hasElements = true\n const idx = elementIndex++\n const innerText = extractRichTextFromTemplateChildren(child.children ?? []).message\n return `<${idx}>${innerText}</${idx}>`\n }\n return ''\n })\n\n return {\n message: parts.join('').trim(),\n hasElements,\n }\n}\n\nfunction getPropName(prop: TemplateProp): string {\n if (typeof prop.name === 'string') return prop.name\n return prop.name.content\n}\n\nfunction extractTemplateInterpolations(\n content: string,\n filename: string,\n): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n const interpolationRegex = /\\{\\{([\\s\\S]*?)\\}\\}/g\n let match: RegExpExecArray | null\n\n while ((match = interpolationRegex.exec(content)) !== null) {\n const expression = match[1]?.trim()\n if (!expression) continue\n\n const extracted = extractFromTsx(expression, filename)\n if (extracted.length === 0) continue\n\n const lineOffset = content.slice(0, match.index).split('\\n').length - 1\n for (const msg of extracted) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n\n/** Extract messages from Vue SFC files */\nexport function extractFromVue(code: string, filename: string): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n\n const { descriptor } = parseSFC(code, { filename })\n\n if (descriptor.template?.ast) {\n walkTemplate(descriptor.template.ast as unknown as TemplateNode, filename, messages)\n }\n\n // Also extract t() function calls from raw template source\n // (picks up t('source text') in template expressions like {{ t('...') }})\n if (descriptor.template?.content) {\n const templateMessages = extractFromTsx(descriptor.template.content, filename)\n const templateLoc = descriptor.template.loc\n const lineOffset = templateLoc.start.line - 1\n const existingIds = new Set(messages.map((m) => m.id))\n for (const msg of templateMessages) {\n if (!existingIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n const interpolationMessages = extractTemplateInterpolations(descriptor.template.content, filename)\n for (const msg of interpolationMessages) {\n if (!existingIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n }\n\n if (descriptor.scriptSetup?.content) {\n const scriptMessages = extractFromTsx(descriptor.scriptSetup.content, filename)\n const scriptLoc = descriptor.scriptSetup.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n if (descriptor.script?.content) {\n const scriptMessages = extractFromTsx(descriptor.script.content, filename)\n const scriptLoc = descriptor.script.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluenti/cli",
3
- "version": "0.1.3",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Fluenti CLI — message extraction from Vue SFC & TSX, PO/JSON catalog compilation",
6
6
  "homepage": "https://fluenti.dev",
@@ -43,6 +43,23 @@
43
43
  "types": "./dist/index.d.ts",
44
44
  "default": "./dist/index.cjs"
45
45
  }
46
+ },
47
+ "./vue-extractor": {
48
+ "import": {
49
+ "types": "./dist/vue-extractor.d.ts",
50
+ "default": "./dist/vue-extractor.js"
51
+ },
52
+ "require": {
53
+ "types": "./dist/vue-extractor.d.ts",
54
+ "default": "./dist/vue-extractor.cjs"
55
+ }
56
+ }
57
+ },
58
+ "typesVersions": {
59
+ "*": {
60
+ "vue-extractor": [
61
+ "./dist/vue-extractor.d.ts"
62
+ ]
46
63
  }
47
64
  },
48
65
  "files": [
@@ -54,7 +71,15 @@
54
71
  "fast-glob": "^3",
55
72
  "gettext-parser": "^9",
56
73
  "jiti": "^2",
57
- "@fluenti/core": "0.1.3"
74
+ "@fluenti/core": "0.2.1"
75
+ },
76
+ "peerDependencies": {
77
+ "@vue/compiler-sfc": "^3.5"
78
+ },
79
+ "peerDependenciesMeta": {
80
+ "@vue/compiler-sfc": {
81
+ "optional": true
82
+ }
58
83
  },
59
84
  "devDependencies": {
60
85
  "typescript": "^5.9",
@@ -1 +0,0 @@
1
- {"version":3,"file":"compile-BJdEF9QX.js","names":[],"sources":["../src/catalog.ts","../src/json-format.ts","../src/po-format.ts","../src/compile.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core'\n\nexport interface CatalogEntry {\n message?: string | undefined\n context?: string | undefined\n comment?: string | undefined\n translation?: string | undefined\n origin?: string | string[] | undefined\n obsolete?: boolean | undefined\n fuzzy?: boolean | undefined\n}\n\nexport type CatalogData = Record<string, CatalogEntry>\n\nexport interface UpdateResult {\n added: number\n unchanged: number\n obsolete: number\n}\n\nexport interface UpdateCatalogOptions {\n stripFuzzy?: boolean\n}\n\n/** Update catalog with newly extracted messages */\nexport function updateCatalog(\n existing: CatalogData,\n extracted: ExtractedMessage[],\n options?: UpdateCatalogOptions,\n): { catalog: CatalogData; result: UpdateResult } {\n const extractedIds = new Set(extracted.map((m) => m.id))\n const consumedCarryForwardIds = new Set<string>()\n const catalog: CatalogData = {}\n let added = 0\n let unchanged = 0\n let obsolete = 0\n\n for (const msg of extracted) {\n const existingEntry = existing[msg.id]\n const carried = existingEntry\n ? undefined\n : findCarryForwardEntry(existing, msg, consumedCarryForwardIds)\n const origin = `${msg.origin.file}:${msg.origin.line}`\n const baseEntry = existingEntry ?? carried?.entry\n\n if (carried) {\n consumedCarryForwardIds.add(carried.id)\n }\n\n if (baseEntry) {\n catalog[msg.id] = {\n ...baseEntry,\n message: msg.message ?? baseEntry.message,\n context: msg.context,\n comment: msg.comment,\n origin,\n obsolete: false,\n }\n unchanged++\n } else if (catalog[msg.id]) {\n // Same ID already seen in this extraction batch — merge origin\n const existing = catalog[msg.id]!\n catalog[msg.id] = {\n ...existing,\n origin: mergeOrigins(existing.origin, origin),\n }\n } else {\n catalog[msg.id] = {\n message: msg.message,\n context: msg.context,\n comment: msg.comment,\n origin,\n }\n added++\n }\n\n if (options?.stripFuzzy) {\n const { fuzzy: _fuzzy, ...rest } = catalog[msg.id]!\n catalog[msg.id] = rest\n }\n }\n\n for (const [id, entry] of Object.entries(existing)) {\n if (!extractedIds.has(id)) {\n const { fuzzy: _fuzzy, ...rest } = entry\n const obsoleteEntry = options?.stripFuzzy\n ? { ...rest, obsolete: true }\n : { ...entry, obsolete: true }\n catalog[id] = obsoleteEntry\n obsolete++\n }\n }\n\n return { catalog, result: { added, unchanged, obsolete } }\n}\n\nfunction mergeOrigins(\n existing: string | string[] | undefined,\n newOrigin: string,\n): string | string[] {\n if (!existing) return newOrigin\n const existingArray = Array.isArray(existing) ? existing : [existing]\n const merged = [...new Set([...existingArray, newOrigin])]\n return merged.length === 1 ? merged[0]! : merged\n}\n\nfunction findCarryForwardEntry(\n existing: CatalogData,\n extracted: ExtractedMessage,\n consumedCarryForwardIds: Set<string>,\n): { id: string; entry: CatalogEntry } | undefined {\n if (!extracted.context) {\n return undefined\n }\n\n const extractedOrigin = `${extracted.origin.file}:${extracted.origin.line}`\n for (const [id, entry] of Object.entries(existing)) {\n if (consumedCarryForwardIds.has(id)) continue\n if (entry.context !== undefined) continue\n if (entry.message !== extracted.message) continue\n if (!sameOrigin(entry.origin, extractedOrigin)) continue\n return { id, entry }\n }\n\n return undefined\n}\n\nfunction sameOrigin(previous: string | string[] | undefined, next: string): boolean {\n if (!previous) return false\n const origins = Array.isArray(previous) ? previous : [previous]\n return origins.some((o) => o === next || originFile(o) === originFile(next))\n}\n\nfunction originFile(origin: string): string {\n const match = origin.match(/^(.*):\\d+$/)\n return match?.[1] ?? origin\n}\n","import type { CatalogData } from './catalog'\n\n/** Read a JSON catalog file */\nexport function readJsonCatalog(content: string): CatalogData {\n const raw = JSON.parse(content) as Record<string, unknown>\n const catalog: CatalogData = {}\n\n for (const [id, entry] of Object.entries(raw)) {\n if (typeof entry === 'object' && entry !== null) {\n const e = entry as Record<string, unknown>\n catalog[id] = {\n message: typeof e['message'] === 'string' ? e['message'] : undefined,\n context: typeof e['context'] === 'string' ? e['context'] : undefined,\n comment: typeof e['comment'] === 'string' ? e['comment'] : undefined,\n translation: typeof e['translation'] === 'string' ? e['translation'] : undefined,\n origin: typeof e['origin'] === 'string'\n ? e['origin']\n : Array.isArray(e['origin']) && (e['origin'] as unknown[]).every((v) => typeof v === 'string')\n ? (e['origin'] as string[])\n : undefined,\n obsolete: typeof e['obsolete'] === 'boolean' ? e['obsolete'] : undefined,\n fuzzy: typeof e['fuzzy'] === 'boolean' ? e['fuzzy'] : undefined,\n }\n }\n }\n\n return catalog\n}\n\n/** Write a catalog to JSON format */\nexport function writeJsonCatalog(catalog: CatalogData): string {\n const output: Record<string, Record<string, unknown>> = {}\n\n for (const [id, entry] of Object.entries(catalog)) {\n const obj: Record<string, unknown> = {}\n if (entry.message !== undefined) obj['message'] = entry.message\n if (entry.context !== undefined) obj['context'] = entry.context\n if (entry.comment !== undefined) obj['comment'] = entry.comment\n if (entry.translation !== undefined) obj['translation'] = entry.translation\n if (entry.origin !== undefined) obj['origin'] = entry.origin\n if (entry.obsolete) obj['obsolete'] = true\n if (entry.fuzzy) obj['fuzzy'] = true\n output[id] = obj\n }\n\n return JSON.stringify(output, null, 2) + '\\n'\n}\n","import type { CatalogData } from './catalog'\nimport { hashMessage } from '@fluenti/core'\nimport * as gettextParser from 'gettext-parser'\n\nconst CUSTOM_ID_MARKER = 'fluenti-id:'\n\ninterface POTranslation {\n msgid: string\n msgctxt?: string\n msgstr: string[]\n comments?: {\n reference?: string\n extracted?: string\n flag?: string\n translator?: string\n previous?: string\n }\n}\n\ninterface POData {\n headers?: Record<string, string>\n translations: Record<string, Record<string, POTranslation>>\n}\n\ninterface ParsedExtractedComment {\n comment?: string\n customId?: string\n sourceMessage?: string\n}\n\n/** Read a PO catalog file */\nexport function readPoCatalog(content: string): CatalogData {\n const po = gettextParser.po.parse(content) as POData\n const catalog: CatalogData = {}\n const translations = po.translations ?? {}\n\n for (const [contextKey, entries] of Object.entries(translations)) {\n for (const [msgid, entry] of Object.entries(entries)) {\n if (!msgid) continue\n\n const context = contextKey || entry.msgctxt || undefined\n const translation = entry.msgstr?.[0] ?? undefined\n const rawReference = entry.comments?.reference ?? undefined\n const origin = rawReference?.includes('\\n')\n ? rawReference.split('\\n').map((r: string) => r.trim()).filter(Boolean)\n : rawReference?.includes(' ')\n ? rawReference.split(/\\s+/).filter(Boolean)\n : rawReference\n const normalizedOrigin = Array.isArray(origin) && origin.length === 1 ? origin[0] : origin\n const isFuzzy = entry.comments?.flag?.includes('fuzzy') ?? false\n const { comment, customId, sourceMessage } = parseExtractedComment(entry.comments?.extracted)\n const resolvedSourceMessage = sourceMessage\n && hashMessage(sourceMessage, context) === msgid\n ? sourceMessage\n : undefined\n const id = customId\n ?? (resolvedSourceMessage ? msgid : hashMessage(msgid, context))\n\n catalog[id] = {\n message: resolvedSourceMessage ?? msgid,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n ...(translation ? { translation } : {}),\n ...(normalizedOrigin !== undefined ? { origin: normalizedOrigin } : {}),\n ...(isFuzzy ? { fuzzy: true } : {}),\n }\n }\n }\n\n return catalog\n}\n\n/** Write a catalog to PO format */\nexport function writePoCatalog(catalog: CatalogData): string {\n const translations: POData['translations'] = {\n '': {\n '': {\n msgid: '',\n msgstr: ['Content-Type: text/plain; charset=UTF-8\\n'],\n },\n },\n }\n\n for (const [id, entry] of Object.entries(catalog)) {\n const poEntry: POTranslation = {\n msgid: entry.message ?? id,\n ...(entry.context !== undefined ? { msgctxt: entry.context } : {}),\n msgstr: [entry.translation ?? ''],\n }\n\n const comments: POTranslation['comments'] = {}\n if (entry.origin) {\n comments.reference = Array.isArray(entry.origin)\n ? entry.origin.join('\\n')\n : entry.origin\n }\n const extractedComment = buildExtractedComment(id, entry.message ?? id, entry.context, entry.comment)\n if (extractedComment) {\n comments.extracted = extractedComment\n }\n if (entry.fuzzy) {\n comments.flag = 'fuzzy'\n }\n if (comments.reference || comments.extracted || comments.flag) {\n poEntry.comments = comments\n }\n\n const contextKey = entry.context ?? ''\n translations[contextKey] ??= {}\n translations[contextKey][poEntry.msgid] = poEntry\n }\n\n const poData: POData = {\n headers: {\n 'Content-Type': 'text/plain; charset=UTF-8',\n },\n translations,\n }\n\n const buffer = gettextParser.po.compile(poData as Parameters<typeof gettextParser.po.compile>[0])\n return buffer.toString()\n}\n\nfunction parseExtractedComment(\n extracted: string | undefined,\n): ParsedExtractedComment {\n if (!extracted) {\n return {}\n }\n\n const lines = extracted.split('\\n').map((line) => line.trim()).filter(Boolean)\n let customId: string | undefined\n let sourceMessage: string | undefined\n const commentLines: string[] = []\n\n for (const line of lines) {\n if (line.startsWith(CUSTOM_ID_MARKER)) {\n customId = line.slice(CUSTOM_ID_MARKER.length).trim() || undefined\n continue\n }\n if (line.startsWith('msg`') && line.endsWith('`')) {\n sourceMessage = line.slice(4, -1)\n continue\n }\n if (line.startsWith('Trans: ')) {\n sourceMessage = normalizeRichTextComment(line.slice('Trans: '.length))\n continue\n }\n commentLines.push(line)\n }\n\n return {\n ...(commentLines.length > 0 ? { comment: commentLines.join('\\n') } : {}),\n ...(customId ? { customId } : {}),\n ...(sourceMessage ? { sourceMessage } : {}),\n }\n}\n\nfunction normalizeRichTextComment(comment: string): string {\n let stack: Array<{ tag: string; index: number }> = []\n let nextIndex = 0\n\n return comment.replace(/<\\/?([a-zA-Z][\\w-]*)>/g, (match, rawTag: string) => {\n const tag = rawTag\n if (match.startsWith('</')) {\n for (let index = stack.length - 1; index >= 0; index--) {\n const entry = stack[index]\n if (entry?.tag !== tag) continue\n stack = stack.filter((_, i) => i !== index)\n return `</${entry.index}>`\n }\n return match\n }\n\n const index = nextIndex++\n stack.push({ tag, index })\n return `<${index}>`\n })\n}\n\nfunction buildExtractedComment(\n id: string,\n message: string,\n context: string | undefined,\n comment: string | undefined,\n): string | undefined {\n const lines: string[] = []\n\n if (comment) {\n lines.push(comment)\n }\n\n if (id !== hashMessage(message, context)) {\n lines.push(`${CUSTOM_ID_MARKER} ${id}`)\n }\n\n return lines.length > 0 ? lines.join('\\n') : undefined\n}\n","import type { CatalogData } from './catalog'\nimport { hashMessage } from '@fluenti/core'\nimport { parse } from '@fluenti/core'\nimport type { ASTNode, PluralNode, SelectNode, VariableNode, FunctionNode } from '@fluenti/core'\n\nconst ICU_VAR_REGEX = /\\{(\\w+)\\}/g\nconst ICU_VAR_TEST = /\\{(\\w+)\\}/\n\nfunction hasVariables(message: string): boolean {\n return ICU_VAR_TEST.test(message)\n}\n\n\nfunction escapeStringLiteral(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"\\\\'\")\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n}\n\nfunction escapeTemplateLiteral(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/`/g, '\\\\`')\n .replace(/\\$\\{/g, '\\\\${')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n}\n\nfunction messageToTemplateString(message: string): string {\n return message.replace(ICU_VAR_REGEX, (_match, name: string) => `\\${v.${name}}`)\n}\n\n\n// ─── ICU → JS code generation for split mode ───────────────────────────────\n\nconst ICU_PLURAL_SELECT_REGEX = /\\{(\\w+),\\s*(plural|select|selectordinal)\\s*,/\n\n/** Check if message contains ICU plural/select syntax */\nfunction hasIcuPluralOrSelect(message: string): boolean {\n return ICU_PLURAL_SELECT_REGEX.test(message)\n}\n\n/**\n * Compile an ICU AST node array into a JS expression string.\n * Used for generating static code (not runtime evaluation).\n */\nfunction astToJsExpression(nodes: ASTNode[], locale: string): string {\n if (nodes.length === 0) return \"''\"\n\n const parts = nodes.map((node) => astNodeToJs(node, locale))\n\n if (parts.length === 1) return parts[0]!\n return parts.join(' + ')\n}\n\nfunction astNodeToJs(node: ASTNode, locale: string): string {\n switch (node.type) {\n case 'text':\n return `'${escapeStringLiteral(node.value)}'`\n\n case 'variable':\n if (node.name === '#') return 'String(__c)'\n return `String(v.${node.name} ?? '{${node.name}}')`\n\n case 'plural':\n return pluralToJs(node as PluralNode, locale)\n\n case 'select':\n return selectToJs(node as SelectNode, locale)\n\n case 'function':\n return `String(v.${node.variable} ?? '')`\n }\n}\n\nfunction pluralToJs(node: PluralNode, locale: string): string {\n const offset = node.offset ?? 0\n const countExpr = offset ? `(v.${node.variable} - ${offset})` : `v.${node.variable}`\n\n const lines: string[] = []\n lines.push(`((c) => { const __c = c; `)\n\n // Exact matches first\n const exactKeys = Object.keys(node.options).filter((k) => k.startsWith('='))\n if (exactKeys.length > 0) {\n for (const key of exactKeys) {\n const num = key.slice(1)\n const body = astToJsExpression(node.options[key]!, locale)\n lines.push(`if (c === ${num}) return ${body}; `)\n }\n }\n\n // CLDR categories via Intl.PluralRules\n const cldrKeys = Object.keys(node.options).filter((k) => !k.startsWith('='))\n if (cldrKeys.length > 1 || (cldrKeys.length === 1 && cldrKeys[0] !== 'other')) {\n lines.push(`const __cat = new Intl.PluralRules('${escapeStringLiteral(locale)}').select(c); `)\n for (const key of cldrKeys) {\n if (key === 'other') continue\n const body = astToJsExpression(node.options[key]!, locale)\n lines.push(`if (__cat === '${key}') return ${body}; `)\n }\n }\n\n // Fallback to 'other'\n const otherBody = node.options['other']\n ? astToJsExpression(node.options['other'], locale)\n : \"''\"\n lines.push(`return ${otherBody}; `)\n lines.push(`})(${countExpr})`)\n\n return lines.join('')\n}\n\nfunction selectToJs(node: SelectNode, locale: string): string {\n const lines: string[] = []\n lines.push(`((s) => { `)\n\n const keys = Object.keys(node.options).filter((k) => k !== 'other')\n for (const key of keys) {\n const body = astToJsExpression(node.options[key]!, locale)\n lines.push(`if (s === '${escapeStringLiteral(key)}') return ${body}; `)\n }\n\n const otherBody = node.options['other']\n ? astToJsExpression(node.options['other'], locale)\n : \"''\"\n lines.push(`return ${otherBody}; `)\n lines.push(`})(String(v.${node.variable} ?? ''))`)\n\n return lines.join('')\n}\n\n/**\n * Compile a catalog to ES module with tree-shakeable named exports.\n * Each message becomes a `/* @__PURE__ *​/` annotated named export.\n * A default export maps message IDs to their compiled values for runtime lookup.\n */\n/** Catalog format version. Bump when the compiled output format changes. */\nexport const CATALOG_VERSION = 1\n\nexport interface CompileStats {\n compiled: number\n missing: string[]\n}\n\nexport interface CompileOptions {\n skipFuzzy?: boolean\n}\n\nexport function compileCatalog(\n catalog: CatalogData,\n locale: string,\n allIds: string[],\n sourceLocale?: string,\n options?: CompileOptions,\n): { code: string; stats: CompileStats } {\n const lines: string[] = []\n lines.push(`// @fluenti/compiled v${CATALOG_VERSION}`)\n const exportNames: Array<{ id: string; exportName: string }> = []\n let compiled = 0\n const missing: string[] = []\n\n const hashToId = new Map<string, string>()\n\n for (const id of allIds) {\n const hash = hashMessage(id)\n\n const existingId = hashToId.get(hash)\n if (existingId !== undefined && existingId !== id) {\n throw new Error(\n `Hash collision detected: messages \"${existingId}\" and \"${id}\" produce the same hash \"${hash}\"`,\n )\n }\n hashToId.set(hash, id)\n\n const exportName = `_${hash}`\n const entry = catalog[id]\n const translated = resolveCompiledMessage(entry, id, locale, sourceLocale, options?.skipFuzzy)\n\n if (translated === undefined) {\n lines.push(`export const ${exportName} = undefined`)\n missing.push(id)\n } else if (hasIcuPluralOrSelect(translated)) {\n // Parse ICU and compile to JS\n const ast = parse(translated)\n const jsExpr = astToJsExpression(ast, locale)\n lines.push(`export const ${exportName} = (v) => ${jsExpr}`)\n compiled++\n } else if (hasVariables(translated)) {\n const templateStr = messageToTemplateString(escapeTemplateLiteral(translated))\n lines.push(`export const ${exportName} = (v) => \\`${templateStr}\\``)\n compiled++\n } else {\n lines.push(`export const ${exportName} = '${escapeStringLiteral(translated)}'`)\n compiled++\n }\n\n exportNames.push({ id, exportName })\n }\n\n if (exportNames.length === 0) {\n return {\n code: `// @fluenti/compiled v${CATALOG_VERSION}\\n// empty catalog\\nexport default {}\\n`,\n stats: { compiled: 0, missing: [] },\n }\n }\n\n // Default export maps message IDs → compiled values for runtime lookup\n lines.push('')\n lines.push('export default {')\n for (const { id, exportName } of exportNames) {\n lines.push(` '${escapeStringLiteral(id)}': ${exportName},`)\n }\n lines.push('}')\n lines.push('')\n\n return { code: lines.join('\\n'), stats: { compiled, missing } }\n}\n\nfunction resolveCompiledMessage(\n entry: CatalogData[string] | undefined,\n id: string,\n locale: string,\n sourceLocale: string | undefined,\n skipFuzzy?: boolean,\n): string | undefined {\n const effectiveSourceLocale = sourceLocale ?? locale\n\n if (!entry) {\n return undefined\n }\n\n if (skipFuzzy && entry.fuzzy) {\n return undefined\n }\n\n if (entry.translation !== undefined && entry.translation.length > 0) {\n return entry.translation\n }\n\n if (locale === effectiveSourceLocale) {\n return entry.message ?? id\n }\n\n return undefined\n}\n\n/**\n * Generate the index module that exports locale list and lazy loaders.\n */\nexport function compileIndex(locales: string[], _catalogDir: string): string {\n const lines: string[] = []\n lines.push(`export const locales = ${JSON.stringify(locales)}`)\n lines.push('')\n lines.push('export const loaders = {')\n for (const locale of locales) {\n lines.push(` '${escapeStringLiteral(locale)}': () => import('./${escapeStringLiteral(locale)}.js'),`)\n }\n lines.push('}')\n lines.push('')\n return lines.join('\\n')\n}\n\n/**\n * Collect the union of all message IDs across all locale catalogs.\n * Ensures every locale file exports the same names.\n */\nexport function collectAllIds(catalogs: Record<string, CatalogData>): string[] {\n const idSet = new Set<string>()\n for (const catalog of Object.values(catalogs)) {\n for (const [id, entry] of Object.entries(catalog)) {\n if (!entry.obsolete) {\n idSet.add(id)\n }\n }\n }\n return [...idSet].sort()\n}\n\n// ─── Type-safe message ID generation ─────────────────────────────────────────\n\nexport interface MessageVariable {\n name: string\n type: string\n}\n\n/**\n * Extract variable names and their TypeScript types from an ICU message string.\n */\nexport function extractMessageVariables(message: string): MessageVariable[] {\n const ast = parse(message)\n const vars = new Map<string, string>()\n collectVariablesFromNodes(ast, vars)\n return [...vars.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, type]) => ({ name, type }))\n}\n\nfunction collectVariablesFromNodes(nodes: ASTNode[], vars: Map<string, string>): void {\n for (const node of nodes) {\n switch (node.type) {\n case 'variable':\n if (node.name !== '#' && !vars.has(node.name)) {\n vars.set((node as VariableNode).name, 'string | number')\n }\n break\n case 'plural': {\n const pn = node as PluralNode\n // Plural variable is always a number\n vars.set(pn.variable, 'number')\n // Recurse into plural option bodies\n for (const optionNodes of Object.values(pn.options)) {\n collectVariablesFromNodes(optionNodes, vars)\n }\n break\n }\n case 'select': {\n const sn = node as SelectNode\n const keys = Object.keys(sn.options).filter((k) => k !== 'other')\n const hasOther = 'other' in sn.options\n const literalTypes = keys.map((k) => `'${k}'`).join(' | ')\n const selectType = hasOther\n ? (keys.length > 0 ? `${literalTypes} | string` : 'string')\n : (keys.length > 0 ? literalTypes : 'string')\n vars.set(sn.variable, selectType)\n // Recurse into select option bodies\n for (const optionNodes of Object.values(sn.options)) {\n collectVariablesFromNodes(optionNodes, vars)\n }\n break\n }\n case 'function':\n if (!vars.has((node as FunctionNode).variable)) {\n vars.set((node as FunctionNode).variable, 'string | number')\n }\n break\n case 'text':\n break\n }\n }\n}\n\n/**\n * Generate a TypeScript declaration file with MessageId union and MessageValues interface.\n */\nexport function compileTypeDeclaration(\n allIds: string[],\n catalogs: Record<string, CatalogData>,\n sourceLocale: string,\n): string {\n const lines: string[] = []\n lines.push('// Auto-generated by @fluenti/cli — do not edit')\n lines.push('')\n\n // MessageId union\n if (allIds.length === 0) {\n lines.push('export type MessageId = never')\n } else {\n lines.push('export type MessageId =')\n for (const id of allIds) {\n lines.push(` | '${escapeStringLiteral(id)}'`)\n }\n }\n\n lines.push('')\n\n // MessageValues interface\n lines.push('export interface MessageValues {')\n for (const id of allIds) {\n // Use source locale catalog to get the message for variable extraction\n const sourceCatalog = catalogs[sourceLocale]\n const entry = sourceCatalog?.[id]\n const message = entry?.message ?? id\n const vars = extractMessageVariables(message)\n\n const escapedId = escapeStringLiteral(id)\n if (vars.length === 0) {\n lines.push(` '${escapedId}': Record<string, never>`)\n } else {\n const fields = vars.map((v) => `${v.name}: ${v.type}`).join('; ')\n lines.push(` '${escapedId}': { ${fields} }`)\n }\n }\n lines.push('}')\n lines.push('')\n\n return lines.join('\\n')\n}\n"],"mappings":"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"}
@@ -1,16 +0,0 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let l=require(`@fluenti/core`),u=require(`gettext-parser`);u=c(u);function d(e,t,n){let r=new Set(t.map(e=>e.id)),i=new Set,a={},o=0,s=0,c=0;for(let r of t){let t=e[r.id],c=t?void 0:p(e,r,i),l=`${r.origin.file}:${r.origin.line}`,u=t??c?.entry;if(c&&i.add(c.id),u)a[r.id]={...u,message:r.message??u.message,context:r.context,comment:r.comment,origin:l,obsolete:!1},s++;else if(a[r.id]){let e=a[r.id];a[r.id]={...e,origin:f(e.origin,l)}}else a[r.id]={message:r.message,context:r.context,comment:r.comment,origin:l},o++;if(n?.stripFuzzy){let{fuzzy:e,...t}=a[r.id];a[r.id]=t}}for(let[t,i]of Object.entries(e))if(!r.has(t)){let{fuzzy:e,...r}=i;a[t]=n?.stripFuzzy?{...r,obsolete:!0}:{...i,obsolete:!0},c++}return{catalog:a,result:{added:o,unchanged:s,obsolete:c}}}function f(e,t){if(!e)return t;let n=Array.isArray(e)?e:[e],r=[...new Set([...n,t])];return r.length===1?r[0]:r}function p(e,t,n){if(!t.context)return;let r=`${t.origin.file}:${t.origin.line}`;for(let[i,a]of Object.entries(e))if(!n.has(i)&&a.context===void 0&&a.message===t.message&&m(a.origin,r))return{id:i,entry:a}}function m(e,t){return e?(Array.isArray(e)?e:[e]).some(e=>e===t||h(e)===h(t)):!1}function h(e){return e.match(/^(.*):\d+$/)?.[1]??e}function g(e){let t=JSON.parse(e),n={};for(let[e,r]of Object.entries(t))if(typeof r==`object`&&r){let t=r;n[e]={message:typeof t.message==`string`?t.message:void 0,context:typeof t.context==`string`?t.context:void 0,comment:typeof t.comment==`string`?t.comment:void 0,translation:typeof t.translation==`string`?t.translation:void 0,origin:typeof t.origin==`string`||Array.isArray(t.origin)&&t.origin.every(e=>typeof e==`string`)?t.origin:void 0,obsolete:typeof t.obsolete==`boolean`?t.obsolete:void 0,fuzzy:typeof t.fuzzy==`boolean`?t.fuzzy:void 0}}return n}function _(e){let t={};for(let[n,r]of Object.entries(e)){let e={};r.message!==void 0&&(e.message=r.message),r.context!==void 0&&(e.context=r.context),r.comment!==void 0&&(e.comment=r.comment),r.translation!==void 0&&(e.translation=r.translation),r.origin!==void 0&&(e.origin=r.origin),r.obsolete&&(e.obsolete=!0),r.fuzzy&&(e.fuzzy=!0),t[n]=e}return JSON.stringify(t,null,2)+`
2
- `}var v=`fluenti-id:`;function y(e){let t=u.po.parse(e),n={},r=t.translations??{};for(let[e,t]of Object.entries(r))for(let[r,i]of Object.entries(t)){if(!r)continue;let t=e||i.msgctxt||void 0,a=i.msgstr?.[0]??void 0,o=i.comments?.reference??void 0,s=o?.includes(`
3
- `)?o.split(`
4
- `).map(e=>e.trim()).filter(Boolean):o?.includes(` `)?o.split(/\s+/).filter(Boolean):o,c=Array.isArray(s)&&s.length===1?s[0]:s,u=i.comments?.flag?.includes(`fuzzy`)??!1,{comment:d,customId:f,sourceMessage:p}=x(i.comments?.extracted),m=p&&(0,l.hashMessage)(p,t)===r?p:void 0,h=f??(m?r:(0,l.hashMessage)(r,t));n[h]={message:m??r,...t===void 0?{}:{context:t},...d===void 0?{}:{comment:d},...a?{translation:a}:{},...c===void 0?{}:{origin:c},...u?{fuzzy:!0}:{}}}return n}function b(e){let t={"":{"":{msgid:``,msgstr:[`Content-Type: text/plain; charset=UTF-8
5
- `]}}};for(let[n,r]of Object.entries(e)){let e={msgid:r.message??n,...r.context===void 0?{}:{msgctxt:r.context},msgstr:[r.translation??``]},i={};r.origin&&(i.reference=Array.isArray(r.origin)?r.origin.join(`
6
- `):r.origin);let a=C(n,r.message??n,r.context,r.comment);a&&(i.extracted=a),r.fuzzy&&(i.flag=`fuzzy`),(i.reference||i.extracted||i.flag)&&(e.comments=i);let o=r.context??``;t[o]??={},t[o][e.msgid]=e}let n={headers:{"Content-Type":`text/plain; charset=UTF-8`},translations:t};return u.po.compile(n).toString()}function x(e){if(!e)return{};let t=e.split(`
7
- `).map(e=>e.trim()).filter(Boolean),n,r,i=[];for(let e of t){if(e.startsWith(v)){n=e.slice(11).trim()||void 0;continue}if(e.startsWith("msg`")&&e.endsWith("`")){r=e.slice(4,-1);continue}if(e.startsWith(`Trans: `)){r=S(e.slice(7));continue}i.push(e)}return{...i.length>0?{comment:i.join(`
8
- `)}:{},...n?{customId:n}:{},...r?{sourceMessage:r}:{}}}function S(e){let t=[],n=0;return e.replace(/<\/?([a-zA-Z][\w-]*)>/g,(e,r)=>{let i=r;if(e.startsWith(`</`)){for(let e=t.length-1;e>=0;e--){let n=t[e];if(n?.tag===i)return t=t.filter((t,n)=>n!==e),`</${n.index}>`}return e}let a=n++;return t.push({tag:i,index:a}),`<${a}>`})}function C(e,t,n,r){let i=[];return r&&i.push(r),e!==(0,l.hashMessage)(t,n)&&i.push(`${v} ${e}`),i.length>0?i.join(`
9
- `):void 0}var w=/\{(\w+)\}/g,T=/\{(\w+)\}/;function E(e){return T.test(e)}function D(e){return e.replace(/\\/g,`\\\\`).replace(/'/g,`\\'`).replace(/\n/g,`\\n`).replace(/\r/g,`\\r`)}function O(e){return e.replace(/\\/g,`\\\\`).replace(/`/g,"\\`").replace(/\$\{/g,"\\${").replace(/\n/g,`\\n`).replace(/\r/g,`\\r`)}function k(e){return e.replace(w,(e,t)=>`\${v.${t}}`)}var A=/\{(\w+),\s*(plural|select|selectordinal)\s*,/;function j(e){return A.test(e)}function M(e,t){if(e.length===0)return`''`;let n=e.map(e=>N(e,t));return n.length===1?n[0]:n.join(` + `)}function N(e,t){switch(e.type){case`text`:return`'${D(e.value)}'`;case`variable`:return e.name===`#`?`String(__c)`:`String(v.${e.name} ?? '{${e.name}}')`;case`plural`:return P(e,t);case`select`:return F(e,t);case`function`:return`String(v.${e.variable} ?? '')`}}function P(e,t){let n=e.offset??0,r=n?`(v.${e.variable} - ${n})`:`v.${e.variable}`,i=[];i.push(`((c) => { const __c = c; `);let a=Object.keys(e.options).filter(e=>e.startsWith(`=`));if(a.length>0)for(let n of a){let r=n.slice(1),a=M(e.options[n],t);i.push(`if (c === ${r}) return ${a}; `)}let o=Object.keys(e.options).filter(e=>!e.startsWith(`=`));if(o.length>1||o.length===1&&o[0]!==`other`){i.push(`const __cat = new Intl.PluralRules('${D(t)}').select(c); `);for(let n of o){if(n===`other`)continue;let r=M(e.options[n],t);i.push(`if (__cat === '${n}') return ${r}; `)}}let s=e.options.other?M(e.options.other,t):`''`;return i.push(`return ${s}; `),i.push(`})(${r})`),i.join(``)}function F(e,t){let n=[];n.push(`((s) => { `);let r=Object.keys(e.options).filter(e=>e!==`other`);for(let i of r){let r=M(e.options[i],t);n.push(`if (s === '${D(i)}') return ${r}; `)}let i=e.options.other?M(e.options.other,t):`''`;return n.push(`return ${i}; `),n.push(`})(String(v.${e.variable} ?? ''))`),n.join(``)}function I(e,t,n,r,i){let a=[];a.push(`// @fluenti/compiled v1`);let o=[],s=0,c=[],u=new Map;for(let d of n){let n=(0,l.hashMessage)(d),f=u.get(n);if(f!==void 0&&f!==d)throw Error(`Hash collision detected: messages "${f}" and "${d}" produce the same hash "${n}"`);u.set(n,d);let p=`_${n}`,m=e[d],h=L(m,d,t,r,i?.skipFuzzy);if(h===void 0)a.push(`export const ${p} = undefined`),c.push(d);else if(j(h)){let e=M((0,l.parse)(h),t);a.push(`export const ${p} = (v) => ${e}`),s++}else if(E(h)){let e=k(O(h));a.push(`export const ${p} = (v) => \`${e}\``),s++}else a.push(`export const ${p} = '${D(h)}'`),s++;o.push({id:d,exportName:p})}if(o.length===0)return{code:`// @fluenti/compiled v1
10
- // empty catalog
11
- export default {}
12
- `,stats:{compiled:0,missing:[]}};a.push(``),a.push(`export default {`);for(let{id:e,exportName:t}of o)a.push(` '${D(e)}': ${t},`);return a.push(`}`),a.push(``),{code:a.join(`
13
- `),stats:{compiled:s,missing:c}}}function L(e,t,n,r,i){let a=r??n;if(e&&!(i&&e.fuzzy)){if(e.translation!==void 0&&e.translation.length>0)return e.translation;if(n===a)return e.message??t}}function R(e,t){let n=[];n.push(`export const locales = ${JSON.stringify(e)}`),n.push(``),n.push(`export const loaders = {`);for(let t of e)n.push(` '${D(t)}': () => import('./${D(t)}.js'),`);return n.push(`}`),n.push(``),n.join(`
14
- `)}function z(e){let t=new Set;for(let n of Object.values(e))for(let[e,r]of Object.entries(n))r.obsolete||t.add(e);return[...t].sort()}function B(e){let t=(0,l.parse)(e),n=new Map;return V(t,n),[...n.entries()].sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>({name:e,type:t}))}function V(e,t){for(let n of e)switch(n.type){case`variable`:n.name!==`#`&&!t.has(n.name)&&t.set(n.name,`string | number`);break;case`plural`:{let e=n;t.set(e.variable,`number`);for(let n of Object.values(e.options))V(n,t);break}case`select`:{let e=n,r=Object.keys(e.options).filter(e=>e!==`other`),i=`other`in e.options,a=r.map(e=>`'${e}'`).join(` | `),o=i?r.length>0?`${a} | string`:`string`:r.length>0?a:`string`;t.set(e.variable,o);for(let n of Object.values(e.options))V(n,t);break}case`function`:t.has(n.variable)||t.set(n.variable,`string | number`);break;case`text`:break}}function H(e,t,n){let r=[];if(r.push(`// Auto-generated by @fluenti/cli — do not edit`),r.push(``),e.length===0)r.push(`export type MessageId = never`);else{r.push(`export type MessageId =`);for(let t of e)r.push(` | '${D(t)}'`)}r.push(``),r.push(`export interface MessageValues {`);for(let i of e){let e=B(t[n]?.[i]?.message??i),a=D(i);if(e.length===0)r.push(` '${a}': Record<string, never>`);else{let t=e.map(e=>`${e.name}: ${e.type}`).join(`; `);r.push(` '${a}': { ${t} }`)}}return r.push(`}`),r.push(``),r.join(`
15
- `)}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return z}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return o}});
16
- //# sourceMappingURL=compile-d4Q8bND5.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"compile-d4Q8bND5.cjs","names":[],"sources":["../src/catalog.ts","../src/json-format.ts","../src/po-format.ts","../src/compile.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core'\n\nexport interface CatalogEntry {\n message?: string | undefined\n context?: string | undefined\n comment?: string | undefined\n translation?: string | undefined\n origin?: string | string[] | undefined\n obsolete?: boolean | undefined\n fuzzy?: boolean | undefined\n}\n\nexport type CatalogData = Record<string, CatalogEntry>\n\nexport interface UpdateResult {\n added: number\n unchanged: number\n obsolete: number\n}\n\nexport interface UpdateCatalogOptions {\n stripFuzzy?: boolean\n}\n\n/** Update catalog with newly extracted messages */\nexport function updateCatalog(\n existing: CatalogData,\n extracted: ExtractedMessage[],\n options?: UpdateCatalogOptions,\n): { catalog: CatalogData; result: UpdateResult } {\n const extractedIds = new Set(extracted.map((m) => m.id))\n const consumedCarryForwardIds = new Set<string>()\n const catalog: CatalogData = {}\n let added = 0\n let unchanged = 0\n let obsolete = 0\n\n for (const msg of extracted) {\n const existingEntry = existing[msg.id]\n const carried = existingEntry\n ? undefined\n : findCarryForwardEntry(existing, msg, consumedCarryForwardIds)\n const origin = `${msg.origin.file}:${msg.origin.line}`\n const baseEntry = existingEntry ?? carried?.entry\n\n if (carried) {\n consumedCarryForwardIds.add(carried.id)\n }\n\n if (baseEntry) {\n catalog[msg.id] = {\n ...baseEntry,\n message: msg.message ?? baseEntry.message,\n context: msg.context,\n comment: msg.comment,\n origin,\n obsolete: false,\n }\n unchanged++\n } else if (catalog[msg.id]) {\n // Same ID already seen in this extraction batch — merge origin\n const existing = catalog[msg.id]!\n catalog[msg.id] = {\n ...existing,\n origin: mergeOrigins(existing.origin, origin),\n }\n } else {\n catalog[msg.id] = {\n message: msg.message,\n context: msg.context,\n comment: msg.comment,\n origin,\n }\n added++\n }\n\n if (options?.stripFuzzy) {\n const { fuzzy: _fuzzy, ...rest } = catalog[msg.id]!\n catalog[msg.id] = rest\n }\n }\n\n for (const [id, entry] of Object.entries(existing)) {\n if (!extractedIds.has(id)) {\n const { fuzzy: _fuzzy, ...rest } = entry\n const obsoleteEntry = options?.stripFuzzy\n ? { ...rest, obsolete: true }\n : { ...entry, obsolete: true }\n catalog[id] = obsoleteEntry\n obsolete++\n }\n }\n\n return { catalog, result: { added, unchanged, obsolete } }\n}\n\nfunction mergeOrigins(\n existing: string | string[] | undefined,\n newOrigin: string,\n): string | string[] {\n if (!existing) return newOrigin\n const existingArray = Array.isArray(existing) ? existing : [existing]\n const merged = [...new Set([...existingArray, newOrigin])]\n return merged.length === 1 ? merged[0]! : merged\n}\n\nfunction findCarryForwardEntry(\n existing: CatalogData,\n extracted: ExtractedMessage,\n consumedCarryForwardIds: Set<string>,\n): { id: string; entry: CatalogEntry } | undefined {\n if (!extracted.context) {\n return undefined\n }\n\n const extractedOrigin = `${extracted.origin.file}:${extracted.origin.line}`\n for (const [id, entry] of Object.entries(existing)) {\n if (consumedCarryForwardIds.has(id)) continue\n if (entry.context !== undefined) continue\n if (entry.message !== extracted.message) continue\n if (!sameOrigin(entry.origin, extractedOrigin)) continue\n return { id, entry }\n }\n\n return undefined\n}\n\nfunction sameOrigin(previous: string | string[] | undefined, next: string): boolean {\n if (!previous) return false\n const origins = Array.isArray(previous) ? previous : [previous]\n return origins.some((o) => o === next || originFile(o) === originFile(next))\n}\n\nfunction originFile(origin: string): string {\n const match = origin.match(/^(.*):\\d+$/)\n return match?.[1] ?? origin\n}\n","import type { CatalogData } from './catalog'\n\n/** Read a JSON catalog file */\nexport function readJsonCatalog(content: string): CatalogData {\n const raw = JSON.parse(content) as Record<string, unknown>\n const catalog: CatalogData = {}\n\n for (const [id, entry] of Object.entries(raw)) {\n if (typeof entry === 'object' && entry !== null) {\n const e = entry as Record<string, unknown>\n catalog[id] = {\n message: typeof e['message'] === 'string' ? e['message'] : undefined,\n context: typeof e['context'] === 'string' ? e['context'] : undefined,\n comment: typeof e['comment'] === 'string' ? e['comment'] : undefined,\n translation: typeof e['translation'] === 'string' ? e['translation'] : undefined,\n origin: typeof e['origin'] === 'string'\n ? e['origin']\n : Array.isArray(e['origin']) && (e['origin'] as unknown[]).every((v) => typeof v === 'string')\n ? (e['origin'] as string[])\n : undefined,\n obsolete: typeof e['obsolete'] === 'boolean' ? e['obsolete'] : undefined,\n fuzzy: typeof e['fuzzy'] === 'boolean' ? e['fuzzy'] : undefined,\n }\n }\n }\n\n return catalog\n}\n\n/** Write a catalog to JSON format */\nexport function writeJsonCatalog(catalog: CatalogData): string {\n const output: Record<string, Record<string, unknown>> = {}\n\n for (const [id, entry] of Object.entries(catalog)) {\n const obj: Record<string, unknown> = {}\n if (entry.message !== undefined) obj['message'] = entry.message\n if (entry.context !== undefined) obj['context'] = entry.context\n if (entry.comment !== undefined) obj['comment'] = entry.comment\n if (entry.translation !== undefined) obj['translation'] = entry.translation\n if (entry.origin !== undefined) obj['origin'] = entry.origin\n if (entry.obsolete) obj['obsolete'] = true\n if (entry.fuzzy) obj['fuzzy'] = true\n output[id] = obj\n }\n\n return JSON.stringify(output, null, 2) + '\\n'\n}\n","import type { CatalogData } from './catalog'\nimport { hashMessage } from '@fluenti/core'\nimport * as gettextParser from 'gettext-parser'\n\nconst CUSTOM_ID_MARKER = 'fluenti-id:'\n\ninterface POTranslation {\n msgid: string\n msgctxt?: string\n msgstr: string[]\n comments?: {\n reference?: string\n extracted?: string\n flag?: string\n translator?: string\n previous?: string\n }\n}\n\ninterface POData {\n headers?: Record<string, string>\n translations: Record<string, Record<string, POTranslation>>\n}\n\ninterface ParsedExtractedComment {\n comment?: string\n customId?: string\n sourceMessage?: string\n}\n\n/** Read a PO catalog file */\nexport function readPoCatalog(content: string): CatalogData {\n const po = gettextParser.po.parse(content) as POData\n const catalog: CatalogData = {}\n const translations = po.translations ?? {}\n\n for (const [contextKey, entries] of Object.entries(translations)) {\n for (const [msgid, entry] of Object.entries(entries)) {\n if (!msgid) continue\n\n const context = contextKey || entry.msgctxt || undefined\n const translation = entry.msgstr?.[0] ?? undefined\n const rawReference = entry.comments?.reference ?? undefined\n const origin = rawReference?.includes('\\n')\n ? rawReference.split('\\n').map((r: string) => r.trim()).filter(Boolean)\n : rawReference?.includes(' ')\n ? rawReference.split(/\\s+/).filter(Boolean)\n : rawReference\n const normalizedOrigin = Array.isArray(origin) && origin.length === 1 ? origin[0] : origin\n const isFuzzy = entry.comments?.flag?.includes('fuzzy') ?? false\n const { comment, customId, sourceMessage } = parseExtractedComment(entry.comments?.extracted)\n const resolvedSourceMessage = sourceMessage\n && hashMessage(sourceMessage, context) === msgid\n ? sourceMessage\n : undefined\n const id = customId\n ?? (resolvedSourceMessage ? msgid : hashMessage(msgid, context))\n\n catalog[id] = {\n message: resolvedSourceMessage ?? msgid,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n ...(translation ? { translation } : {}),\n ...(normalizedOrigin !== undefined ? { origin: normalizedOrigin } : {}),\n ...(isFuzzy ? { fuzzy: true } : {}),\n }\n }\n }\n\n return catalog\n}\n\n/** Write a catalog to PO format */\nexport function writePoCatalog(catalog: CatalogData): string {\n const translations: POData['translations'] = {\n '': {\n '': {\n msgid: '',\n msgstr: ['Content-Type: text/plain; charset=UTF-8\\n'],\n },\n },\n }\n\n for (const [id, entry] of Object.entries(catalog)) {\n const poEntry: POTranslation = {\n msgid: entry.message ?? id,\n ...(entry.context !== undefined ? { msgctxt: entry.context } : {}),\n msgstr: [entry.translation ?? ''],\n }\n\n const comments: POTranslation['comments'] = {}\n if (entry.origin) {\n comments.reference = Array.isArray(entry.origin)\n ? entry.origin.join('\\n')\n : entry.origin\n }\n const extractedComment = buildExtractedComment(id, entry.message ?? id, entry.context, entry.comment)\n if (extractedComment) {\n comments.extracted = extractedComment\n }\n if (entry.fuzzy) {\n comments.flag = 'fuzzy'\n }\n if (comments.reference || comments.extracted || comments.flag) {\n poEntry.comments = comments\n }\n\n const contextKey = entry.context ?? ''\n translations[contextKey] ??= {}\n translations[contextKey][poEntry.msgid] = poEntry\n }\n\n const poData: POData = {\n headers: {\n 'Content-Type': 'text/plain; charset=UTF-8',\n },\n translations,\n }\n\n const buffer = gettextParser.po.compile(poData as Parameters<typeof gettextParser.po.compile>[0])\n return buffer.toString()\n}\n\nfunction parseExtractedComment(\n extracted: string | undefined,\n): ParsedExtractedComment {\n if (!extracted) {\n return {}\n }\n\n const lines = extracted.split('\\n').map((line) => line.trim()).filter(Boolean)\n let customId: string | undefined\n let sourceMessage: string | undefined\n const commentLines: string[] = []\n\n for (const line of lines) {\n if (line.startsWith(CUSTOM_ID_MARKER)) {\n customId = line.slice(CUSTOM_ID_MARKER.length).trim() || undefined\n continue\n }\n if (line.startsWith('msg`') && line.endsWith('`')) {\n sourceMessage = line.slice(4, -1)\n continue\n }\n if (line.startsWith('Trans: ')) {\n sourceMessage = normalizeRichTextComment(line.slice('Trans: '.length))\n continue\n }\n commentLines.push(line)\n }\n\n return {\n ...(commentLines.length > 0 ? { comment: commentLines.join('\\n') } : {}),\n ...(customId ? { customId } : {}),\n ...(sourceMessage ? { sourceMessage } : {}),\n }\n}\n\nfunction normalizeRichTextComment(comment: string): string {\n let stack: Array<{ tag: string; index: number }> = []\n let nextIndex = 0\n\n return comment.replace(/<\\/?([a-zA-Z][\\w-]*)>/g, (match, rawTag: string) => {\n const tag = rawTag\n if (match.startsWith('</')) {\n for (let index = stack.length - 1; index >= 0; index--) {\n const entry = stack[index]\n if (entry?.tag !== tag) continue\n stack = stack.filter((_, i) => i !== index)\n return `</${entry.index}>`\n }\n return match\n }\n\n const index = nextIndex++\n stack.push({ tag, index })\n return `<${index}>`\n })\n}\n\nfunction buildExtractedComment(\n id: string,\n message: string,\n context: string | undefined,\n comment: string | undefined,\n): string | undefined {\n const lines: string[] = []\n\n if (comment) {\n lines.push(comment)\n }\n\n if (id !== hashMessage(message, context)) {\n lines.push(`${CUSTOM_ID_MARKER} ${id}`)\n }\n\n return lines.length > 0 ? lines.join('\\n') : undefined\n}\n","import type { CatalogData } from './catalog'\nimport { hashMessage } from '@fluenti/core'\nimport { parse } from '@fluenti/core'\nimport type { ASTNode, PluralNode, SelectNode, VariableNode, FunctionNode } from '@fluenti/core'\n\nconst ICU_VAR_REGEX = /\\{(\\w+)\\}/g\nconst ICU_VAR_TEST = /\\{(\\w+)\\}/\n\nfunction hasVariables(message: string): boolean {\n return ICU_VAR_TEST.test(message)\n}\n\n\nfunction escapeStringLiteral(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"\\\\'\")\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n}\n\nfunction escapeTemplateLiteral(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/`/g, '\\\\`')\n .replace(/\\$\\{/g, '\\\\${')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n}\n\nfunction messageToTemplateString(message: string): string {\n return message.replace(ICU_VAR_REGEX, (_match, name: string) => `\\${v.${name}}`)\n}\n\n\n// ─── ICU → JS code generation for split mode ───────────────────────────────\n\nconst ICU_PLURAL_SELECT_REGEX = /\\{(\\w+),\\s*(plural|select|selectordinal)\\s*,/\n\n/** Check if message contains ICU plural/select syntax */\nfunction hasIcuPluralOrSelect(message: string): boolean {\n return ICU_PLURAL_SELECT_REGEX.test(message)\n}\n\n/**\n * Compile an ICU AST node array into a JS expression string.\n * Used for generating static code (not runtime evaluation).\n */\nfunction astToJsExpression(nodes: ASTNode[], locale: string): string {\n if (nodes.length === 0) return \"''\"\n\n const parts = nodes.map((node) => astNodeToJs(node, locale))\n\n if (parts.length === 1) return parts[0]!\n return parts.join(' + ')\n}\n\nfunction astNodeToJs(node: ASTNode, locale: string): string {\n switch (node.type) {\n case 'text':\n return `'${escapeStringLiteral(node.value)}'`\n\n case 'variable':\n if (node.name === '#') return 'String(__c)'\n return `String(v.${node.name} ?? '{${node.name}}')`\n\n case 'plural':\n return pluralToJs(node as PluralNode, locale)\n\n case 'select':\n return selectToJs(node as SelectNode, locale)\n\n case 'function':\n return `String(v.${node.variable} ?? '')`\n }\n}\n\nfunction pluralToJs(node: PluralNode, locale: string): string {\n const offset = node.offset ?? 0\n const countExpr = offset ? `(v.${node.variable} - ${offset})` : `v.${node.variable}`\n\n const lines: string[] = []\n lines.push(`((c) => { const __c = c; `)\n\n // Exact matches first\n const exactKeys = Object.keys(node.options).filter((k) => k.startsWith('='))\n if (exactKeys.length > 0) {\n for (const key of exactKeys) {\n const num = key.slice(1)\n const body = astToJsExpression(node.options[key]!, locale)\n lines.push(`if (c === ${num}) return ${body}; `)\n }\n }\n\n // CLDR categories via Intl.PluralRules\n const cldrKeys = Object.keys(node.options).filter((k) => !k.startsWith('='))\n if (cldrKeys.length > 1 || (cldrKeys.length === 1 && cldrKeys[0] !== 'other')) {\n lines.push(`const __cat = new Intl.PluralRules('${escapeStringLiteral(locale)}').select(c); `)\n for (const key of cldrKeys) {\n if (key === 'other') continue\n const body = astToJsExpression(node.options[key]!, locale)\n lines.push(`if (__cat === '${key}') return ${body}; `)\n }\n }\n\n // Fallback to 'other'\n const otherBody = node.options['other']\n ? astToJsExpression(node.options['other'], locale)\n : \"''\"\n lines.push(`return ${otherBody}; `)\n lines.push(`})(${countExpr})`)\n\n return lines.join('')\n}\n\nfunction selectToJs(node: SelectNode, locale: string): string {\n const lines: string[] = []\n lines.push(`((s) => { `)\n\n const keys = Object.keys(node.options).filter((k) => k !== 'other')\n for (const key of keys) {\n const body = astToJsExpression(node.options[key]!, locale)\n lines.push(`if (s === '${escapeStringLiteral(key)}') return ${body}; `)\n }\n\n const otherBody = node.options['other']\n ? astToJsExpression(node.options['other'], locale)\n : \"''\"\n lines.push(`return ${otherBody}; `)\n lines.push(`})(String(v.${node.variable} ?? ''))`)\n\n return lines.join('')\n}\n\n/**\n * Compile a catalog to ES module with tree-shakeable named exports.\n * Each message becomes a `/* @__PURE__ *​/` annotated named export.\n * A default export maps message IDs to their compiled values for runtime lookup.\n */\n/** Catalog format version. Bump when the compiled output format changes. */\nexport const CATALOG_VERSION = 1\n\nexport interface CompileStats {\n compiled: number\n missing: string[]\n}\n\nexport interface CompileOptions {\n skipFuzzy?: boolean\n}\n\nexport function compileCatalog(\n catalog: CatalogData,\n locale: string,\n allIds: string[],\n sourceLocale?: string,\n options?: CompileOptions,\n): { code: string; stats: CompileStats } {\n const lines: string[] = []\n lines.push(`// @fluenti/compiled v${CATALOG_VERSION}`)\n const exportNames: Array<{ id: string; exportName: string }> = []\n let compiled = 0\n const missing: string[] = []\n\n const hashToId = new Map<string, string>()\n\n for (const id of allIds) {\n const hash = hashMessage(id)\n\n const existingId = hashToId.get(hash)\n if (existingId !== undefined && existingId !== id) {\n throw new Error(\n `Hash collision detected: messages \"${existingId}\" and \"${id}\" produce the same hash \"${hash}\"`,\n )\n }\n hashToId.set(hash, id)\n\n const exportName = `_${hash}`\n const entry = catalog[id]\n const translated = resolveCompiledMessage(entry, id, locale, sourceLocale, options?.skipFuzzy)\n\n if (translated === undefined) {\n lines.push(`export const ${exportName} = undefined`)\n missing.push(id)\n } else if (hasIcuPluralOrSelect(translated)) {\n // Parse ICU and compile to JS\n const ast = parse(translated)\n const jsExpr = astToJsExpression(ast, locale)\n lines.push(`export const ${exportName} = (v) => ${jsExpr}`)\n compiled++\n } else if (hasVariables(translated)) {\n const templateStr = messageToTemplateString(escapeTemplateLiteral(translated))\n lines.push(`export const ${exportName} = (v) => \\`${templateStr}\\``)\n compiled++\n } else {\n lines.push(`export const ${exportName} = '${escapeStringLiteral(translated)}'`)\n compiled++\n }\n\n exportNames.push({ id, exportName })\n }\n\n if (exportNames.length === 0) {\n return {\n code: `// @fluenti/compiled v${CATALOG_VERSION}\\n// empty catalog\\nexport default {}\\n`,\n stats: { compiled: 0, missing: [] },\n }\n }\n\n // Default export maps message IDs → compiled values for runtime lookup\n lines.push('')\n lines.push('export default {')\n for (const { id, exportName } of exportNames) {\n lines.push(` '${escapeStringLiteral(id)}': ${exportName},`)\n }\n lines.push('}')\n lines.push('')\n\n return { code: lines.join('\\n'), stats: { compiled, missing } }\n}\n\nfunction resolveCompiledMessage(\n entry: CatalogData[string] | undefined,\n id: string,\n locale: string,\n sourceLocale: string | undefined,\n skipFuzzy?: boolean,\n): string | undefined {\n const effectiveSourceLocale = sourceLocale ?? locale\n\n if (!entry) {\n return undefined\n }\n\n if (skipFuzzy && entry.fuzzy) {\n return undefined\n }\n\n if (entry.translation !== undefined && entry.translation.length > 0) {\n return entry.translation\n }\n\n if (locale === effectiveSourceLocale) {\n return entry.message ?? id\n }\n\n return undefined\n}\n\n/**\n * Generate the index module that exports locale list and lazy loaders.\n */\nexport function compileIndex(locales: string[], _catalogDir: string): string {\n const lines: string[] = []\n lines.push(`export const locales = ${JSON.stringify(locales)}`)\n lines.push('')\n lines.push('export const loaders = {')\n for (const locale of locales) {\n lines.push(` '${escapeStringLiteral(locale)}': () => import('./${escapeStringLiteral(locale)}.js'),`)\n }\n lines.push('}')\n lines.push('')\n return lines.join('\\n')\n}\n\n/**\n * Collect the union of all message IDs across all locale catalogs.\n * Ensures every locale file exports the same names.\n */\nexport function collectAllIds(catalogs: Record<string, CatalogData>): string[] {\n const idSet = new Set<string>()\n for (const catalog of Object.values(catalogs)) {\n for (const [id, entry] of Object.entries(catalog)) {\n if (!entry.obsolete) {\n idSet.add(id)\n }\n }\n }\n return [...idSet].sort()\n}\n\n// ─── Type-safe message ID generation ─────────────────────────────────────────\n\nexport interface MessageVariable {\n name: string\n type: string\n}\n\n/**\n * Extract variable names and their TypeScript types from an ICU message string.\n */\nexport function extractMessageVariables(message: string): MessageVariable[] {\n const ast = parse(message)\n const vars = new Map<string, string>()\n collectVariablesFromNodes(ast, vars)\n return [...vars.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, type]) => ({ name, type }))\n}\n\nfunction collectVariablesFromNodes(nodes: ASTNode[], vars: Map<string, string>): void {\n for (const node of nodes) {\n switch (node.type) {\n case 'variable':\n if (node.name !== '#' && !vars.has(node.name)) {\n vars.set((node as VariableNode).name, 'string | number')\n }\n break\n case 'plural': {\n const pn = node as PluralNode\n // Plural variable is always a number\n vars.set(pn.variable, 'number')\n // Recurse into plural option bodies\n for (const optionNodes of Object.values(pn.options)) {\n collectVariablesFromNodes(optionNodes, vars)\n }\n break\n }\n case 'select': {\n const sn = node as SelectNode\n const keys = Object.keys(sn.options).filter((k) => k !== 'other')\n const hasOther = 'other' in sn.options\n const literalTypes = keys.map((k) => `'${k}'`).join(' | ')\n const selectType = hasOther\n ? (keys.length > 0 ? `${literalTypes} | string` : 'string')\n : (keys.length > 0 ? literalTypes : 'string')\n vars.set(sn.variable, selectType)\n // Recurse into select option bodies\n for (const optionNodes of Object.values(sn.options)) {\n collectVariablesFromNodes(optionNodes, vars)\n }\n break\n }\n case 'function':\n if (!vars.has((node as FunctionNode).variable)) {\n vars.set((node as FunctionNode).variable, 'string | number')\n }\n break\n case 'text':\n break\n }\n }\n}\n\n/**\n * Generate a TypeScript declaration file with MessageId union and MessageValues interface.\n */\nexport function compileTypeDeclaration(\n allIds: string[],\n catalogs: Record<string, CatalogData>,\n sourceLocale: string,\n): string {\n const lines: string[] = []\n lines.push('// Auto-generated by @fluenti/cli — do not edit')\n lines.push('')\n\n // MessageId union\n if (allIds.length === 0) {\n lines.push('export type MessageId = never')\n } else {\n lines.push('export type MessageId =')\n for (const id of allIds) {\n lines.push(` | '${escapeStringLiteral(id)}'`)\n }\n }\n\n lines.push('')\n\n // MessageValues interface\n lines.push('export interface MessageValues {')\n for (const id of allIds) {\n // Use source locale catalog to get the message for variable extraction\n const sourceCatalog = catalogs[sourceLocale]\n const entry = sourceCatalog?.[id]\n const message = entry?.message ?? id\n const vars = extractMessageVariables(message)\n\n const escapedId = escapeStringLiteral(id)\n if (vars.length === 0) {\n lines.push(` '${escapedId}': Record<string, never>`)\n } else {\n const fields = vars.map((v) => `${v.name}: ${v.type}`).join('; ')\n lines.push(` '${escapedId}': { ${fields} }`)\n }\n }\n lines.push('}')\n lines.push('')\n\n return lines.join('\\n')\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- require(`./compile-d4Q8bND5.cjs`);let e=require(`@fluenti/core/internal`);var t=new Set([`@fluenti/react`,`@fluenti/vue`,`@fluenti/solid`,`@fluenti/next`]);function n(e){let t=e.trim();if(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(t))return t;if(/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(t)&&!t.endsWith(`.`)){let e=t.split(`.`);return e[e.length-1]}let n=t.match(/^([a-zA-Z_$][a-zA-Z0-9_$.]*)\s*\(/);return n?n[1].replace(/\./g,`_`):``}function r(e,t){let r=``,i=0;for(let a=0;a<e.length;a++){if(r+=e[a],a>=t.length)continue;let o=n(t[a]);if(o===``){r+=`{arg${i}}`,i++;continue}r+=`{${o}}`}return r}function i(e,t,n){if(!e.message)return;let r=n.loc?.start.line??1,i=(n.loc?.start.column??0)+1;return{id:e.id,message:e.message,...e.context===void 0?{}:{context:e.context},...e.comment===void 0?{}:{comment:e.comment},origin:{file:t,line:r,column:i}}}function a(t){if(t.message)return{id:t.id??(0,e.createMessageId)(t.message,t.context),message:t.message,...t.context===void 0?{}:{context:t.context},...t.comment===void 0?{}:{comment:t.comment}}}function o(e){if(e.type===`StringLiteral`)return a({message:e.value});if(e.type===`TemplateLiteral`){let t=e;return t.expressions.length===0?a({message:t.quasis.map(e=>e.value.cooked??e.value.raw).join(``)}):void 0}if(e.type!==`ObjectExpression`)return;let t={};for(let n of e.properties){if(n.type!==`ObjectProperty`)continue;let e=n;if(e.computed||!S(e.key))continue;let r=e.key.name;if(![`id`,`message`,`context`,`comment`].includes(r))continue;let i=u(e.value);i!==void 0&&(t[r]=i)}if(t.message)return a(t)}function s(e){let t=[`zero`,`one`,`two`,`few`,`many`,`other`],n=e.value??e.count??`count`,r=[],i=e.offset;for(let n of t){let t=e[n];if(t===void 0)continue;let i=n===`zero`?`=0`:n;r.push(`${i} {${t}}`)}return r.length===0?``:`{${n}, plural, ${i?`offset:${i} `:``}${r.join(` `)}}`}function c(e){let t=0;function n(e){let r=``;for(let i of e){if(i.type===`JSXText`){r+=l(i.value);continue}if(i.type===`JSXElement`){let e=t++,a=n(i.children);if(a===void 0)return;r+=`<${e}>${a}</${e}>`;continue}if(i.type===`JSXFragment`){let e=n(i.children);if(e===void 0)return;r+=e;continue}if(i.type===`JSXExpressionContainer`){let e=i.expression;if(e.type===`StringLiteral`){r+=e.value;continue}if(e.type===`NumericLiteral`){r+=String(e.value);continue}return}}return r}let r=n(e);if(r!==void 0)return r.replace(/\s+/g,` `).trim()||void 0}function l(e){return e.replace(/\s+/g,` `)}function u(e){if(e.type===`StringLiteral`)return e.value;if(e.type===`NumericLiteral`)return String(e.value);if(e.type===`JSXExpressionContainer`)return u(e.expression);if(e.type===`TemplateLiteral`){let t=e;if(t.expressions.length===0)return t.quasis.map(e=>e.value.cooked??e.value.raw).join(``)}}function d(e,t){if(!(e.start==null||e.end==null))return e.type===`JSXExpressionContainer`?d(e.expression,t):t.slice(e.start,e.end).trim()}function f(e,t){for(let n of e.attributes){if(n.type!==`JSXAttribute`)continue;let e=n;if(e.name.type===`JSXIdentifier`&&e.name.name===t)return e}}function p(e,t){let n={};for(let r of[`id`,`value`,`count`,`offset`,`zero`,`one`,`two`,`few`,`many`,`other`]){let i=f(e,r);if(!i?.value)continue;let a=u(i.value);if(a!==void 0){n[r]=a;continue}let o=d(i.value,t);o!==void 0&&(r===`value`||r===`count`||r===`offset`)&&(n[r]=o)}return n}function m(t,n){let i=r(n.quasi.quasis.map(e=>e.value.cooked??e.value.raw),n.quasi.expressions.map(e=>e.start==null||e.end==null?``:t.slice(e.start,e.end)));return{id:(0,e.createMessageId)(i),message:i}}function h(e){let n=new Set,r=Array.isArray(e.body)?e.body:[];for(let e of r)if(_(e)&&t.has(e.source.value))for(let t of e.specifiers)v(t)&&y(t)===`t`&&n.add(t.local.name);return n}function g(t,n){let r=(0,e.parseSourceModule)(t);if(!r)return[];let a=[],l=h(r);return(0,e.walkSourceAst)(r,r=>{if(r.type===`TaggedTemplateExpression`){let e=r;if(S(e.tag)&&(e.tag.name===`t`||l.has(e.tag.name))){let r=i(m(t,e),n,e);r&&a.push(r)}return}if(r.type===`CallExpression`){let e=r;if(S(e.callee)&&(e.callee.name===`t`||l.has(e.callee.name))){if(l.has(e.callee.name)&&e.arguments[0]?.type!==`ObjectExpression`)return;let t=e.arguments[0]?o(e.arguments[0]):void 0,r=t?i(t,n,e):void 0;r&&a.push(r)}return}if(r.type!==`JSXElement`)return;let d=r,h=d.openingElement,g=b(h.name);if(g===`Trans`){let e=f(h,`message`),t=f(h,`id`),r=f(h,`context`),o=f(h,`comment`),s=e?.value?x({id:t?.value?u(t.value):void 0,message:u(e.value),context:r?.value?u(r.value):void 0,comment:o?.value?u(o.value):void 0}):x({id:t?.value?u(t.value):void 0,message:c(d.children),context:r?.value?u(r.value):void 0,comment:o?.value?u(o.value):void 0}),l=s?i(s,n,d):void 0;l&&a.push(l);return}if(g===`Plural`){let r=p(h,t),o=s(r);if(!o)return;let c=i({id:r.id??(0,e.createMessageId)(o),message:o},n,d);c&&a.push(c)}}),a}function _(t){return(0,e.isSourceNode)(t)&&t.type===`ImportDeclaration`}function v(t){return(0,e.isSourceNode)(t)&&t.type===`ImportSpecifier`}function y(e){if(e.imported.type===`Identifier`)return e.imported.name;if(e.imported.type===`StringLiteral`)return e.imported.value}function b(e){if(e.type===`JSXIdentifier`)return String(e.name)}function x(e){let t={};return e.id!==void 0&&(t.id=e.id),e.message!==void 0&&(t.message=e.message),e.context!==void 0&&(t.context=e.context),e.comment!==void 0&&(t.comment=e.comment),a(t)}function S(t){return(0,e.isSourceNode)(t)&&t.type===`Identifier`}Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return g}});
2
- //# sourceMappingURL=tsx-extractor-LEAVCuX9.cjs.map
@@ -1,3 +0,0 @@
1
- const e=require(`./compile-d4Q8bND5.cjs`),t=require(`./tsx-extractor-LEAVCuX9.cjs`);let n=require(`@vue/compiler-sfc`),r=require(`@fluenti/core/internal`);var i=e.u({extractFromVue:()=>g}),a=1,o=2,s=7,c=6;function l(e){return e.filter(e=>e.type===o).map(e=>(e.content??``).trim()).join(``)}function u(e,t){let n=e.split(`|`).map(e=>e.trim()),r=[`one`,`other`,`zero`,`few`,`many`],i=[];if(n.length===2)i.push(`one {${n[0]}}`),i.push(`other {${n[1]}}`);else for(let e=0;e<n.length&&e<r.length;e++)i.push(`${r[e]} {${n[e]}}`);return`{${t}, plural, ${i.join(` `)}}`}function d(e){let t=e.count??`count`,n=[`zero`,`one`,`two`,`few`,`many`,`other`],r=[],i=e.offset;for(let t of n)if(e[t]!==void 0){let n=t===`zero`?`=0`:t;r.push(`${n} {${e[t]}}`)}return r.length===0?``:`{${t}, plural, ${i?`offset:${i} `:``}${r.join(` `)}}`}function f(e,t,n){if(e.type===a){let i=e.props?.find(e=>e.type===s&&m(e)===`t`);if(i){let a=new Set([`plural`]),o=(i.modifiers??[]).map(e=>typeof e==`string`?e:e.content),s=o.includes(`plural`),c=o.filter(e=>!a.has(e)),d=i.arg?.content,f=d?[d,...c].join(`.`):void 0,p=l(e.children??[]);if(s){let e=u(p,i.exp?.content??`count`),a=f??(0,r.createMessageId)(e);n.push({id:a,message:e,origin:{file:t,line:i.loc.start.line,column:i.loc.start.column}})}else if(p){let e=f??(0,r.createMessageId)(p);n.push({id:e,message:p,origin:{file:t,line:i.loc.start.line,column:i.loc.start.column}})}}if(e.tag===`Trans`){let i=e.props?.find(e=>e.type===c&&m(e)===`message`),a=e.props?.find(e=>e.type===c&&m(e)===`id`),o=e.props?.find(e=>e.type===c&&m(e)===`context`),s=e.props?.find(e=>e.type===c&&m(e)===`comment`),l=o?.value?.content,u=s?.value?.content;if(i?.value){let o=i.value.content,s=a?.value?.content??(0,r.createMessageId)(o,l);n.push({id:s,message:o,...l===void 0?{}:{context:l},...u===void 0?{}:{comment:u},origin:{file:t,line:e.loc.start.line,column:e.loc.start.column}})}else if(e.children&&e.children.length>0){let i=p(e.children);if(i.message){let o=a?.value?.content??(0,r.createMessageId)(i.message,l);n.push({id:o,message:i.message,...l===void 0?{}:{context:l},...u===void 0?{}:{comment:u},origin:{file:t,line:e.loc.start.line,column:e.loc.start.column}})}}}if(e.tag===`Plural`){let i={},a,o;for(let t of e.props??[])t.type===c&&t.value&&(i[m(t)]=t.value.content),t.type===s&&m(t)===`bind`&&t.arg?.content===`value`&&t.exp&&(a=t.exp.content),t.type===s&&m(t)===`bind`&&t.arg?.content===`offset`&&t.exp&&(o=t.exp.content);let l=a??i.count??`count`,u=o??i.offset,f=d({...i,count:l,...u===void 0?{}:{offset:u}});if(f){let a=i.id??(0,r.createMessageId)(f);n.push({id:a,message:f,origin:{file:t,line:e.loc.start.line,column:e.loc.start.column}})}}}if(e.children)for(let r of e.children)f(r,t,n)}function p(e){let t=0,n=!1;return{message:e.map(e=>{if(e.type===o)return(e.content??``).trim()?e.content??``:``;if(e.type===a&&e.tag){n=!0;let r=t++;return`<${r}>${p(e.children??[]).message}</${r}>`}return``}).join(``).trim(),hasElements:n}}function m(e){return typeof e.name==`string`?e.name:e.name.content}function h(e,n){let r=[],i=/\{\{([\s\S]*?)\}\}/g,a;for(;(a=i.exec(e))!==null;){let i=a[1]?.trim();if(!i)continue;let o=t.t(i,n);if(o.length===0)continue;let s=e.slice(0,a.index).split(`
2
- `).length-1;for(let e of o)r.push({...e,origin:{...e.origin,line:e.origin.line+s}})}return r}function g(e,r){let i=[],{descriptor:a}=(0,n.parse)(e,{filename:r});if(a.template?.ast&&f(a.template.ast,r,i),a.template?.content){let e=t.t(a.template.content,r),n=a.template.loc.start.line-1,o=new Set(i.map(e=>e.id));for(let t of e)o.has(t.id)||i.push({...t,origin:{...t.origin,line:t.origin.line+n}});let s=h(a.template.content,r);for(let e of s)o.has(e.id)||i.push({...e,origin:{...e.origin,line:e.origin.line+n}})}if(a.scriptSetup?.content){let e=t.t(a.scriptSetup.content,r),n=a.scriptSetup.loc.start.line-1;for(let t of e)i.push({...t,origin:{...t.origin,line:t.origin.line+n}})}if(a.script?.content){let e=t.t(a.script.content,r),n=a.script.loc.start.line-1;for(let t of e)i.push({...t,origin:{...t.origin,line:t.origin.line+n}})}return i}Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return g}});
3
- //# sourceMappingURL=vue-extractor-BlHc3vzt.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vue-extractor-BlHc3vzt.cjs","names":[],"sources":["../src/vue-extractor.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core'\nimport { parse as parseSFC } from '@vue/compiler-sfc'\nimport { createMessageId } from '@fluenti/core/internal'\nimport { extractFromTsx } from './tsx-extractor'\n\n// Vue template AST node types\nconst ELEMENT_NODE = 1\nconst TEXT_NODE = 2\nconst DIRECTIVE_PROP = 7\nconst ATTRIBUTE_PROP = 6\n\ninterface LocInfo {\n line: number\n column: number\n offset: number\n}\n\ninterface SourceLoc {\n start: LocInfo\n end: LocInfo\n source: string\n}\n\ninterface TemplateNode {\n type: number\n tag?: string\n tagType?: number\n props?: TemplateProp[]\n children?: TemplateNode[]\n content?: string\n loc: SourceLoc\n}\n\ninterface TemplateProp {\n type: number\n name: string | { content: string }\n rawName?: string\n arg?: { content: string; isStatic: boolean }\n exp?: { content: string }\n modifiers?: Array<{ content: string } | string>\n value?: { content: string }\n nameLoc?: SourceLoc\n loc: SourceLoc\n}\n\nfunction getTextContent(children: TemplateNode[]): string {\n return children\n .filter((c) => c.type === TEXT_NODE)\n .map((c) => (c.content ?? '').trim())\n .join('')\n}\n\nfunction buildPluralICUFromPipe(text: string, countVar: string): string {\n const forms = text.split('|').map((s) => s.trim())\n const categories = ['one', 'other', 'zero', 'few', 'many']\n const options: string[] = []\n\n if (forms.length === 2) {\n options.push(`one {${forms[0]}}`)\n options.push(`other {${forms[1]}}`)\n } else {\n for (let i = 0; i < forms.length && i < categories.length; i++) {\n options.push(`${categories[i]} {${forms[i]}}`)\n }\n }\n\n return `{${countVar}, plural, ${options.join(' ')}}`\n}\n\nfunction buildPluralICUFromProps(props: Record<string, string>): string {\n const countVar = props['count'] ?? 'count'\n const categories = ['zero', 'one', 'two', 'few', 'many', 'other']\n const options: string[] = []\n const offset = props['offset']\n\n for (const cat of categories) {\n if (props[cat] !== undefined) {\n const key = cat === 'zero' ? '=0' : cat\n options.push(`${key} {${props[cat]}}`)\n }\n }\n\n if (options.length === 0) return ''\n const offsetPrefix = offset ? `offset:${offset} ` : ''\n return `{${countVar}, plural, ${offsetPrefix}${options.join(' ')}}`\n}\n\nfunction walkTemplate(\n node: TemplateNode,\n filename: string,\n messages: ExtractedMessage[],\n): void {\n if (node.type === ELEMENT_NODE) {\n const vtDirective = node.props?.find(\n (p) => p.type === DIRECTIVE_PROP && getPropName(p) === 't',\n )\n\n if (vtDirective) {\n const RESERVED_MODIFIERS = new Set(['plural'])\n const modifiers = (vtDirective.modifiers ?? []).map(\n (m: string | { content: string }) => (typeof m === 'string' ? m : m.content),\n )\n const isPlural = modifiers.includes('plural')\n // Reconstruct dotted ID: v-t:checkout.title → arg=\"checkout\", modifier=\"title\" → \"checkout.title\"\n // Non-reserved modifiers are treated as ID path segments\n const idSegments = modifiers.filter((m: string) => !RESERVED_MODIFIERS.has(m))\n const argContent = vtDirective.arg?.content\n const explicitId = argContent\n ? [argContent, ...idSegments].join('.')\n : undefined\n const textContent = getTextContent(node.children ?? [])\n\n if (isPlural) {\n const countVar = vtDirective.exp?.content ?? 'count'\n const message = buildPluralICUFromPipe(textContent, countVar)\n const id = explicitId ?? createMessageId(message)\n messages.push({\n id,\n message,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n } else if (textContent) {\n const id = explicitId ?? createMessageId(textContent)\n messages.push({\n id,\n message: textContent,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n }\n }\n\n if (node.tag === 'Trans') {\n const messageProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'message',\n )\n const idProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'id',\n )\n const contextProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'context',\n )\n const commentProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'comment',\n )\n const context = contextProp?.value?.content\n const comment = commentProp?.value?.content\n\n if (messageProp?.value) {\n // Old API: <Trans message=\"...\" />\n const message = messageProp.value.content\n const id = idProp?.value?.content ?? createMessageId(message, context)\n messages.push({\n id,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n } else if (node.children && node.children.length > 0) {\n // New API: <Trans>content with <a>rich text</a></Trans>\n const richText = extractRichTextFromTemplateChildren(node.children)\n if (richText.message) {\n const id = idProp?.value?.content ?? createMessageId(richText.message, context)\n messages.push({\n id,\n message: richText.message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.tag === 'Plural') {\n const propsMap: Record<string, string> = {}\n let valueExpr: string | undefined\n let offsetExpr: string | undefined\n for (const prop of node.props ?? []) {\n if (prop.type === ATTRIBUTE_PROP && prop.value) {\n propsMap[getPropName(prop)] = prop.value.content\n }\n // Handle :value=\"expr\" binding (directive prop)\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'value' && prop.exp) {\n valueExpr = prop.exp.content\n }\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'offset' && prop.exp) {\n offsetExpr = prop.exp.content\n }\n }\n\n // Use :value binding expression as count variable, fall back to 'count' static prop\n const countVar = valueExpr ?? propsMap['count'] ?? 'count'\n const offset = offsetExpr ?? propsMap['offset']\n const pluralMessage = buildPluralICUFromProps({\n ...propsMap,\n count: countVar,\n ...(offset !== undefined ? { offset } : {}),\n })\n if (pluralMessage) {\n const id = propsMap['id'] ?? createMessageId(pluralMessage)\n messages.push({\n id,\n message: pluralMessage,\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, filename, messages)\n }\n }\n}\n\nfunction extractRichTextFromTemplateChildren(\n children: TemplateNode[],\n): { message: string; hasElements: boolean } {\n let elementIndex = 0\n let hasElements = false\n\n const parts = children.map((child) => {\n if (child.type === TEXT_NODE) {\n return (child.content ?? '').trim() ? child.content ?? '' : ''\n }\n if (child.type === ELEMENT_NODE && child.tag) {\n hasElements = true\n const idx = elementIndex++\n const innerText = extractRichTextFromTemplateChildren(child.children ?? []).message\n return `<${idx}>${innerText}</${idx}>`\n }\n return ''\n })\n\n return {\n message: parts.join('').trim(),\n hasElements,\n }\n}\n\nfunction getPropName(prop: TemplateProp): string {\n if (typeof prop.name === 'string') return prop.name\n return prop.name.content\n}\n\nfunction extractTemplateInterpolations(\n content: string,\n filename: string,\n): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n const interpolationRegex = /\\{\\{([\\s\\S]*?)\\}\\}/g\n let match: RegExpExecArray | null\n\n while ((match = interpolationRegex.exec(content)) !== null) {\n const expression = match[1]?.trim()\n if (!expression) continue\n\n const extracted = extractFromTsx(expression, filename)\n if (extracted.length === 0) continue\n\n const lineOffset = content.slice(0, match.index).split('\\n').length - 1\n for (const msg of extracted) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n\n/** Extract messages from Vue SFC files */\nexport function extractFromVue(code: string, filename: string): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n\n const { descriptor } = parseSFC(code, { filename })\n\n if (descriptor.template?.ast) {\n walkTemplate(descriptor.template.ast as unknown as TemplateNode, filename, messages)\n }\n\n // Also extract t() function calls from raw template source\n // (picks up t('source text') in template expressions like {{ t('...') }})\n if (descriptor.template?.content) {\n const templateMessages = extractFromTsx(descriptor.template.content, filename)\n const templateLoc = descriptor.template.loc\n const lineOffset = templateLoc.start.line - 1\n const existingIds = new Set(messages.map((m) => m.id))\n for (const msg of templateMessages) {\n if (!existingIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n const interpolationMessages = extractTemplateInterpolations(descriptor.template.content, filename)\n for (const msg of interpolationMessages) {\n if (!existingIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n }\n\n if (descriptor.scriptSetup?.content) {\n const scriptMessages = extractFromTsx(descriptor.scriptSetup.content, filename)\n const scriptLoc = descriptor.scriptSetup.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n if (descriptor.script?.content) {\n const scriptMessages = extractFromTsx(descriptor.script.content, filename)\n const scriptLoc = descriptor.script.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n"],"mappings":"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"}