better-translation 0.1.4 → 0.1.5
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.
- package/dist/ai.d.mts +19 -0
- package/dist/ai.d.mts.map +1 -0
- package/dist/ai.mjs +60 -0
- package/dist/ai.mjs.map +1 -0
- package/dist/message-id-7Mx7G9xT.mjs.map +1 -1
- package/dist/react.mjs.map +1 -1
- package/dist/server.mjs.map +1 -1
- package/dist/vite.mjs.map +1 -1
- package/package.json +9 -4
package/dist/ai.d.mts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { c as TranslateFn } from "./types-DVtEiSW4.mjs";
|
|
2
|
+
import { generateText } from "ai";
|
|
3
|
+
|
|
4
|
+
//#region src/ai.d.ts
|
|
5
|
+
type AiModel = Parameters<typeof generateText>[0]["model"];
|
|
6
|
+
interface CreateAiTranslateOptions {
|
|
7
|
+
/** AI SDK model value. Defaults to a Vercel AI Gateway model string. */
|
|
8
|
+
model?: AiModel;
|
|
9
|
+
/** Primary translation brief for product, tone, glossary, or domain instructions. */
|
|
10
|
+
prompt?: string;
|
|
11
|
+
/** Maximum number of messages sent in a single translation request. */
|
|
12
|
+
batchSize?: number;
|
|
13
|
+
/** Optional temperature forwarded to the selected model provider. */
|
|
14
|
+
temperature?: number;
|
|
15
|
+
}
|
|
16
|
+
declare function createAiTranslate(options?: CreateAiTranslateOptions): TranslateFn;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { CreateAiTranslateOptions, createAiTranslate };
|
|
19
|
+
//# sourceMappingURL=ai.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai.d.mts","names":[],"sources":["../src/ai.ts"],"mappings":";;;;KAOK,OAAA,GAAU,UAAA,QAAkB,YAAA;AAAA,UAKhB,wBAAA;EALZ;EAOH,KAAA,GAAQ,OAAA;;EAER,MAAA;EAT2C;EAW3C,SAAA;EANuC;EAQvC,WAAA;AAAA;AAAA,iBAGc,iBAAA,CAAkB,OAAA,GAAS,wBAAA,GAAgC,WAAA"}
|
package/dist/ai.mjs
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/ai.ts
|
|
3
|
+
const DEFAULT_GATEWAY_MODEL = "openai/gpt-5.5";
|
|
4
|
+
const DEFAULT_BATCH_SIZE = 25;
|
|
5
|
+
const translationPayloadSchema = z.object({ translations: z.record(z.string(), z.string()) });
|
|
6
|
+
function createAiTranslate(options = {}) {
|
|
7
|
+
return async (messages, locale) => {
|
|
8
|
+
const result = {};
|
|
9
|
+
const batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
10
|
+
for (let index = 0; index < messages.length; index += batchSize) {
|
|
11
|
+
const batch = messages.slice(index, index + batchSize);
|
|
12
|
+
Object.assign(result, await translateBatch(batch, locale, options));
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async function translateBatch(messages, locale, options) {
|
|
18
|
+
const { translations } = await translateWithAi(messages, locale, options);
|
|
19
|
+
return Object.fromEntries(messages.map((message) => {
|
|
20
|
+
const translated = translations[message.id]?.trim();
|
|
21
|
+
return [message.id, translated || message.text];
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
async function translateWithAi(messages, locale, options) {
|
|
25
|
+
const { generateText, Output } = await import("ai");
|
|
26
|
+
const { output } = await generateText({
|
|
27
|
+
model: options.model ?? DEFAULT_GATEWAY_MODEL,
|
|
28
|
+
output: Output.object({ schema: translationPayloadSchema }),
|
|
29
|
+
system: createSystemPrompt(locale, options.prompt),
|
|
30
|
+
prompt: createUserPrompt(messages, locale),
|
|
31
|
+
...options.temperature === void 0 ? {} : { temperature: options.temperature }
|
|
32
|
+
});
|
|
33
|
+
return output;
|
|
34
|
+
}
|
|
35
|
+
function createSystemPrompt(locale, prompt) {
|
|
36
|
+
return [`## Translation Brief
|
|
37
|
+
${prompt?.trim() || "Translate the provided UI messages as concise, natural application UI copy."}
|
|
38
|
+
|
|
39
|
+
## Target Locale
|
|
40
|
+
${locale}
|
|
41
|
+
|
|
42
|
+
## Required Output Contract
|
|
43
|
+
Return translations keyed by message id in the requested structured output.
|
|
44
|
+
Use each message context when provided.
|
|
45
|
+
Do not add labels, explanations, markdown, or code fences.`].join("\n\n");
|
|
46
|
+
}
|
|
47
|
+
function createUserPrompt(messages, locale) {
|
|
48
|
+
return JSON.stringify({
|
|
49
|
+
targetLocale: locale,
|
|
50
|
+
messages: messages.map((message) => ({
|
|
51
|
+
id: message.id,
|
|
52
|
+
text: message.text,
|
|
53
|
+
context: message.meta.context
|
|
54
|
+
}))
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { createAiTranslate };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=ai.mjs.map
|
package/dist/ai.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai.mjs","names":[],"sources":["../src/ai.ts"],"sourcesContent":["import type { generateText } from \"ai\"\nimport { z } from \"zod\"\n\nimport type { TranslateFn, TranslateMessage } from \"./types.js\"\n\nconst DEFAULT_GATEWAY_MODEL = \"openai/gpt-5.5\"\nconst DEFAULT_BATCH_SIZE = 25\ntype AiModel = Parameters<typeof generateText>[0][\"model\"]\nconst translationPayloadSchema = z.object({\n translations: z.record(z.string(), z.string()),\n})\n\nexport interface CreateAiTranslateOptions {\n /** AI SDK model value. Defaults to a Vercel AI Gateway model string. */\n model?: AiModel\n /** Primary translation brief for product, tone, glossary, or domain instructions. */\n prompt?: string\n /** Maximum number of messages sent in a single translation request. */\n batchSize?: number\n /** Optional temperature forwarded to the selected model provider. */\n temperature?: number\n}\n\nexport function createAiTranslate(options: CreateAiTranslateOptions = {}): TranslateFn {\n return async (messages, locale) => {\n const result: Record<string, string> = {}\n const batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE\n\n for (let index = 0; index < messages.length; index += batchSize) {\n const batch = messages.slice(index, index + batchSize)\n Object.assign(result, await translateBatch(batch, locale, options))\n }\n\n return result\n }\n}\n\nasync function translateBatch(messages: TranslateMessage[], locale: string, options: CreateAiTranslateOptions) {\n const { translations } = await translateWithAi(messages, locale, options)\n\n return Object.fromEntries(\n messages.map((message) => {\n const translated = translations[message.id]?.trim()\n return [message.id, translated || message.text]\n }),\n )\n}\n\nasync function translateWithAi(messages: TranslateMessage[], locale: string, options: CreateAiTranslateOptions) {\n const { generateText, Output } = await import(\"ai\")\n const { output } = await generateText({\n model: options.model ?? DEFAULT_GATEWAY_MODEL,\n output: Output.object({ schema: translationPayloadSchema }),\n system: createSystemPrompt(locale, options.prompt),\n prompt: createUserPrompt(messages, locale),\n ...(options.temperature === undefined ? {} : { temperature: options.temperature }),\n })\n\n return output\n}\n\nfunction createSystemPrompt(locale: string, prompt?: string) {\n const translationBrief = prompt?.trim() || \"Translate the provided UI messages as concise, natural application UI copy.\"\n\n return [\n `## Translation Brief\n${translationBrief}\n\n## Target Locale\n${locale}\n\n## Required Output Contract\nReturn translations keyed by message id in the requested structured output.\nUse each message context when provided.\nDo not add labels, explanations, markdown, or code fences.`,\n ].join(\"\\n\\n\")\n}\n\nfunction createUserPrompt(messages: TranslateMessage[], locale: string) {\n return JSON.stringify({\n targetLocale: locale,\n messages: messages.map((message) => ({\n id: message.id,\n text: message.text,\n context: message.meta.context,\n })),\n })\n}\n"],"mappings":";;AAKA,MAAM,wBAAwB;AAC9B,MAAM,qBAAqB;AAE3B,MAAM,2BAA2B,EAAE,OAAO,EACxC,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,EAC/C,CAAC;AAaF,SAAgB,kBAAkB,UAAoC,EAAE,EAAe;CACrF,OAAO,OAAO,UAAU,WAAW;EACjC,MAAM,SAAiC,EAAE;EACzC,MAAM,YAAY,QAAQ,aAAa;EAEvC,KAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS,WAAW;GAC/D,MAAM,QAAQ,SAAS,MAAM,OAAO,QAAQ,UAAU;GACtD,OAAO,OAAO,QAAQ,MAAM,eAAe,OAAO,QAAQ,QAAQ,CAAC;;EAGrE,OAAO;;;AAIX,eAAe,eAAe,UAA8B,QAAgB,SAAmC;CAC7G,MAAM,EAAE,iBAAiB,MAAM,gBAAgB,UAAU,QAAQ,QAAQ;CAEzE,OAAO,OAAO,YACZ,SAAS,KAAK,YAAY;EACxB,MAAM,aAAa,aAAa,QAAQ,KAAK,MAAM;EACnD,OAAO,CAAC,QAAQ,IAAI,cAAc,QAAQ,KAAK;GAC/C,CACH;;AAGH,eAAe,gBAAgB,UAA8B,QAAgB,SAAmC;CAC9G,MAAM,EAAE,cAAc,WAAW,MAAM,OAAO;CAC9C,MAAM,EAAE,WAAW,MAAM,aAAa;EACpC,OAAO,QAAQ,SAAS;EACxB,QAAQ,OAAO,OAAO,EAAE,QAAQ,0BAA0B,CAAC;EAC3D,QAAQ,mBAAmB,QAAQ,QAAQ,OAAO;EAClD,QAAQ,iBAAiB,UAAU,OAAO;EAC1C,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,GAAG,EAAE,aAAa,QAAQ,aAAa;EAClF,CAAC;CAEF,OAAO;;AAGT,SAAS,mBAAmB,QAAgB,QAAiB;CAG3D,OAAO,CACL;EAHuB,QAAQ,MAAM,IAAI,8EAI1B;;;EAGjB,OAAO;;;;;4DAMN,CAAC,KAAK,OAAO;;AAGhB,SAAS,iBAAiB,UAA8B,QAAgB;CACtE,OAAO,KAAK,UAAU;EACpB,cAAc;EACd,UAAU,SAAS,KAAK,aAAa;GACnC,IAAI,QAAQ;GACZ,MAAM,QAAQ;GACd,SAAS,QAAQ,KAAK;GACvB,EAAE;EACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-id-7Mx7G9xT.mjs","names":[],"sources":["../src/message-id.ts"],"sourcesContent":["import type { TranslateOptions } from \"./types.js\"\n\nexport function normalizeMeta(meta?: TranslateOptions): TranslateOptions {\n if (!meta) return {}\n\n return Object.fromEntries(\n Object.entries(meta)\n .filter(([key, value]) => key !== \"id\" && value !== undefined)\n .sort(([a], [b]) => a.localeCompare(b)),\n ) as TranslateOptions\n}\n\nexport function serializeMeta(meta?: TranslateOptions) {\n if (!meta) return \"\"\n\n const normalized = normalizeMeta(meta)\n return Object.keys(normalized).length > 0 ? JSON.stringify(normalized) : \"\"\n}\n\nexport function getMessageIdentity(message: string, meta?: TranslateOptions) {\n const serializedMeta = serializeMeta(meta)\n return serializedMeta ? `${message}\\0${serializedMeta}` : message\n}\n\n/** Generates the stable hashed id used to store and look up a translated message. */\nexport function getMessageId(message: string, meta?: TranslateOptions) {\n const value = getMessageIdentity(message, meta)\n let hash = 2166136261\n\n for (let i = 0; i < value.length; i++) {\n hash ^= value.charCodeAt(i)\n hash = Math.imul(hash, 16777619)\n }\n\n return `m_${(hash >>> 0).toString(36)}`\n}\n\n/** Resolves the lookup id for function-style `t()` calls, preferring an explicit `options.id`. */\nexport function getCallMessageId(message: string, options?: TranslateOptions) {\n return options?.id ?? getMessageId(message, options)\n}\n"],"mappings":";AAEA,SAAgB,cAAc,MAA2C;
|
|
1
|
+
{"version":3,"file":"message-id-7Mx7G9xT.mjs","names":[],"sources":["../src/message-id.ts"],"sourcesContent":["import type { TranslateOptions } from \"./types.js\"\n\nexport function normalizeMeta(meta?: TranslateOptions): TranslateOptions {\n if (!meta) return {}\n\n return Object.fromEntries(\n Object.entries(meta)\n .filter(([key, value]) => key !== \"id\" && value !== undefined)\n .sort(([a], [b]) => a.localeCompare(b)),\n ) as TranslateOptions\n}\n\nexport function serializeMeta(meta?: TranslateOptions) {\n if (!meta) return \"\"\n\n const normalized = normalizeMeta(meta)\n return Object.keys(normalized).length > 0 ? JSON.stringify(normalized) : \"\"\n}\n\nexport function getMessageIdentity(message: string, meta?: TranslateOptions) {\n const serializedMeta = serializeMeta(meta)\n return serializedMeta ? `${message}\\0${serializedMeta}` : message\n}\n\n/** Generates the stable hashed id used to store and look up a translated message. */\nexport function getMessageId(message: string, meta?: TranslateOptions) {\n const value = getMessageIdentity(message, meta)\n let hash = 2166136261\n\n for (let i = 0; i < value.length; i++) {\n hash ^= value.charCodeAt(i)\n hash = Math.imul(hash, 16777619)\n }\n\n return `m_${(hash >>> 0).toString(36)}`\n}\n\n/** Resolves the lookup id for function-style `t()` calls, preferring an explicit `options.id`. */\nexport function getCallMessageId(message: string, options?: TranslateOptions) {\n return options?.id ?? getMessageId(message, options)\n}\n"],"mappings":";AAEA,SAAgB,cAAc,MAA2C;CACvE,IAAI,CAAC,MAAM,OAAO,EAAE;CAEpB,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,CACjB,QAAQ,CAAC,KAAK,WAAW,QAAQ,QAAQ,UAAU,KAAA,EAAU,CAC7D,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,CAC1C;;AAGH,SAAgB,cAAc,MAAyB;CACrD,IAAI,CAAC,MAAM,OAAO;CAElB,MAAM,aAAa,cAAc,KAAK;CACtC,OAAO,OAAO,KAAK,WAAW,CAAC,SAAS,IAAI,KAAK,UAAU,WAAW,GAAG;;AAG3E,SAAgB,mBAAmB,SAAiB,MAAyB;CAC3E,MAAM,iBAAiB,cAAc,KAAK;CAC1C,OAAO,iBAAiB,GAAG,QAAQ,IAAI,mBAAmB;;;AAI5D,SAAgB,aAAa,SAAiB,MAAyB;CACrE,MAAM,QAAQ,mBAAmB,SAAS,KAAK;CAC/C,IAAI,OAAO;CAEX,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,QAAQ,MAAM,WAAW,EAAE;EAC3B,OAAO,KAAK,KAAK,MAAM,SAAS;;CAGlC,OAAO,MAAM,SAAS,GAAG,SAAS,GAAG;;;AAIvC,SAAgB,iBAAiB,SAAiB,SAA4B;CAC5E,OAAO,SAAS,MAAM,aAAa,SAAS,QAAQ"}
|
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.mjs","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import { Children, createContext, isValidElement, useContext, useMemo, type ReactNode } from \"react\"\n\nimport type { TranslateOptions } from \"./types.js\"\n\nimport { getCallMessageId, getMessageId } from \"./message-id.js\"\n\ninterface TranslateContextValue {\n messages: Record<string, string>\n}\n\nconst TranslateContext = createContext<TranslateContextValue>({ messages: {} })\n\n/** Props for `TranslateProvider`. */\nexport interface TranslateProviderProps {\n /** Flattened locale message map keyed by stable message id. */\n messages: Record<string, string>\n /** React subtree that should have access to translations. */\n children: ReactNode\n}\n\n/** Provides translated messages to React components below it. */\nexport function TranslateProvider({ messages, children }: TranslateProviderProps) {\n const value = useMemo(() => ({ messages }), [messages])\n return <TranslateContext.Provider value={value}>{children}</TranslateContext.Provider>\n}\n\n/** Returns the raw locale message map from the current provider. */\nexport function useMessages() {\n return useContext(TranslateContext).messages\n}\n\ntype MessageValues = Record<string, unknown>\ntype TranslateFn = (message: string, valuesOrOptions?: MessageValues | TranslateOptions, options?: TranslateOptions) => string\n\n/** Returns a translator function for text used in props, labels, and other non-JSX positions. */\nexport function useT(): TranslateFn {\n const { messages } = useContext(TranslateContext)\n return useMemo<TranslateFn>(\n () => (message, valuesOrOptions, options) => {\n const values = isTranslateOptions(valuesOrOptions) ? undefined : normalizeValues(valuesOrOptions)\n const resolvedOptions = isTranslateOptions(valuesOrOptions) ? valuesOrOptions : options\n const template = messages[getCallMessageId(message, resolvedOptions)] ?? message\n if (!values) return template\n return template.replace(/\\{(\\w+)\\}/g, (_, name: string) => values[name] ?? `{${name}}`)\n },\n [messages],\n )\n}\n\n/** Props for `Var`. */\nexport type VarProps = {\n /** Optional shorthand child form, normalized at build time for simple identifiers. */\n children?: ReactNode\n} & Record<string, ReactNode | undefined>\n\n/** Marks a runtime value for placeholder interpolation inside `<T>` content. */\nexport function Var(props: VarProps) {\n return <>{getRuntimeVarEntry(props)?.value ?? props.children}</>\n}\n\n/** Props for `T`. */\nexport interface TProps {\n /** Explicit stable id to use instead of hashing the rendered source text, whether provided manually or by a transform. */\n id?: string\n /** Extra disambiguating context for translators and custom grouping. */\n context?: string\n /** Source-language JSX content to translate. */\n children?: ReactNode\n}\n\n/** Renders translated JSX content and supports placeholders through `<Var>`. */\nexport function T({ id, context, children }: TProps) {\n const { messages } = useContext(TranslateContext)\n const resolvedMeta = context ? { context } : undefined\n const runtimeContent = useMemo(() => extractRuntimeContent(children), [children])\n const template = messages[id ?? (runtimeContent.message ? getMessageId(runtimeContent.message, resolvedMeta) : \"\")]\n const vars = template?.includes(\"{\") ? runtimeContent.vars : undefined\n const interpolated = useMemo(() => interpolate(template, vars), [template, vars])\n\n if (!template) return <>{children}</>\n if (!vars) return <>{template}</>\n if (!interpolated.length) return <>{children}</>\n return <>{interpolated}</>\n}\n\nfunction interpolate(template?: string, vars?: Record<string, ReactNode>): ReactNode[] {\n if (!template || !vars) return []\n const result: ReactNode[] = []\n const re = /\\{(\\w+)\\}/g\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n while ((match = re.exec(template)) !== null) {\n if (match.index > lastIndex) result.push(template.slice(lastIndex, match.index))\n result.push(vars[match[1]!] ?? `{${match[1]}}`)\n lastIndex = re.lastIndex\n }\n\n if (lastIndex < template.length) result.push(template.slice(lastIndex))\n return result\n}\n\nfunction extractRuntimeContent(children: ReactNode) {\n const parts: string[] = []\n const vars: Record<string, ReactNode> = {}\n\n Children.forEach(children, (child) => {\n if (typeof child === \"string\" || typeof child === \"number\") {\n parts.push(String(child))\n return\n }\n\n if (isValidElement<VarProps>(child) && child.type === Var) {\n const entry = getRuntimeVarEntry(child.props)\n if (entry) {\n parts.push(`{${entry.name}}`)\n vars[entry.name] = entry.value\n }\n }\n })\n\n return {\n message: parts.join(\"\").replace(/\\s+/g, \" \").trim(),\n vars: Object.keys(vars).length > 0 ? vars : undefined,\n }\n}\n\nfunction getRuntimeVarEntry(props: VarProps) {\n if (typeof props.name === \"string\" && props.children !== undefined) {\n return { name: props.name, value: props.children }\n }\n\n const entries = Object.entries(props).filter(([key]) => key !== \"children\")\n if (entries.length !== 1) return undefined\n\n const [name, value] = entries[0]!\n return { name, value }\n}\n\nfunction isTranslateOptions(value?: MessageValues | TranslateOptions): value is TranslateOptions {\n if (!value || Array.isArray(value)) return false\n return Object.keys(value).every((key) => key === \"id\" || key === \"context\")\n}\n\nfunction normalizeValues(values?: MessageValues) {\n if (!values) return undefined\n const entries = Object.entries(values).map(([name, value]) => [name, String(value)] as const)\n return entries.length > 0 ? Object.fromEntries(entries) : undefined\n}\n"],"mappings":";;;;AAUA,MAAM,mBAAmB,cAAqC,EAAE,UAAU,EAAE,EAAE,CAAC;;AAW/E,SAAgB,kBAAkB,EAAE,UAAU,YAAoC;CAChF,MAAM,QAAQ,eAAe,EAAE,UAAU,GAAG,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"react.mjs","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import { Children, createContext, isValidElement, useContext, useMemo, type ReactNode } from \"react\"\n\nimport type { TranslateOptions } from \"./types.js\"\n\nimport { getCallMessageId, getMessageId } from \"./message-id.js\"\n\ninterface TranslateContextValue {\n messages: Record<string, string>\n}\n\nconst TranslateContext = createContext<TranslateContextValue>({ messages: {} })\n\n/** Props for `TranslateProvider`. */\nexport interface TranslateProviderProps {\n /** Flattened locale message map keyed by stable message id. */\n messages: Record<string, string>\n /** React subtree that should have access to translations. */\n children: ReactNode\n}\n\n/** Provides translated messages to React components below it. */\nexport function TranslateProvider({ messages, children }: TranslateProviderProps) {\n const value = useMemo(() => ({ messages }), [messages])\n return <TranslateContext.Provider value={value}>{children}</TranslateContext.Provider>\n}\n\n/** Returns the raw locale message map from the current provider. */\nexport function useMessages() {\n return useContext(TranslateContext).messages\n}\n\ntype MessageValues = Record<string, unknown>\ntype TranslateFn = (message: string, valuesOrOptions?: MessageValues | TranslateOptions, options?: TranslateOptions) => string\n\n/** Returns a translator function for text used in props, labels, and other non-JSX positions. */\nexport function useT(): TranslateFn {\n const { messages } = useContext(TranslateContext)\n return useMemo<TranslateFn>(\n () => (message, valuesOrOptions, options) => {\n const values = isTranslateOptions(valuesOrOptions) ? undefined : normalizeValues(valuesOrOptions)\n const resolvedOptions = isTranslateOptions(valuesOrOptions) ? valuesOrOptions : options\n const template = messages[getCallMessageId(message, resolvedOptions)] ?? message\n if (!values) return template\n return template.replace(/\\{(\\w+)\\}/g, (_, name: string) => values[name] ?? `{${name}}`)\n },\n [messages],\n )\n}\n\n/** Props for `Var`. */\nexport type VarProps = {\n /** Optional shorthand child form, normalized at build time for simple identifiers. */\n children?: ReactNode\n} & Record<string, ReactNode | undefined>\n\n/** Marks a runtime value for placeholder interpolation inside `<T>` content. */\nexport function Var(props: VarProps) {\n return <>{getRuntimeVarEntry(props)?.value ?? props.children}</>\n}\n\n/** Props for `T`. */\nexport interface TProps {\n /** Explicit stable id to use instead of hashing the rendered source text, whether provided manually or by a transform. */\n id?: string\n /** Extra disambiguating context for translators and custom grouping. */\n context?: string\n /** Source-language JSX content to translate. */\n children?: ReactNode\n}\n\n/** Renders translated JSX content and supports placeholders through `<Var>`. */\nexport function T({ id, context, children }: TProps) {\n const { messages } = useContext(TranslateContext)\n const resolvedMeta = context ? { context } : undefined\n const runtimeContent = useMemo(() => extractRuntimeContent(children), [children])\n const template = messages[id ?? (runtimeContent.message ? getMessageId(runtimeContent.message, resolvedMeta) : \"\")]\n const vars = template?.includes(\"{\") ? runtimeContent.vars : undefined\n const interpolated = useMemo(() => interpolate(template, vars), [template, vars])\n\n if (!template) return <>{children}</>\n if (!vars) return <>{template}</>\n if (!interpolated.length) return <>{children}</>\n return <>{interpolated}</>\n}\n\nfunction interpolate(template?: string, vars?: Record<string, ReactNode>): ReactNode[] {\n if (!template || !vars) return []\n const result: ReactNode[] = []\n const re = /\\{(\\w+)\\}/g\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n while ((match = re.exec(template)) !== null) {\n if (match.index > lastIndex) result.push(template.slice(lastIndex, match.index))\n result.push(vars[match[1]!] ?? `{${match[1]}}`)\n lastIndex = re.lastIndex\n }\n\n if (lastIndex < template.length) result.push(template.slice(lastIndex))\n return result\n}\n\nfunction extractRuntimeContent(children: ReactNode) {\n const parts: string[] = []\n const vars: Record<string, ReactNode> = {}\n\n Children.forEach(children, (child) => {\n if (typeof child === \"string\" || typeof child === \"number\") {\n parts.push(String(child))\n return\n }\n\n if (isValidElement<VarProps>(child) && child.type === Var) {\n const entry = getRuntimeVarEntry(child.props)\n if (entry) {\n parts.push(`{${entry.name}}`)\n vars[entry.name] = entry.value\n }\n }\n })\n\n return {\n message: parts.join(\"\").replace(/\\s+/g, \" \").trim(),\n vars: Object.keys(vars).length > 0 ? vars : undefined,\n }\n}\n\nfunction getRuntimeVarEntry(props: VarProps) {\n if (typeof props.name === \"string\" && props.children !== undefined) {\n return { name: props.name, value: props.children }\n }\n\n const entries = Object.entries(props).filter(([key]) => key !== \"children\")\n if (entries.length !== 1) return undefined\n\n const [name, value] = entries[0]!\n return { name, value }\n}\n\nfunction isTranslateOptions(value?: MessageValues | TranslateOptions): value is TranslateOptions {\n if (!value || Array.isArray(value)) return false\n return Object.keys(value).every((key) => key === \"id\" || key === \"context\")\n}\n\nfunction normalizeValues(values?: MessageValues) {\n if (!values) return undefined\n const entries = Object.entries(values).map(([name, value]) => [name, String(value)] as const)\n return entries.length > 0 ? Object.fromEntries(entries) : undefined\n}\n"],"mappings":"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"}
|
package/dist/server.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","names":[],"sources":["../src/server.ts"],"sourcesContent":["import type { TranslateOptions } from \"./types.js\"\n\nimport { getCallMessageId } from \"./message-id.js\"\n\ntype MessageValues = Record<string, unknown>\nexport type ServerTranslator = (\n message: string,\n valuesOrOptions?: MessageValues | TranslateOptions,\n options?: TranslateOptions,\n) => string\n\n/** Creates a lightweight server-side translator from a loaded message map. */\nexport function createTranslator(messages: Record<string, string>): ServerTranslator {\n return function t(message: string, valuesOrOptions?: MessageValues | TranslateOptions, options?: TranslateOptions) {\n const values = isTranslateOptions(valuesOrOptions) ? undefined : normalizeValues(valuesOrOptions)\n const resolvedOptions = isTranslateOptions(valuesOrOptions) ? valuesOrOptions : options\n const template = messages[getCallMessageId(message, resolvedOptions)] ?? message\n\n if (!values) return template\n\n return template.replace(/\\{(\\w+)\\}/g, (_, name: string) => values[name] ?? `{${name}}`)\n }\n}\n\nfunction isTranslateOptions(value?: MessageValues | TranslateOptions): value is TranslateOptions {\n if (!value || Array.isArray(value)) return false\n return Object.keys(value).every((key) => key === \"id\" || key === \"context\")\n}\n\nfunction normalizeValues(values?: MessageValues) {\n if (!values) return undefined\n\n const entries = Object.entries(values).map(([name, value]) => [name, String(value)])\n return entries.length > 0 ? Object.fromEntries(entries) : undefined\n}\n"],"mappings":";;;AAYA,SAAgB,iBAAiB,UAAoD;
|
|
1
|
+
{"version":3,"file":"server.mjs","names":[],"sources":["../src/server.ts"],"sourcesContent":["import type { TranslateOptions } from \"./types.js\"\n\nimport { getCallMessageId } from \"./message-id.js\"\n\ntype MessageValues = Record<string, unknown>\nexport type ServerTranslator = (\n message: string,\n valuesOrOptions?: MessageValues | TranslateOptions,\n options?: TranslateOptions,\n) => string\n\n/** Creates a lightweight server-side translator from a loaded message map. */\nexport function createTranslator(messages: Record<string, string>): ServerTranslator {\n return function t(message: string, valuesOrOptions?: MessageValues | TranslateOptions, options?: TranslateOptions) {\n const values = isTranslateOptions(valuesOrOptions) ? undefined : normalizeValues(valuesOrOptions)\n const resolvedOptions = isTranslateOptions(valuesOrOptions) ? valuesOrOptions : options\n const template = messages[getCallMessageId(message, resolvedOptions)] ?? message\n\n if (!values) return template\n\n return template.replace(/\\{(\\w+)\\}/g, (_, name: string) => values[name] ?? `{${name}}`)\n }\n}\n\nfunction isTranslateOptions(value?: MessageValues | TranslateOptions): value is TranslateOptions {\n if (!value || Array.isArray(value)) return false\n return Object.keys(value).every((key) => key === \"id\" || key === \"context\")\n}\n\nfunction normalizeValues(values?: MessageValues) {\n if (!values) return undefined\n\n const entries = Object.entries(values).map(([name, value]) => [name, String(value)])\n return entries.length > 0 ? Object.fromEntries(entries) : undefined\n}\n"],"mappings":";;;AAYA,SAAgB,iBAAiB,UAAoD;CACnF,OAAO,SAAS,EAAE,SAAiB,iBAAoD,SAA4B;EACjH,MAAM,SAAS,mBAAmB,gBAAgB,GAAG,KAAA,IAAY,gBAAgB,gBAAgB;EAEjG,MAAM,WAAW,SAAS,iBAAiB,SADnB,mBAAmB,gBAAgB,GAAG,kBAAkB,QACZ,KAAK;EAEzE,IAAI,CAAC,QAAQ,OAAO;EAEpB,OAAO,SAAS,QAAQ,eAAe,GAAG,SAAiB,OAAO,SAAS,IAAI,KAAK,GAAG;;;AAI3F,SAAS,mBAAmB,OAAqE;CAC/F,IAAI,CAAC,SAAS,MAAM,QAAQ,MAAM,EAAE,OAAO;CAC3C,OAAO,OAAO,KAAK,MAAM,CAAC,OAAO,QAAQ,QAAQ,QAAQ,QAAQ,UAAU;;AAG7E,SAAS,gBAAgB,QAAwB;CAC/C,IAAI,CAAC,QAAQ,OAAO,KAAA;CAEpB,MAAM,UAAU,OAAO,QAAQ,OAAO,CAAC,KAAK,CAAC,MAAM,WAAW,CAAC,MAAM,OAAO,MAAM,CAAC,CAAC;CACpF,OAAO,QAAQ,SAAS,IAAI,OAAO,YAAY,QAAQ,GAAG,KAAA"}
|
package/dist/vite.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.mjs","names":[],"sources":["../src/cache.ts","../src/extractor.ts","../src/runtime-config.ts","../src/plugin.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { dirname } from \"node:path\"\n\nimport type { TranslationCache } from \"./types.js\"\n\nconst CURRENT_VERSION = 1\n\nexport function createEmptyCache(): TranslationCache {\n return { version: CURRENT_VERSION, entries: {} }\n}\n\n/** Loads the translation cache from disk, resetting it when the schema version changes. */\nexport function loadCache(path: string): TranslationCache {\n if (!existsSync(path)) return createEmptyCache()\n try {\n const data = JSON.parse(readFileSync(path, \"utf-8\")) as TranslationCache\n if (data.version !== CURRENT_VERSION) return createEmptyCache()\n return data\n } catch {\n return createEmptyCache()\n }\n}\n\n/** Persists the translation cache so future runs can reuse existing translations. */\nexport function saveCache(path: string, cache: TranslationCache) {\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n const next = JSON.stringify(cache, null, 2)\n if (existsSync(path) && readFileSync(path, \"utf-8\") === next) return\n writeFileSync(path, next)\n}\n\n/** Builds the cache key used to distinguish translations by stable message id and locale. */\nexport function getCacheKey(messageId: string, locale: string) {\n return `${messageId}\\0${locale}`\n}\n","import type { Argument, JSXChild, StringLiteral } from \"oxc-parser\"\nimport { parseSync, Visitor } from \"oxc-parser\"\n\nimport type { ExtractedMessage, MessageSource, TranslateOptions } from \"./types.js\"\n\nimport { getCallMessageId, getMessageId } from \"./message-id.js\"\n\ninterface Markers {\n call: string[]\n component: string[]\n logging: boolean\n}\n\nexport interface SourceEdit {\n start: number\n end: number\n replacement: string\n}\n\nexport interface SourceAnalysis {\n parsed: boolean\n messages: ExtractedMessage[]\n edits: SourceEdit[]\n}\n\n/** Extracts messages and source edits from a file in one coordinated parse pass. */\nexport function analyzeSourceFile(code: string, filename: string, markers: Markers): SourceAnalysis {\n const messages: ExtractedMessage[] = []\n const edits: SourceEdit[] = []\n const result = parseSync(filename, code)\n if (result.errors.length > 0) return { parsed: false, messages, edits }\n const lineStarts = getLineStarts(code)\n\n const visitor = new Visitor({\n CallExpression(node) {\n if (\n node.callee.type === \"Identifier\" &&\n markers.call.includes(node.callee.name) &&\n node.arguments.length >= 1 &&\n isStringLiteral(node.arguments[0]!)\n ) {\n const value = (node.arguments[0] as StringLiteral).value\n const meta = getCallMetaArgument(node.arguments)\n const id = getCallMessageId(value, meta)\n messages.push({\n id,\n defaultMessage: value,\n meta: meta ?? {},\n placeholders: extractPlaceholdersFromMessage(value),\n source: createSource({\n filename,\n lineStarts,\n marker: node.callee.name,\n kind: \"call\",\n start: node.start,\n end: node.end,\n }),\n })\n\n const callOptionsEdit = createCallOptionsEdit(code, node.arguments, id)\n if (callOptionsEdit) edits.push(callOptionsEdit)\n }\n },\n\n JSXElement(node) {\n const opening = node.openingElement\n if (\n opening.name.type === \"JSXIdentifier\" &&\n opening.name.name === \"Var\" &&\n (opening.attributes as Array<unknown>).length === 0\n ) {\n const identifier = getVarChildIdentifier(node.children)\n if (identifier) {\n edits.push({\n start: node.start,\n end: node.end,\n replacement: `<Var ${identifier}={${identifier}} />`,\n })\n }\n }\n\n if (opening.name.type !== \"JSXIdentifier\") return\n if (!markers.component.includes(opening.name.name)) return\n\n const extraction = extractJSXChildren(node.children)\n if (!extraction.valid) {\n if (markers.logging) {\n console.warn(`[better-translation] Non-static <${opening.name.name}> in ${filename}, skipping`)\n }\n return\n }\n\n const context = getJSXStringAttribute(opening.attributes, \"context\")\n const meta = context ? { context } : undefined\n const id = getJSXStringAttribute(opening.attributes, \"id\") ?? getMessageId(extraction.message, meta)\n messages.push({\n id,\n defaultMessage: extraction.message,\n meta: meta ?? {},\n placeholders: extraction.placeholders,\n source: createSource({\n filename,\n lineStarts,\n marker: opening.name.name,\n kind: \"component\",\n start: node.start,\n end: node.end,\n }),\n })\n\n if (!hasJSXAttribute(opening.attributes as Array<unknown>, \"id\")) {\n edits.push({\n start: opening.name.end,\n end: opening.name.end,\n replacement: ` id=\"${id}\"`,\n })\n }\n },\n })\n\n visitor.visit(result.program)\n return { parsed: true, messages, edits }\n}\n\nfunction isStringLiteral(node: Argument): node is StringLiteral {\n return node.type === \"Literal\" && typeof (node as StringLiteral).value === \"string\"\n}\n\nfunction getMetaArgument(node?: Argument) {\n if (!node) return undefined\n if (isStringLiteral(node)) return { context: node.value }\n\n if (node.type !== \"ObjectExpression\") return undefined\n\n const meta: TranslateOptions = {}\n\n for (const property of node.properties as Array<{\n type: string\n key?: { type: string; name?: string; value?: unknown }\n value?: Argument\n }>) {\n if (\n (property.type === \"ObjectProperty\" || property.type === \"Property\") &&\n ((property.key?.type === \"Identifier\" && (property.key.name === \"context\" || property.key.name === \"id\")) ||\n (property.key?.type === \"Literal\" && (property.key.value === \"context\" || property.key.value === \"id\"))) &&\n property.value &&\n isStringLiteral(property.value)\n ) {\n const key = property.key?.type === \"Identifier\" ? property.key.name : property.key?.value\n if (key === \"context\" || key === \"id\") meta[key] = property.value.value\n }\n }\n\n return Object.keys(meta).length > 0 ? meta : undefined\n}\n\nfunction getCallMetaArgument(args: Argument[]) {\n return getMetaArgument(isTranslateOptionsArgument(args[1]) ? args[1] : args[2])\n}\n\nfunction createCallOptionsEdit(code: string, args: Argument[], id: string) {\n const valuesArg = args[1]\n const optionsArg = isTranslateOptionsArgument(valuesArg) ? valuesArg : args[2]\n\n if (!optionsArg) {\n return {\n start: (valuesArg ?? args[0]!).end,\n end: (valuesArg ?? args[0]!).end,\n replacement: `, { id: ${JSON.stringify(id)} }`,\n }\n }\n\n if (isStringLiteral(optionsArg)) {\n const contextValue = code.slice(optionsArg.start, optionsArg.end)\n return {\n start: optionsArg.start,\n end: optionsArg.end,\n replacement: `{ id: ${JSON.stringify(id)}, context: ${contextValue} }`,\n }\n }\n\n if (optionsArg.type !== \"ObjectExpression\") return undefined\n if (hasObjectProperty(optionsArg, \"id\")) return undefined\n\n const objectSource = code.slice(optionsArg.start, optionsArg.end)\n const innerSource = objectSource.slice(1, -1)\n const replacement =\n innerSource.trim().length > 0 ? `{ id: ${JSON.stringify(id)},${innerSource} }` : `{ id: ${JSON.stringify(id)} }`\n\n return {\n start: optionsArg.start,\n end: optionsArg.end,\n replacement,\n }\n}\n\nfunction isTranslateOptionsArgument(node?: Argument) {\n if (!node) return false\n if (isStringLiteral(node)) return true\n if (node.type !== \"ObjectExpression\") return false\n\n return (node.properties as Array<unknown>).every((entry) => {\n const property = entry as\n | {\n type?: string\n key?: { type?: string; name?: string; value?: unknown }\n }\n | undefined\n\n if (property?.type !== \"ObjectProperty\" && property?.type !== \"Property\") return false\n\n return (\n (property.key?.type === \"Identifier\" && (property.key.name === \"context\" || property.key.name === \"id\")) ||\n (property.key?.type === \"Literal\" && (property.key.value === \"context\" || property.key.value === \"id\"))\n )\n })\n}\n\nfunction getJSXStringAttribute(attributes: Array<unknown>, name: string) {\n for (const attr of attributes as Array<{\n type: string\n name?: { type: string; name?: string }\n value?: { type: string; value?: unknown } | null\n }>) {\n if (\n attr.type === \"JSXAttribute\" &&\n attr.name?.type === \"JSXIdentifier\" &&\n attr.name.name === name &&\n attr.value?.type === \"Literal\" &&\n typeof attr.value.value === \"string\"\n ) {\n return attr.value.value as string\n }\n }\n}\n\nfunction hasJSXAttribute(attributes: Array<unknown>, name: string) {\n return attributes.some((attr) => {\n const attribute = attr as\n | {\n type?: string\n name?: { type?: string; name?: string }\n }\n | undefined\n\n return attribute?.type === \"JSXAttribute\" && attribute.name?.type === \"JSXIdentifier\" && attribute.name.name === name\n })\n}\n\nfunction hasObjectProperty(node: { properties: Array<unknown> }, name: string) {\n return node.properties.some((entry) => {\n const property = entry as\n | {\n type?: string\n key?: { type?: string; name?: string; value?: unknown }\n }\n | undefined\n\n if (property?.type !== \"ObjectProperty\" && property?.type !== \"Property\") return false\n return (\n (property.key?.type === \"Identifier\" && property.key.name === name) ||\n (property.key?.type === \"Literal\" && property.key.value === name)\n )\n })\n}\n\nfunction extractPlaceholdersFromMessage(message: string) {\n const names = new Set<string>()\n for (const match of message.matchAll(/\\{(\\w+)\\}/g)) {\n if (match[1]) names.add(match[1])\n }\n return [...names]\n}\n\nfunction createSource({\n filename,\n lineStarts,\n marker,\n kind,\n start,\n end,\n}: {\n filename: string\n lineStarts: number[]\n marker: string\n kind: MessageSource[\"kind\"]\n start: number\n end: number\n}): MessageSource {\n const startPosition = getPosition(start, lineStarts)\n const endPosition = getPosition(end, lineStarts)\n\n return {\n file: filename,\n kind,\n marker,\n line: startPosition.line,\n column: startPosition.column,\n endLine: endPosition.line,\n endColumn: endPosition.column,\n start,\n end,\n }\n}\n\nfunction getLineStarts(code: string) {\n const starts = [0]\n for (let i = 0; i < code.length; i++) {\n if (code[i] === \"\\n\") starts.push(i + 1)\n }\n return starts\n}\n\nfunction getPosition(offset: number, lineStarts: number[]) {\n let low = 0\n let high = lineStarts.length - 1\n\n while (low <= high) {\n const mid = Math.floor((low + high) / 2)\n const lineStart = lineStarts[mid]!\n const nextLineStart = lineStarts[mid + 1] ?? Number.POSITIVE_INFINITY\n\n if (offset < lineStart) {\n high = mid - 1\n continue\n }\n\n if (offset >= nextLineStart) {\n low = mid + 1\n continue\n }\n\n return { line: mid + 1, column: offset - lineStart + 1 }\n }\n\n const lastLine = lineStarts.length - 1\n const lastStart = lineStarts[lastLine] ?? 0\n return { line: lastLine + 1, column: offset - lastStart + 1 }\n}\n\ninterface ExtractionResult {\n message: string\n placeholders: string[]\n valid: boolean\n}\n\nfunction extractJSXChildren(children: Array<JSXChild>): ExtractionResult {\n const parts: string[] = []\n const placeholders: string[] = []\n\n for (const child of children) {\n switch (child.type) {\n case \"JSXText\":\n parts.push(child.value)\n break\n\n case \"JSXElement\": {\n const name = child.openingElement.name\n if (name.type !== \"JSXIdentifier\" || name.name !== \"Var\") {\n return { message: \"\", placeholders: [], valid: false }\n }\n\n const varName = getVarPlaceholderName(child)\n if (!varName) return { message: \"\", placeholders: [], valid: false }\n\n placeholders.push(varName)\n parts.push(`{${varName}}`)\n break\n }\n\n case \"JSXExpressionContainer\":\n if (child.expression.type !== \"JSXEmptyExpression\") {\n return { message: \"\", placeholders: [], valid: false }\n }\n break\n\n default:\n break\n }\n }\n\n const message = parts.join(\"\").replace(/\\s+/g, \" \").trim()\n return { message, placeholders, valid: message.length > 0 }\n}\n\nfunction getVarPlaceholderName(node: { openingElement: { attributes: Array<unknown> }; children: Array<JSXChild> }) {\n const explicitName = getJSXStringAttribute(node.openingElement.attributes as Array<unknown>, \"name\")\n if (explicitName) return explicitName\n\n const customPropName = getSingleJSXAttributeName(node.openingElement.attributes as Array<unknown>)\n if (customPropName) return customPropName\n\n return getVarChildIdentifier(node.children)\n}\n\nfunction getSingleJSXAttributeName(attributes: Array<unknown>) {\n const keys = attributes.flatMap((attr) => {\n const attribute = attr as\n | {\n type?: string\n name?: { type?: string; name?: string }\n }\n | undefined\n\n if (attribute?.type !== \"JSXAttribute\" || attribute.name?.type !== \"JSXIdentifier\") return []\n return [attribute.name.name]\n })\n\n return keys.length === 1 ? keys[0] : undefined\n}\n\nfunction getVarChildIdentifier(children: Array<JSXChild>) {\n const meaningfulChildren = children.filter((child) => !(child.type === \"JSXText\" && child.value.trim().length === 0))\n if (meaningfulChildren.length !== 1) return undefined\n\n const [child] = meaningfulChildren\n if (!child || child.type !== \"JSXExpressionContainer\" || child.expression.type !== \"Identifier\") return undefined\n return child.expression.name\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nimport type { BetterTranslateRuntimeConfig } from \"./types.js\"\n\nexport const DEFAULT_LOCAL_OUTPUT_DIR = \"src/lib/bt\"\nexport const RUNTIME_CONFIG_FILENAME = \"runtime.json\"\nexport const COMMON_RUNTIME_CONFIG_DIRS = [DEFAULT_LOCAL_OUTPUT_DIR, `assets/${DEFAULT_LOCAL_OUTPUT_DIR}`]\n\nexport function getRuntimeConfigPath(root: string, dir: string) {\n return resolve(root, dir, RUNTIME_CONFIG_FILENAME)\n}\n\nexport function getRuntimeConfigCandidatePaths(importMetaUrl: string, dirs = COMMON_RUNTIME_CONFIG_DIRS) {\n return getSearchBaseDirs(importMetaUrl).flatMap((baseDir) => dirs.map((dir) => resolve(baseDir, dir, RUNTIME_CONFIG_FILENAME)))\n}\n\nexport function getLocalConfigCandidatePaths(dir: string, importMetaUrl: string, fileName: string) {\n return getSearchBaseDirs(importMetaUrl).map((baseDir) => resolve(baseDir, dir, fileName))\n}\n\nexport function readRuntimeConfigFromPaths(paths: string[]) {\n for (const path of dedupe(paths)) {\n if (!existsSync(path)) continue\n try {\n return JSON.parse(readFileSync(path, \"utf-8\")) as BetterTranslateRuntimeConfig\n } catch {\n continue\n }\n }\n return null\n}\n\nfunction dedupe(paths: string[]) {\n return [...new Set(paths)]\n}\n\nfunction getSearchBaseDirs(importMetaUrl: string) {\n const currentDir = dirname(fileURLToPath(importMetaUrl))\n const candidates = [...collectParentDirs(process.cwd()), ...collectParentDirs(currentDir)]\n return dedupe(candidates)\n}\n\nfunction collectParentDirs(startDir: string, maxDepth = 6) {\n const dirs: string[] = []\n let currentDir = startDir\n\n for (let i = 0; i <= maxDepth; i++) {\n dirs.push(currentDir)\n const parentDir = dirname(currentDir)\n if (parentDir === currentDir) break\n currentDir = parentDir\n }\n\n return dirs\n}\n","import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, relative, resolve } from \"node:path\"\nimport type { Plugin } from \"vite\"\n\nimport type {\n BetterTranslatePluginOptions,\n BetterTranslateRuntimeConfig,\n ExtractedMessage,\n ManifestEntry,\n MessageManifest,\n MessageManifestFile,\n MessageSource,\n RuntimeMessages,\n TranslateMessage,\n TranslationCache,\n} from \"./types.js\"\n\nimport { createEmptyCache, getCacheKey, loadCache, saveCache } from \"./cache.js\"\nimport { analyzeSourceFile } from \"./extractor.js\"\nimport { serializeMeta } from \"./message-id.js\"\nimport { DEFAULT_LOCAL_OUTPUT_DIR, getRuntimeConfigPath } from \"./runtime-config.js\"\n\nconst PREFIX = \"\\x1b[36m[better-translation]\\x1b[0m\"\nconst DIM = \"\\x1b[2m\"\nconst RESET = \"\\x1b[0m\"\nconst YELLOW = \"\\x1b[33m\"\nconst BOLD = \"\\x1b[1m\"\nconst CYAN = \"\\x1b[36m\"\nconst REMOTE_API_BASE_URL = \"https://better-translation.com\"\nconst REMOTE_STUB = `${YELLOW}stub${RESET}`\nconst DEFAULT_ROOT_DIR = \"src\"\nconst DEFAULT_SCAN_EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".jsx\"]\nconst CALL_MARKERS = [\"t\", \"useT\"]\nconst COMPONENT_MARKERS = [\"T\"]\nconst PRIVATE_MANIFEST_FILENAME = \"manifest.json\"\nconst LOAD_MESSAGES_FILENAME = \"load-messages.ts\"\nconst LOCALES_SUBDIR = \"locales\"\nconst GITIGNORE_FILENAME = \".gitignore\"\nconst GITIGNORE_CONTENTS = [\"# Generated by better-translation/vite\", \"manifest.json\", \"\"].join(\"\\n\")\n\ninterface SyncResult {\n manifestChanged: boolean\n localeMessagesChanged: boolean\n}\n\nfunction formatLocale(locale: string) {\n return locale.toUpperCase()\n}\n\nfunction formatLocales(locales: string[]) {\n return locales.map(formatLocale).join(\", \")\n}\n\n/** Scans source files for translatable messages and keeps locale JSON files in sync. */\nexport function betterTranslation(options: BetterTranslatePluginOptions): Plugin {\n const {\n locales,\n defaultLocale = locales[0] ?? \"en\",\n rootDir = DEFAULT_ROOT_DIR,\n cacheFile = \".cache/better-translation.json\",\n logging = true,\n storage = { type: \"bundle\", output: DEFAULT_LOCAL_OUTPUT_DIR },\n translate,\n } = options\n const usesBundleStorage = storage.type === \"bundle\"\n const localesDir = storage.type === \"bundle\" ? (storage.output ?? DEFAULT_LOCAL_OUTPUT_DIR) : DEFAULT_LOCAL_OUTPUT_DIR\n const remoteUrl = storage.type === \"remote\" ? (storage.url ?? REMOTE_API_BASE_URL) : REMOTE_API_BASE_URL\n const manifest: MessageManifest = {}\n const fileMessages = new Map<string, ExtractedMessage[]>()\n let cache: TranslationCache = createEmptyCache()\n let root = \"\"\n let isDev = false\n let translateTimer: ReturnType<typeof setTimeout> | null = null\n let warnedRemoteTranslateStub = false\n let warnedRemoteSyncStub = false\n let sourceRoots: string[] = []\n\n function log(message: string) {\n if (logging) console.log(message)\n }\n\n async function remoteTranslate(messages: TranslateMessage[], _locale: string) {\n if (!warnedRemoteTranslateStub) {\n warnedRemoteTranslateStub = true\n log(`${PREFIX} ${REMOTE_STUB} remote translate via ${DIM}${remoteUrl}${RESET} not implemented yet`)\n }\n return Object.fromEntries(messages.map((message) => [message.id, message.text])) as Record<string, string>\n }\n\n async function syncRemote() {\n if (!warnedRemoteSyncStub) {\n warnedRemoteSyncStub = true\n log(`${PREFIX} ${REMOTE_STUB} remote locale sync via ${DIM}${remoteUrl}${RESET} not implemented yet`)\n }\n }\n\n const resolvedTranslate = translate ?? (usesBundleStorage ? undefined : remoteTranslate)\n\n function buildMessageManifest(): MessageManifestFile {\n return Object.fromEntries(\n Object.entries(manifest).map(([id, entry]) => [\n id,\n {\n defaultMessage: entry.defaultMessage,\n meta: entry.meta,\n placeholders: entry.placeholders,\n sources: entry.sources,\n },\n ]),\n )\n }\n\n function shouldScanFile(id: string) {\n const cleanId = id.split(\"?\", 1)[0] ?? id\n if (cleanId.includes(\"node_modules\")) return false\n const extension = DEFAULT_SCAN_EXTENSIONS.find((ext) => cleanId.endsWith(ext))\n if (!extension) return false\n return sourceRoots.some(\n (sourceRoot) => cleanId === sourceRoot || cleanId.startsWith(`${sourceRoot}/`) || cleanId.startsWith(`${sourceRoot}\\\\`),\n )\n }\n\n function getPrivateManifestPath() {\n return resolve(root, localesDir, PRIVATE_MANIFEST_FILENAME)\n }\n\n function getRuntimeConfig(): BetterTranslateRuntimeConfig {\n return {\n storage: usesBundleStorage ? { type: \"bundle\", output: localesDir } : { type: \"remote\", url: remoteUrl },\n defaultLocale,\n locales,\n }\n }\n\n function writeRuntimeConfig() {\n if (!usesBundleStorage) return\n\n const runtimeConfig = JSON.stringify(getRuntimeConfig(), null, 2) + \"\\n\"\n const path = getRuntimeConfigPath(root, localesDir)\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, runtimeConfig)\n }\n\n function getLocalesDirPath() {\n return resolve(root, localesDir, LOCALES_SUBDIR)\n }\n\n function getLocalePath(locale: string) {\n return resolve(getLocalesDirPath(), `${locale}.json`)\n }\n\n function readLocaleMessages(locale: string): RuntimeMessages {\n const path = getLocalePath(locale)\n if (!existsSync(path)) return {}\n\n try {\n const input = JSON.parse(readFileSync(path, \"utf-8\")) as unknown\n return normalizeLocaleMessages(input)\n } catch {\n return {}\n }\n }\n\n function writePrivateManifest() {\n if (!usesBundleStorage) return\n const path = getPrivateManifestPath()\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, JSON.stringify(buildMessageManifest(), null, 2) + \"\\n\")\n }\n\n function assertFileContents(path: string, expected: string, label: string) {\n if (!existsSync(path)) {\n throw new Error(`${PREFIX} missing committed ${label} at ${relative(root, path)}`)\n }\n const actual = readFileSync(path, \"utf-8\")\n if (actual !== expected) {\n throw new Error(\n [\n `${PREFIX} committed ${label} is out of date`,\n `expected committed file at ${relative(root, path)} to match the generated output`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ].join(\"\\n\"),\n )\n }\n }\n\n function assertJsonFileContents(path: string, expected: unknown, label: string) {\n if (!existsSync(path)) {\n throw new Error(`${PREFIX} missing committed ${label} at ${relative(root, path)}`)\n }\n\n let actual: unknown\n try {\n actual = JSON.parse(readFileSync(path, \"utf-8\")) as unknown\n } catch {\n throw new Error(`${PREFIX} committed ${label} is invalid JSON at ${relative(root, path)}`)\n }\n\n if (JSON.stringify(actual) !== JSON.stringify(expected)) {\n throw new Error(\n [\n `${PREFIX} committed ${label} is out of date`,\n `expected committed file at ${relative(root, path)} to match the generated output`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ].join(\"\\n\"),\n )\n }\n }\n\n function buildLoadMessagesModule() {\n const localeValues = locales.map((locale) => JSON.stringify(locale)).join(\", \")\n return [\n \"// Auto-generated by better-translation/vite - do not edit.\",\n \"// Delete this file to regenerate from your plugin `locales` config.\",\n \"\",\n `export const locales = [${localeValues}] as const`,\n \"\",\n \"export type AppLocale = (typeof locales)[number]\",\n \"\",\n \"export async function loadMessages(locale: AppLocale): Promise<Record<string, string>> {\",\n ` const messages = await import(\\`./${LOCALES_SUBDIR}/\\${locale}.json\\`)`,\n \" return messages.default\",\n \"}\",\n \"\",\n ].join(\"\\n\")\n }\n\n function writeLoadMessagesModule() {\n if (!usesBundleStorage) return\n const path = resolve(root, localesDir, LOAD_MESSAGES_FILENAME)\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, buildLoadMessagesModule())\n }\n\n function writeGitignore() {\n if (!usesBundleStorage) return\n const path = resolve(root, localesDir, GITIGNORE_FILENAME)\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, GITIGNORE_CONTENTS)\n }\n\n function assertGeneratedFilesCommitted() {\n if (!usesBundleStorage) return\n assertJsonFileContents(getRuntimeConfigPath(root, localesDir), getRuntimeConfig(), \"runtime config\")\n const loadMessagesPath = resolve(root, localesDir, LOAD_MESSAGES_FILENAME)\n if (!existsSync(loadMessagesPath)) {\n throw new Error(`${PREFIX} missing committed load-messages module at ${relative(root, loadMessagesPath)}`)\n }\n assertFileContents(resolve(root, localesDir, GITIGNORE_FILENAME), GITIGNORE_CONTENTS, \"generated .gitignore\")\n }\n\n function buildLocalLocaleMessages(locale: string): RuntimeMessages {\n const existingMessages = readLocaleMessages(locale)\n const messages: RuntimeMessages = {}\n\n if (locale === defaultLocale) {\n for (const [id, entry] of Object.entries(manifest)) {\n messages[id] = entry.defaultMessage\n }\n return messages\n }\n\n for (const id of Object.keys(manifest)) {\n if (Object.hasOwn(messages, id)) continue\n if (Object.hasOwn(existingMessages, id)) {\n const existingMessage = existingMessages[id]!\n messages[id] = existingMessage\n continue\n }\n const cachedMessage = cache.entries[getCacheKey(id, locale)]?.translation\n if (cachedMessage !== undefined) messages[id] = cachedMessage\n }\n return messages\n }\n\n function writeLocaleFilesToDisk() {\n if (!usesBundleStorage) return\n const dir = getLocalesDirPath()\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n for (const locale of locales) {\n writeFileIfChanged(resolve(dir, `${locale}.json`), JSON.stringify(buildLocalLocaleMessages(locale), null, 2) + \"\\n\")\n }\n writeLoadMessagesModule()\n }\n\n function getMissingMessagesByLocale() {\n const missingByLocale = new Map<string, TranslateMessage[]>()\n\n for (const locale of locales) {\n if (locale === defaultLocale) continue\n const existingMessages = readLocaleMessages(locale)\n for (const [id, entry] of Object.entries(manifest)) {\n if (!Object.hasOwn(existingMessages, id) && !Object.hasOwn(cache.entries, getCacheKey(id, locale))) {\n const misses = missingByLocale.get(locale) ?? []\n misses.push({\n id,\n text: entry.defaultMessage,\n meta: entry.meta,\n placeholders: entry.placeholders,\n sources: entry.sources,\n })\n missingByLocale.set(locale, misses)\n }\n }\n }\n\n return missingByLocale\n }\n\n function assertLocalBuildTranslationsComplete() {\n const expectedIds = new Set(Object.keys(manifest))\n const issues: string[] = []\n\n for (const locale of locales) {\n const localePath = getLocalePath(locale)\n if (!existsSync(localePath)) {\n issues.push(`- ${locale}: missing file at ${relative(root, localePath)}`)\n continue\n }\n\n const localeMessages = readLocaleMessages(locale)\n const missingIds = [...expectedIds].filter((id) => !Object.hasOwn(localeMessages, id))\n const orphanIds = Object.keys(localeMessages).filter((id) => !expectedIds.has(id))\n\n if (locale === defaultLocale) {\n const staleIds = [...expectedIds].filter((id) => localeMessages[id] !== manifest[id]!.defaultMessage)\n if (missingIds.length > 0) issues.push(formatLocaleIssue(locale, \"missing\", missingIds))\n if (orphanIds.length > 0) issues.push(formatLocaleIssue(locale, \"orphaned\", orphanIds))\n if (staleIds.length > 0) issues.push(formatLocaleIssue(locale, \"outdated default messages\", staleIds))\n continue\n }\n\n if (missingIds.length > 0) issues.push(formatLocaleIssue(locale, \"missing\", missingIds))\n if (orphanIds.length > 0) issues.push(formatLocaleIssue(locale, \"orphaned\", orphanIds))\n }\n\n if (issues.length === 0) return\n\n throw new Error(\n [\n `${PREFIX} committed locale artifacts are out of sync for local production build`,\n `local production builds are check-only and never regenerate locale files`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ...issues,\n ].join(\"\\n\"),\n )\n }\n\n async function translateMissingMessages() {\n if (!resolvedTranslate) return false\n const missingByLocale = getMissingMessagesByLocale()\n\n const totalMisses = [...missingByLocale.values()].reduce((count, misses) => count + misses.length, 0)\n if (totalMisses === 0) return false\n\n const missLocales = [...missingByLocale.keys()]\n log(\n `${PREFIX} ${BOLD}Translating${RESET} ${CYAN}${totalMisses}${RESET} ${totalMisses === 1 ? \"Message\" : \"Messages\"} -> ${CYAN}${formatLocales(missLocales)}${RESET}`,\n )\n\n for (const [locale, misses] of missingByLocale) {\n const result = await resolvedTranslate(misses, locale)\n\n for (const miss of misses) {\n const translated = result[miss.id] ?? miss.text\n cache.entries[getCacheKey(miss.id, locale)] = {\n sourceText: miss.text,\n meta: miss.meta,\n locale,\n translation: translated,\n timestamp: Date.now(),\n }\n }\n }\n\n return true\n }\n\n function scheduleDevTranslation() {\n if (!resolvedTranslate) return\n if (!isDev) return\n if (translateTimer) clearTimeout(translateTimer)\n translateTimer = setTimeout(async () => {\n const translated = await translateMissingMessages()\n if (translated) saveCache(resolve(root, cacheFile), cache)\n writeLocaleFilesToDisk()\n writePrivateManifest()\n }, 1000)\n }\n\n function removeFileMessages(file: string) {\n const previous = fileMessages.get(file)\n if (!previous) return false\n\n for (const message of previous) {\n const entry = manifest[message.id]\n if (!entry) continue\n entry.sources = entry.sources.filter((source) => !isSameSource(source, message.source))\n if (entry.sources.length === 0) delete manifest[message.id]\n }\n\n fileMessages.delete(file)\n return true\n }\n\n function syncFileMessages(file: string, messages: ExtractedMessage[]): SyncResult {\n const previousMessages = fileMessages.get(file) ?? []\n const nextEntries = groupMessagesById(messages)\n for (const [id, entry] of Object.entries(nextEntries)) {\n const existing = manifest[id]\n if (existing && !hasSameMessageShape(existing, entry)) {\n throw new Error(formatCollisionError(id, existing, entry))\n }\n }\n\n removeFileMessages(file)\n for (const [id, entry] of Object.entries(nextEntries)) {\n if (!manifest[id]) {\n manifest[id] = entry\n continue\n }\n for (const source of entry.sources) {\n if (!manifest[id]!.sources.some((existingSource) => isSameSource(existingSource, source))) {\n manifest[id]!.sources.push(source)\n }\n }\n }\n\n if (messages.length > 0) fileMessages.set(file, messages)\n return {\n manifestChanged:\n previousMessages.length !== messages.length ||\n previousMessages.some((message, index) => !isSameExtractedMessage(message, messages[index])),\n localeMessagesChanged:\n previousMessages.length !== messages.length ||\n previousMessages.some((message, index) => !hasSameMessageShape(message, messages[index]!)),\n }\n }\n\n function syncSourceCode(file: string, code: string) {\n const analysis = analyzeSourceFile(code, file, {\n call: CALL_MARKERS,\n component: COMPONENT_MARKERS,\n logging,\n })\n if (!analysis.parsed) return null\n return syncFileMessages(\n file,\n analysis.messages.map((message) => ({\n ...message,\n source: {\n ...message.source,\n file: toRootRelativePath(message.source.file),\n },\n })),\n )\n }\n\n function removeTrackedFile(file: string): SyncResult {\n const hadPreviousMessages = removeFileMessages(file)\n return {\n manifestChanged: hadPreviousMessages,\n localeMessagesChanged: hadPreviousMessages,\n }\n }\n\n function applySyncResult(syncResult: SyncResult | null, options: { scheduleTranslation: boolean }) {\n if (!syncResult) return\n if (syncResult.localeMessagesChanged) {\n writeLocaleFilesToDisk()\n writePrivateManifest()\n if (options.scheduleTranslation) scheduleDevTranslation()\n return\n }\n if (syncResult.manifestChanged) writePrivateManifest()\n }\n\n function scanAllSourceFiles() {\n for (const id of Object.keys(manifest)) delete manifest[id]\n fileMessages.clear()\n\n for (const sourceRoot of sourceRoots) {\n if (!existsSync(sourceRoot)) continue\n for (const file of collectScanFiles(sourceRoot).sort()) {\n const code = readFileSync(file, \"utf-8\")\n syncSourceCode(file, code)\n }\n }\n }\n\n function toRootRelativePath(file: string) {\n return relative(root, file).replaceAll(\"\\\\\", \"/\")\n }\n\n return {\n name: \"better-translation-extract\",\n enforce: \"pre\",\n\n configResolved(config) {\n root = config.root\n isDev = config.command === \"serve\"\n sourceRoots = (Array.isArray(rootDir) ? rootDir : [rootDir]).map((dir) => resolve(root, dir))\n log(\n `${PREFIX} Locales: ${CYAN}${formatLocales(locales)}${RESET} | Default: ${CYAN}${formatLocale(defaultLocale)}${RESET} | Storage: ${CYAN}${usesBundleStorage ? \"Bundle\" : \"Remote\"}${RESET} | Out Dir: ${DIM}${usesBundleStorage ? localesDir : \"n/a\"}${RESET} | Roots: ${DIM}${(Array.isArray(rootDir) ? rootDir : [rootDir]).join(\", \")}${RESET}`,\n )\n },\n\n buildStart() {\n cache = loadCache(resolve(root, cacheFile))\n scanAllSourceFiles()\n if (usesBundleStorage && !isDev) {\n assertGeneratedFilesCommitted()\n assertLocalBuildTranslationsComplete()\n return\n }\n writeRuntimeConfig()\n writeLoadMessagesModule()\n writeGitignore()\n writeLocaleFilesToDisk()\n writePrivateManifest()\n\n if (isDev) scheduleDevTranslation()\n },\n\n configureServer(server) {\n server.watcher.add(sourceRoots)\n\n const syncFileFromDisk = (file: string) => {\n if (!shouldScanFile(file) || !existsSync(file)) return\n applySyncResult(syncSourceCode(file, readFileSync(file, \"utf-8\")), { scheduleTranslation: true })\n }\n\n const removeFileFromManifest = (file: string) => {\n if (!shouldScanFile(file)) return\n applySyncResult(removeTrackedFile(file), { scheduleTranslation: true })\n }\n\n server.watcher.on(\"add\", syncFileFromDisk)\n server.watcher.on(\"change\", syncFileFromDisk)\n server.watcher.on(\"unlink\", removeFileFromManifest)\n },\n\n transform(code, id) {\n const cleanId = id.split(\"?\", 1)[0] ?? id\n if (!shouldScanFile(cleanId)) return\n\n const analysis = analyzeSourceFile(code, cleanId, {\n call: CALL_MARKERS,\n component: COMPONENT_MARKERS,\n logging,\n })\n\n if (analysis.edits.length === 0) return\n return {\n code: applyEdits(code, analysis.edits),\n map: null,\n }\n },\n\n async generateBundle() {\n if (usesBundleStorage) {\n if (!isDev) {\n assertGeneratedFilesCommitted()\n }\n assertLocalBuildTranslationsComplete()\n } else {\n await translateMissingMessages()\n }\n\n if (!usesBundleStorage) {\n await syncRemote()\n }\n },\n\n closeBundle() {\n if (usesBundleStorage && !isDev) return\n saveCache(resolve(root, cacheFile), cache)\n },\n }\n}\n\nfunction formatLocaleIssue(locale: string, label: string, ids: string[]) {\n const preview = ids\n .slice(0, 5)\n .map((id) => JSON.stringify(id))\n .join(\", \")\n const suffix = ids.length > 5 ? `, ... ${ids.length - 5} more` : \"\"\n return `- ${locale}: ${label} (${preview}${suffix})`\n}\n\nfunction normalizeLocaleMessages(input: unknown): RuntimeMessages {\n if (isRuntimeMessages(input)) return input\n if (\n typeof input === \"object\" &&\n input !== null &&\n \"messages\" in input &&\n typeof input.messages === \"object\" &&\n input.messages !== null\n ) {\n return Object.fromEntries(\n Object.entries(input.messages).flatMap(([id, entry]) =>\n typeof entry === \"object\" && entry !== null && \"translation\" in entry && typeof entry.translation === \"string\"\n ? [[id, entry.translation]]\n : [],\n ),\n ) as RuntimeMessages\n }\n return {}\n}\n\nfunction writeFileIfChanged(path: string, contents: string) {\n if (existsSync(path) && readFileSync(path, \"utf-8\") === contents) return false\n writeFileSync(path, contents)\n return true\n}\n\nfunction collectScanFiles(root: string) {\n const files: string[] = []\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n const path = resolve(root, entry.name)\n if (entry.isDirectory()) {\n if (entry.name === \"node_modules\") continue\n files.push(...collectScanFiles(path))\n continue\n }\n files.push(path)\n }\n return files\n}\n\nfunction isRuntimeMessages(input: unknown): input is RuntimeMessages {\n return typeof input === \"object\" && input !== null && Object.values(input).every((value) => typeof value === \"string\")\n}\n\nfunction applyEdits(code: string, edits: Array<{ start: number; end: number; replacement: string }>) {\n let transformed = code\n for (const edit of [...edits].sort((a, b) => b.start - a.start)) {\n transformed = `${transformed.slice(0, edit.start)}${edit.replacement}${transformed.slice(edit.end)}`\n }\n return transformed\n}\n\nfunction groupMessagesById(messages: ExtractedMessage[]): MessageManifest {\n const grouped: MessageManifest = {}\n\n for (const message of messages) {\n const existing = grouped[message.id]\n if (existing && !hasSameMessageShape(existing, message)) {\n throw new Error(formatCollisionError(message.id, existing, message))\n }\n if (!existing) {\n grouped[message.id] = {\n defaultMessage: message.defaultMessage,\n meta: message.meta,\n placeholders: message.placeholders,\n sources: [message.source],\n }\n continue\n }\n if (!existing.sources.some((source) => isSameSource(source, message.source))) {\n existing.sources.push(message.source)\n }\n }\n\n return grouped\n}\n\nfunction hasSameMessageShape(\n existing: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\">,\n incoming: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\"> | ExtractedMessage,\n) {\n return (\n existing.defaultMessage === incoming.defaultMessage &&\n serializeMeta(existing.meta) === serializeMeta(incoming.meta) &&\n JSON.stringify(existing.placeholders) === JSON.stringify(incoming.placeholders)\n )\n}\n\nfunction isSameSource(left: MessageSource, right: MessageSource) {\n return (\n left.file === right.file &&\n left.kind === right.kind &&\n left.marker === right.marker &&\n left.start === right.start &&\n left.end === right.end\n )\n}\n\nfunction isSameExtractedMessage(left: ExtractedMessage, right?: ExtractedMessage) {\n if (!right) return false\n return hasSameMessageShape(left, right) && isSameSource(left.source, right.source)\n}\n\nfunction formatCollisionError(\n id: string,\n existing: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\" | \"sources\">,\n incoming: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\" | \"sources\"> | ExtractedMessage,\n) {\n const existingSources = formatSources(existing.sources)\n const incomingSources = formatSources(\"source\" in incoming ? [incoming.source] : incoming.sources)\n return [\n `${PREFIX} conflicting message definition for ${BOLD}\"${id}\"${RESET}`,\n `existing: ${JSON.stringify({ defaultMessage: existing.defaultMessage, meta: existing.meta, placeholders: existing.placeholders })}`,\n `existing sources: ${existingSources}`,\n `incoming: ${JSON.stringify({ defaultMessage: incoming.defaultMessage, meta: incoming.meta, placeholders: incoming.placeholders })}`,\n `incoming sources: ${incomingSources}`,\n ].join(\"\\n\")\n}\n\nfunction formatSources(sources: MessageSource[]) {\n return sources.map((source) => `${source.file}:${source.line}:${source.column}`).join(\", \")\n}\n"],"mappings":";;;;;AAKA,MAAM,kBAAkB;AAExB,SAAgB,mBAAqC;AACnD,QAAO;EAAE,SAAS;EAAiB,SAAS,EAAE;EAAE;;;AAIlD,SAAgB,UAAU,MAAgC;AACxD,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO,kBAAkB;AAChD,KAAI;EACF,MAAM,OAAO,KAAK,MAAM,aAAa,MAAM,QAAQ,CAAC;AACpD,MAAI,KAAK,YAAY,gBAAiB,QAAO,kBAAkB;AAC/D,SAAO;SACD;AACN,SAAO,kBAAkB;;;;AAK7B,SAAgB,UAAU,MAAc,OAAyB;CAC/D,MAAM,MAAM,QAAQ,KAAK;AACzB,KAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CACzD,MAAM,OAAO,KAAK,UAAU,OAAO,MAAM,EAAE;AAC3C,KAAI,WAAW,KAAK,IAAI,aAAa,MAAM,QAAQ,KAAK,KAAM;AAC9D,eAAc,MAAM,KAAK;;;AAI3B,SAAgB,YAAY,WAAmB,QAAgB;AAC7D,QAAO,GAAG,UAAU,IAAI;;;;;ACR1B,SAAgB,kBAAkB,MAAc,UAAkB,SAAkC;CAClG,MAAM,WAA+B,EAAE;CACvC,MAAM,QAAsB,EAAE;CAC9B,MAAM,SAAS,UAAU,UAAU,KAAK;AACxC,KAAI,OAAO,OAAO,SAAS,EAAG,QAAO;EAAE,QAAQ;EAAO;EAAU;EAAO;CACvE,MAAM,aAAa,cAAc,KAAK;AAEtB,KAAI,QAAQ;EAC1B,eAAe,MAAM;AACnB,OACE,KAAK,OAAO,SAAS,gBACrB,QAAQ,KAAK,SAAS,KAAK,OAAO,KAAK,IACvC,KAAK,UAAU,UAAU,KACzB,gBAAgB,KAAK,UAAU,GAAI,EACnC;IACA,MAAM,QAAS,KAAK,UAAU,GAAqB;IACnD,MAAM,OAAO,oBAAoB,KAAK,UAAU;IAChD,MAAM,KAAK,iBAAiB,OAAO,KAAK;AACxC,aAAS,KAAK;KACZ;KACA,gBAAgB;KAChB,MAAM,QAAQ,EAAE;KAChB,cAAc,+BAA+B,MAAM;KACnD,QAAQ,aAAa;MACnB;MACA;MACA,QAAQ,KAAK,OAAO;MACpB,MAAM;MACN,OAAO,KAAK;MACZ,KAAK,KAAK;MACX,CAAC;KACH,CAAC;IAEF,MAAM,kBAAkB,sBAAsB,MAAM,KAAK,WAAW,GAAG;AACvE,QAAI,gBAAiB,OAAM,KAAK,gBAAgB;;;EAIpD,WAAW,MAAM;GACf,MAAM,UAAU,KAAK;AACrB,OACE,QAAQ,KAAK,SAAS,mBACtB,QAAQ,KAAK,SAAS,SACrB,QAAQ,WAA8B,WAAW,GAClD;IACA,MAAM,aAAa,sBAAsB,KAAK,SAAS;AACvD,QAAI,WACF,OAAM,KAAK;KACT,OAAO,KAAK;KACZ,KAAK,KAAK;KACV,aAAa,QAAQ,WAAW,IAAI,WAAW;KAChD,CAAC;;AAIN,OAAI,QAAQ,KAAK,SAAS,gBAAiB;AAC3C,OAAI,CAAC,QAAQ,UAAU,SAAS,QAAQ,KAAK,KAAK,CAAE;GAEpD,MAAM,aAAa,mBAAmB,KAAK,SAAS;AACpD,OAAI,CAAC,WAAW,OAAO;AACrB,QAAI,QAAQ,QACV,SAAQ,KAAK,oCAAoC,QAAQ,KAAK,KAAK,OAAO,SAAS,YAAY;AAEjG;;GAGF,MAAM,UAAU,sBAAsB,QAAQ,YAAY,UAAU;GACpE,MAAM,OAAO,UAAU,EAAE,SAAS,GAAG,KAAA;GACrC,MAAM,KAAK,sBAAsB,QAAQ,YAAY,KAAK,IAAI,aAAa,WAAW,SAAS,KAAK;AACpG,YAAS,KAAK;IACZ;IACA,gBAAgB,WAAW;IAC3B,MAAM,QAAQ,EAAE;IAChB,cAAc,WAAW;IACzB,QAAQ,aAAa;KACnB;KACA;KACA,QAAQ,QAAQ,KAAK;KACrB,MAAM;KACN,OAAO,KAAK;KACZ,KAAK,KAAK;KACX,CAAC;IACH,CAAC;AAEF,OAAI,CAAC,gBAAgB,QAAQ,YAA8B,KAAK,CAC9D,OAAM,KAAK;IACT,OAAO,QAAQ,KAAK;IACpB,KAAK,QAAQ,KAAK;IAClB,aAAa,QAAQ,GAAG;IACzB,CAAC;;EAGP,CAAC,CAEM,MAAM,OAAO,QAAQ;AAC7B,QAAO;EAAE,QAAQ;EAAM;EAAU;EAAO;;AAG1C,SAAS,gBAAgB,MAAuC;AAC9D,QAAO,KAAK,SAAS,aAAa,OAAQ,KAAuB,UAAU;;AAG7E,SAAS,gBAAgB,MAAiB;AACxC,KAAI,CAAC,KAAM,QAAO,KAAA;AAClB,KAAI,gBAAgB,KAAK,CAAE,QAAO,EAAE,SAAS,KAAK,OAAO;AAEzD,KAAI,KAAK,SAAS,mBAAoB,QAAO,KAAA;CAE7C,MAAM,OAAyB,EAAE;AAEjC,MAAK,MAAM,YAAY,KAAK,WAK1B,MACG,SAAS,SAAS,oBAAoB,SAAS,SAAS,gBACvD,SAAS,KAAK,SAAS,iBAAiB,SAAS,IAAI,SAAS,aAAa,SAAS,IAAI,SAAS,SAChG,SAAS,KAAK,SAAS,cAAc,SAAS,IAAI,UAAU,aAAa,SAAS,IAAI,UAAU,UACnG,SAAS,SACT,gBAAgB,SAAS,MAAM,EAC/B;EACA,MAAM,MAAM,SAAS,KAAK,SAAS,eAAe,SAAS,IAAI,OAAO,SAAS,KAAK;AACpF,MAAI,QAAQ,aAAa,QAAQ,KAAM,MAAK,OAAO,SAAS,MAAM;;AAItE,QAAO,OAAO,KAAK,KAAK,CAAC,SAAS,IAAI,OAAO,KAAA;;AAG/C,SAAS,oBAAoB,MAAkB;AAC7C,QAAO,gBAAgB,2BAA2B,KAAK,GAAG,GAAG,KAAK,KAAK,KAAK,GAAG;;AAGjF,SAAS,sBAAsB,MAAc,MAAkB,IAAY;CACzE,MAAM,YAAY,KAAK;CACvB,MAAM,aAAa,2BAA2B,UAAU,GAAG,YAAY,KAAK;AAE5E,KAAI,CAAC,WACH,QAAO;EACL,QAAQ,aAAa,KAAK,IAAK;EAC/B,MAAM,aAAa,KAAK,IAAK;EAC7B,aAAa,WAAW,KAAK,UAAU,GAAG,CAAC;EAC5C;AAGH,KAAI,gBAAgB,WAAW,EAAE;EAC/B,MAAM,eAAe,KAAK,MAAM,WAAW,OAAO,WAAW,IAAI;AACjE,SAAO;GACL,OAAO,WAAW;GAClB,KAAK,WAAW;GAChB,aAAa,SAAS,KAAK,UAAU,GAAG,CAAC,aAAa,aAAa;GACpE;;AAGH,KAAI,WAAW,SAAS,mBAAoB,QAAO,KAAA;AACnD,KAAI,kBAAkB,YAAY,KAAK,CAAE,QAAO,KAAA;CAGhD,MAAM,cADe,KAAK,MAAM,WAAW,OAAO,WAAW,IAAI,CAChC,MAAM,GAAG,GAAG;CAC7C,MAAM,cACJ,YAAY,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,UAAU,GAAG,CAAC,GAAG,YAAY,MAAM,SAAS,KAAK,UAAU,GAAG,CAAC;AAE/G,QAAO;EACL,OAAO,WAAW;EAClB,KAAK,WAAW;EAChB;EACD;;AAGH,SAAS,2BAA2B,MAAiB;AACnD,KAAI,CAAC,KAAM,QAAO;AAClB,KAAI,gBAAgB,KAAK,CAAE,QAAO;AAClC,KAAI,KAAK,SAAS,mBAAoB,QAAO;AAE7C,QAAQ,KAAK,WAA8B,OAAO,UAAU;EAC1D,MAAM,WAAW;AAOjB,MAAI,UAAU,SAAS,oBAAoB,UAAU,SAAS,WAAY,QAAO;AAEjF,SACG,SAAS,KAAK,SAAS,iBAAiB,SAAS,IAAI,SAAS,aAAa,SAAS,IAAI,SAAS,SACjG,SAAS,KAAK,SAAS,cAAc,SAAS,IAAI,UAAU,aAAa,SAAS,IAAI,UAAU;GAEnG;;AAGJ,SAAS,sBAAsB,YAA4B,MAAc;AACvE,MAAK,MAAM,QAAQ,WAKjB,KACE,KAAK,SAAS,kBACd,KAAK,MAAM,SAAS,mBACpB,KAAK,KAAK,SAAS,QACnB,KAAK,OAAO,SAAS,aACrB,OAAO,KAAK,MAAM,UAAU,SAE5B,QAAO,KAAK,MAAM;;AAKxB,SAAS,gBAAgB,YAA4B,MAAc;AACjE,QAAO,WAAW,MAAM,SAAS;EAC/B,MAAM,YAAY;AAOlB,SAAO,WAAW,SAAS,kBAAkB,UAAU,MAAM,SAAS,mBAAmB,UAAU,KAAK,SAAS;GACjH;;AAGJ,SAAS,kBAAkB,MAAsC,MAAc;AAC7E,QAAO,KAAK,WAAW,MAAM,UAAU;EACrC,MAAM,WAAW;AAOjB,MAAI,UAAU,SAAS,oBAAoB,UAAU,SAAS,WAAY,QAAO;AACjF,SACG,SAAS,KAAK,SAAS,gBAAgB,SAAS,IAAI,SAAS,QAC7D,SAAS,KAAK,SAAS,aAAa,SAAS,IAAI,UAAU;GAE9D;;AAGJ,SAAS,+BAA+B,SAAiB;CACvD,MAAM,wBAAQ,IAAI,KAAa;AAC/B,MAAK,MAAM,SAAS,QAAQ,SAAS,aAAa,CAChD,KAAI,MAAM,GAAI,OAAM,IAAI,MAAM,GAAG;AAEnC,QAAO,CAAC,GAAG,MAAM;;AAGnB,SAAS,aAAa,EACpB,UACA,YACA,QACA,MACA,OACA,OAQgB;CAChB,MAAM,gBAAgB,YAAY,OAAO,WAAW;CACpD,MAAM,cAAc,YAAY,KAAK,WAAW;AAEhD,QAAO;EACL,MAAM;EACN;EACA;EACA,MAAM,cAAc;EACpB,QAAQ,cAAc;EACtB,SAAS,YAAY;EACrB,WAAW,YAAY;EACvB;EACA;EACD;;AAGH,SAAS,cAAc,MAAc;CACnC,MAAM,SAAS,CAAC,EAAE;AAClB,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAC/B,KAAI,KAAK,OAAO,KAAM,QAAO,KAAK,IAAI,EAAE;AAE1C,QAAO;;AAGT,SAAS,YAAY,QAAgB,YAAsB;CACzD,IAAI,MAAM;CACV,IAAI,OAAO,WAAW,SAAS;AAE/B,QAAO,OAAO,MAAM;EAClB,MAAM,MAAM,KAAK,OAAO,MAAM,QAAQ,EAAE;EACxC,MAAM,YAAY,WAAW;EAC7B,MAAM,gBAAgB,WAAW,MAAM,MAAM,OAAO;AAEpD,MAAI,SAAS,WAAW;AACtB,UAAO,MAAM;AACb;;AAGF,MAAI,UAAU,eAAe;AAC3B,SAAM,MAAM;AACZ;;AAGF,SAAO;GAAE,MAAM,MAAM;GAAG,QAAQ,SAAS,YAAY;GAAG;;CAG1D,MAAM,WAAW,WAAW,SAAS;CACrC,MAAM,YAAY,WAAW,aAAa;AAC1C,QAAO;EAAE,MAAM,WAAW;EAAG,QAAQ,SAAS,YAAY;EAAG;;AAS/D,SAAS,mBAAmB,UAA6C;CACvE,MAAM,QAAkB,EAAE;CAC1B,MAAM,eAAyB,EAAE;AAEjC,MAAK,MAAM,SAAS,SAClB,SAAQ,MAAM,MAAd;EACE,KAAK;AACH,SAAM,KAAK,MAAM,MAAM;AACvB;EAEF,KAAK,cAAc;GACjB,MAAM,OAAO,MAAM,eAAe;AAClC,OAAI,KAAK,SAAS,mBAAmB,KAAK,SAAS,MACjD,QAAO;IAAE,SAAS;IAAI,cAAc,EAAE;IAAE,OAAO;IAAO;GAGxD,MAAM,UAAU,sBAAsB,MAAM;AAC5C,OAAI,CAAC,QAAS,QAAO;IAAE,SAAS;IAAI,cAAc,EAAE;IAAE,OAAO;IAAO;AAEpE,gBAAa,KAAK,QAAQ;AAC1B,SAAM,KAAK,IAAI,QAAQ,GAAG;AAC1B;;EAGF,KAAK;AACH,OAAI,MAAM,WAAW,SAAS,qBAC5B,QAAO;IAAE,SAAS;IAAI,cAAc,EAAE;IAAE,OAAO;IAAO;AAExD;EAEF,QACE;;CAIN,MAAM,UAAU,MAAM,KAAK,GAAG,CAAC,QAAQ,QAAQ,IAAI,CAAC,MAAM;AAC1D,QAAO;EAAE;EAAS;EAAc,OAAO,QAAQ,SAAS;EAAG;;AAG7D,SAAS,sBAAsB,MAAqF;CAClH,MAAM,eAAe,sBAAsB,KAAK,eAAe,YAA8B,OAAO;AACpG,KAAI,aAAc,QAAO;CAEzB,MAAM,iBAAiB,0BAA0B,KAAK,eAAe,WAA6B;AAClG,KAAI,eAAgB,QAAO;AAE3B,QAAO,sBAAsB,KAAK,SAAS;;AAG7C,SAAS,0BAA0B,YAA4B;CAC7D,MAAM,OAAO,WAAW,SAAS,SAAS;EACxC,MAAM,YAAY;AAOlB,MAAI,WAAW,SAAS,kBAAkB,UAAU,MAAM,SAAS,gBAAiB,QAAO,EAAE;AAC7F,SAAO,CAAC,UAAU,KAAK,KAAK;GAC5B;AAEF,QAAO,KAAK,WAAW,IAAI,KAAK,KAAK,KAAA;;AAGvC,SAAS,sBAAsB,UAA2B;CACxD,MAAM,qBAAqB,SAAS,QAAQ,UAAU,EAAE,MAAM,SAAS,aAAa,MAAM,MAAM,MAAM,CAAC,WAAW,GAAG;AACrH,KAAI,mBAAmB,WAAW,EAAG,QAAO,KAAA;CAE5C,MAAM,CAAC,SAAS;AAChB,KAAI,CAAC,SAAS,MAAM,SAAS,4BAA4B,MAAM,WAAW,SAAS,aAAc,QAAO,KAAA;AACxG,QAAO,MAAM,WAAW;;;;AC3Z1B,MAAa,2BAA2B;AACxC,MAAa,0BAA0B;AAC8B,GAAU,yBAAV;AAErE,SAAgB,qBAAqB,MAAc,KAAa;AAC9D,QAAO,QAAQ,MAAM,KAAK,wBAAwB;;;;ACWpD,MAAM,SAAS;AACf,MAAM,MAAM;AACZ,MAAM,QAAQ;AACd,MAAM,SAAS;AACf,MAAM,OAAO;AACb,MAAM,OAAO;AACb,MAAM,sBAAsB;AAC5B,MAAM,cAAc,GAAG,OAAO,MAAM;AACpC,MAAM,mBAAmB;AACzB,MAAM,0BAA0B;CAAC;CAAO;CAAQ;CAAO;CAAO;AAC9D,MAAM,eAAe,CAAC,KAAK,OAAO;AAClC,MAAM,oBAAoB,CAAC,IAAI;AAC/B,MAAM,4BAA4B;AAClC,MAAM,yBAAyB;AAC/B,MAAM,iBAAiB;AACvB,MAAM,qBAAqB;AAC3B,MAAM,qBAAqB;CAAC;CAA0C;CAAiB;CAAG,CAAC,KAAK,KAAK;AAOrG,SAAS,aAAa,QAAgB;AACpC,QAAO,OAAO,aAAa;;AAG7B,SAAS,cAAc,SAAmB;AACxC,QAAO,QAAQ,IAAI,aAAa,CAAC,KAAK,KAAK;;;AAI7C,SAAgB,kBAAkB,SAA+C;CAC/E,MAAM,EACJ,SACA,gBAAgB,QAAQ,MAAM,MAC9B,UAAU,kBACV,YAAY,kCACZ,UAAU,MACV,UAAU;EAAE,MAAM;EAAU,QAAQ;EAA0B,EAC9D,cACE;CACJ,MAAM,oBAAoB,QAAQ,SAAS;CAC3C,MAAM,aAAa,QAAQ,SAAS,WAAY,QAAQ,UAAA,eAAsC;CAC9F,MAAM,YAAY,QAAQ,SAAS,WAAY,QAAQ,OAAO,sBAAuB;CACrF,MAAM,WAA4B,EAAE;CACpC,MAAM,+BAAe,IAAI,KAAiC;CAC1D,IAAI,QAA0B,kBAAkB;CAChD,IAAI,OAAO;CACX,IAAI,QAAQ;CACZ,IAAI,iBAAuD;CAC3D,IAAI,4BAA4B;CAChC,IAAI,uBAAuB;CAC3B,IAAI,cAAwB,EAAE;CAE9B,SAAS,IAAI,SAAiB;AAC5B,MAAI,QAAS,SAAQ,IAAI,QAAQ;;CAGnC,eAAe,gBAAgB,UAA8B,SAAiB;AAC5E,MAAI,CAAC,2BAA2B;AAC9B,+BAA4B;AAC5B,OAAI,GAAG,OAAO,GAAG,YAAY,wBAAwB,MAAM,YAAY,MAAM,sBAAsB;;AAErG,SAAO,OAAO,YAAY,SAAS,KAAK,YAAY,CAAC,QAAQ,IAAI,QAAQ,KAAK,CAAC,CAAC;;CAGlF,eAAe,aAAa;AAC1B,MAAI,CAAC,sBAAsB;AACzB,0BAAuB;AACvB,OAAI,GAAG,OAAO,GAAG,YAAY,0BAA0B,MAAM,YAAY,MAAM,sBAAsB;;;CAIzG,MAAM,oBAAoB,cAAc,oBAAoB,KAAA,IAAY;CAExE,SAAS,uBAA4C;AACnD,SAAO,OAAO,YACZ,OAAO,QAAQ,SAAS,CAAC,KAAK,CAAC,IAAI,WAAW,CAC5C,IACA;GACE,gBAAgB,MAAM;GACtB,MAAM,MAAM;GACZ,cAAc,MAAM;GACpB,SAAS,MAAM;GAChB,CACF,CAAC,CACH;;CAGH,SAAS,eAAe,IAAY;EAClC,MAAM,UAAU,GAAG,MAAM,KAAK,EAAE,CAAC,MAAM;AACvC,MAAI,QAAQ,SAAS,eAAe,CAAE,QAAO;AAE7C,MAAI,CADc,wBAAwB,MAAM,QAAQ,QAAQ,SAAS,IAAI,CAAC,CAC9D,QAAO;AACvB,SAAO,YAAY,MAChB,eAAe,YAAY,cAAc,QAAQ,WAAW,GAAG,WAAW,GAAG,IAAI,QAAQ,WAAW,GAAG,WAAW,IAAI,CACxH;;CAGH,SAAS,yBAAyB;AAChC,SAAO,QAAQ,MAAM,YAAY,0BAA0B;;CAG7D,SAAS,mBAAiD;AACxD,SAAO;GACL,SAAS,oBAAoB;IAAE,MAAM;IAAU,QAAQ;IAAY,GAAG;IAAE,MAAM;IAAU,KAAK;IAAW;GACxG;GACA;GACD;;CAGH,SAAS,qBAAqB;AAC5B,MAAI,CAAC,kBAAmB;EAExB,MAAM,gBAAgB,KAAK,UAAU,kBAAkB,EAAE,MAAM,EAAE,GAAG;EACpE,MAAM,OAAO,qBAAqB,MAAM,WAAW;EACnD,MAAM,MAAM,QAAQ,KAAK;AACzB,MAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AACzD,qBAAmB,MAAM,cAAc;;CAGzC,SAAS,oBAAoB;AAC3B,SAAO,QAAQ,MAAM,YAAY,eAAe;;CAGlD,SAAS,cAAc,QAAgB;AACrC,SAAO,QAAQ,mBAAmB,EAAE,GAAG,OAAO,OAAO;;CAGvD,SAAS,mBAAmB,QAAiC;EAC3D,MAAM,OAAO,cAAc,OAAO;AAClC,MAAI,CAAC,WAAW,KAAK,CAAE,QAAO,EAAE;AAEhC,MAAI;AAEF,UAAO,wBADO,KAAK,MAAM,aAAa,MAAM,QAAQ,CAAC,CAChB;UAC/B;AACN,UAAO,EAAE;;;CAIb,SAAS,uBAAuB;AAC9B,MAAI,CAAC,kBAAmB;EACxB,MAAM,OAAO,wBAAwB;EACrC,MAAM,MAAM,QAAQ,KAAK;AACzB,MAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AACzD,qBAAmB,MAAM,KAAK,UAAU,sBAAsB,EAAE,MAAM,EAAE,GAAG,KAAK;;CAGlF,SAAS,mBAAmB,MAAc,UAAkB,OAAe;AACzE,MAAI,CAAC,WAAW,KAAK,CACnB,OAAM,IAAI,MAAM,GAAG,OAAO,qBAAqB,MAAM,MAAM,SAAS,MAAM,KAAK,GAAG;AAGpF,MADe,aAAa,MAAM,QAAQ,KAC3B,SACb,OAAM,IAAI,MACR;GACE,GAAG,OAAO,aAAa,MAAM;GAC7B,8BAA8B,SAAS,MAAM,KAAK,CAAC;GACnD;GACD,CAAC,KAAK,KAAK,CACb;;CAIL,SAAS,uBAAuB,MAAc,UAAmB,OAAe;AAC9E,MAAI,CAAC,WAAW,KAAK,CACnB,OAAM,IAAI,MAAM,GAAG,OAAO,qBAAqB,MAAM,MAAM,SAAS,MAAM,KAAK,GAAG;EAGpF,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,aAAa,MAAM,QAAQ,CAAC;UAC1C;AACN,SAAM,IAAI,MAAM,GAAG,OAAO,aAAa,MAAM,sBAAsB,SAAS,MAAM,KAAK,GAAG;;AAG5F,MAAI,KAAK,UAAU,OAAO,KAAK,KAAK,UAAU,SAAS,CACrD,OAAM,IAAI,MACR;GACE,GAAG,OAAO,aAAa,MAAM;GAC7B,8BAA8B,SAAS,MAAM,KAAK,CAAC;GACnD;GACD,CAAC,KAAK,KAAK,CACb;;CAIL,SAAS,0BAA0B;AAEjC,SAAO;GACL;GACA;GACA;GACA,2BALmB,QAAQ,KAAK,WAAW,KAAK,UAAU,OAAO,CAAC,CAAC,KAAK,KAAK,CAKrC;GACxC;GACA;GACA;GACA;GACA,uCAAuC,eAAe;GACtD;GACA;GACA;GACD,CAAC,KAAK,KAAK;;CAGd,SAAS,0BAA0B;AACjC,MAAI,CAAC,kBAAmB;EACxB,MAAM,OAAO,QAAQ,MAAM,YAAY,uBAAuB;EAC9D,MAAM,MAAM,QAAQ,KAAK;AACzB,MAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AACzD,qBAAmB,MAAM,yBAAyB,CAAC;;CAGrD,SAAS,iBAAiB;AACxB,MAAI,CAAC,kBAAmB;EACxB,MAAM,OAAO,QAAQ,MAAM,YAAY,mBAAmB;EAC1D,MAAM,MAAM,QAAQ,KAAK;AACzB,MAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AACzD,qBAAmB,MAAM,mBAAmB;;CAG9C,SAAS,gCAAgC;AACvC,MAAI,CAAC,kBAAmB;AACxB,yBAAuB,qBAAqB,MAAM,WAAW,EAAE,kBAAkB,EAAE,iBAAiB;EACpG,MAAM,mBAAmB,QAAQ,MAAM,YAAY,uBAAuB;AAC1E,MAAI,CAAC,WAAW,iBAAiB,CAC/B,OAAM,IAAI,MAAM,GAAG,OAAO,6CAA6C,SAAS,MAAM,iBAAiB,GAAG;AAE5G,qBAAmB,QAAQ,MAAM,YAAY,mBAAmB,EAAE,oBAAoB,uBAAuB;;CAG/G,SAAS,yBAAyB,QAAiC;EACjE,MAAM,mBAAmB,mBAAmB,OAAO;EACnD,MAAM,WAA4B,EAAE;AAEpC,MAAI,WAAW,eAAe;AAC5B,QAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,SAAS,CAChD,UAAS,MAAM,MAAM;AAEvB,UAAO;;AAGT,OAAK,MAAM,MAAM,OAAO,KAAK,SAAS,EAAE;AACtC,OAAI,OAAO,OAAO,UAAU,GAAG,CAAE;AACjC,OAAI,OAAO,OAAO,kBAAkB,GAAG,EAAE;AAEvC,aAAS,MADe,iBAAiB;AAEzC;;GAEF,MAAM,gBAAgB,MAAM,QAAQ,YAAY,IAAI,OAAO,GAAG;AAC9D,OAAI,kBAAkB,KAAA,EAAW,UAAS,MAAM;;AAElD,SAAO;;CAGT,SAAS,yBAAyB;AAChC,MAAI,CAAC,kBAAmB;EACxB,MAAM,MAAM,mBAAmB;AAC/B,MAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AACzD,OAAK,MAAM,UAAU,QACnB,oBAAmB,QAAQ,KAAK,GAAG,OAAO,OAAO,EAAE,KAAK,UAAU,yBAAyB,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK;AAEtH,2BAAyB;;CAG3B,SAAS,6BAA6B;EACpC,MAAM,kCAAkB,IAAI,KAAiC;AAE7D,OAAK,MAAM,UAAU,SAAS;AAC5B,OAAI,WAAW,cAAe;GAC9B,MAAM,mBAAmB,mBAAmB,OAAO;AACnD,QAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,SAAS,CAChD,KAAI,CAAC,OAAO,OAAO,kBAAkB,GAAG,IAAI,CAAC,OAAO,OAAO,MAAM,SAAS,YAAY,IAAI,OAAO,CAAC,EAAE;IAClG,MAAM,SAAS,gBAAgB,IAAI,OAAO,IAAI,EAAE;AAChD,WAAO,KAAK;KACV;KACA,MAAM,MAAM;KACZ,MAAM,MAAM;KACZ,cAAc,MAAM;KACpB,SAAS,MAAM;KAChB,CAAC;AACF,oBAAgB,IAAI,QAAQ,OAAO;;;AAKzC,SAAO;;CAGT,SAAS,uCAAuC;EAC9C,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,SAAS,CAAC;EAClD,MAAM,SAAmB,EAAE;AAE3B,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,aAAa,cAAc,OAAO;AACxC,OAAI,CAAC,WAAW,WAAW,EAAE;AAC3B,WAAO,KAAK,KAAK,OAAO,oBAAoB,SAAS,MAAM,WAAW,GAAG;AACzE;;GAGF,MAAM,iBAAiB,mBAAmB,OAAO;GACjD,MAAM,aAAa,CAAC,GAAG,YAAY,CAAC,QAAQ,OAAO,CAAC,OAAO,OAAO,gBAAgB,GAAG,CAAC;GACtF,MAAM,YAAY,OAAO,KAAK,eAAe,CAAC,QAAQ,OAAO,CAAC,YAAY,IAAI,GAAG,CAAC;AAElF,OAAI,WAAW,eAAe;IAC5B,MAAM,WAAW,CAAC,GAAG,YAAY,CAAC,QAAQ,OAAO,eAAe,QAAQ,SAAS,IAAK,eAAe;AACrG,QAAI,WAAW,SAAS,EAAG,QAAO,KAAK,kBAAkB,QAAQ,WAAW,WAAW,CAAC;AACxF,QAAI,UAAU,SAAS,EAAG,QAAO,KAAK,kBAAkB,QAAQ,YAAY,UAAU,CAAC;AACvF,QAAI,SAAS,SAAS,EAAG,QAAO,KAAK,kBAAkB,QAAQ,6BAA6B,SAAS,CAAC;AACtG;;AAGF,OAAI,WAAW,SAAS,EAAG,QAAO,KAAK,kBAAkB,QAAQ,WAAW,WAAW,CAAC;AACxF,OAAI,UAAU,SAAS,EAAG,QAAO,KAAK,kBAAkB,QAAQ,YAAY,UAAU,CAAC;;AAGzF,MAAI,OAAO,WAAW,EAAG;AAEzB,QAAM,IAAI,MACR;GACE,GAAG,OAAO;GACV;GACA;GACA,GAAG;GACJ,CAAC,KAAK,KAAK,CACb;;CAGH,eAAe,2BAA2B;AACxC,MAAI,CAAC,kBAAmB,QAAO;EAC/B,MAAM,kBAAkB,4BAA4B;EAEpD,MAAM,cAAc,CAAC,GAAG,gBAAgB,QAAQ,CAAC,CAAC,QAAQ,OAAO,WAAW,QAAQ,OAAO,QAAQ,EAAE;AACrG,MAAI,gBAAgB,EAAG,QAAO;EAE9B,MAAM,cAAc,CAAC,GAAG,gBAAgB,MAAM,CAAC;AAC/C,MACE,GAAG,OAAO,GAAG,KAAK,aAAa,MAAM,GAAG,OAAO,cAAc,MAAM,GAAG,gBAAgB,IAAI,YAAY,WAAW,MAAM,OAAO,cAAc,YAAY,GAAG,QAC5J;AAED,OAAK,MAAM,CAAC,QAAQ,WAAW,iBAAiB;GAC9C,MAAM,SAAS,MAAM,kBAAkB,QAAQ,OAAO;AAEtD,QAAK,MAAM,QAAQ,QAAQ;IACzB,MAAM,aAAa,OAAO,KAAK,OAAO,KAAK;AAC3C,UAAM,QAAQ,YAAY,KAAK,IAAI,OAAO,IAAI;KAC5C,YAAY,KAAK;KACjB,MAAM,KAAK;KACX;KACA,aAAa;KACb,WAAW,KAAK,KAAK;KACtB;;;AAIL,SAAO;;CAGT,SAAS,yBAAyB;AAChC,MAAI,CAAC,kBAAmB;AACxB,MAAI,CAAC,MAAO;AACZ,MAAI,eAAgB,cAAa,eAAe;AAChD,mBAAiB,WAAW,YAAY;AAEtC,OADmB,MAAM,0BAA0B,CACnC,WAAU,QAAQ,MAAM,UAAU,EAAE,MAAM;AAC1D,2BAAwB;AACxB,yBAAsB;KACrB,IAAK;;CAGV,SAAS,mBAAmB,MAAc;EACxC,MAAM,WAAW,aAAa,IAAI,KAAK;AACvC,MAAI,CAAC,SAAU,QAAO;AAEtB,OAAK,MAAM,WAAW,UAAU;GAC9B,MAAM,QAAQ,SAAS,QAAQ;AAC/B,OAAI,CAAC,MAAO;AACZ,SAAM,UAAU,MAAM,QAAQ,QAAQ,WAAW,CAAC,aAAa,QAAQ,QAAQ,OAAO,CAAC;AACvF,OAAI,MAAM,QAAQ,WAAW,EAAG,QAAO,SAAS,QAAQ;;AAG1D,eAAa,OAAO,KAAK;AACzB,SAAO;;CAGT,SAAS,iBAAiB,MAAc,UAA0C;EAChF,MAAM,mBAAmB,aAAa,IAAI,KAAK,IAAI,EAAE;EACrD,MAAM,cAAc,kBAAkB,SAAS;AAC/C,OAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,YAAY,EAAE;GACrD,MAAM,WAAW,SAAS;AAC1B,OAAI,YAAY,CAAC,oBAAoB,UAAU,MAAM,CACnD,OAAM,IAAI,MAAM,qBAAqB,IAAI,UAAU,MAAM,CAAC;;AAI9D,qBAAmB,KAAK;AACxB,OAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,YAAY,EAAE;AACrD,OAAI,CAAC,SAAS,KAAK;AACjB,aAAS,MAAM;AACf;;AAEF,QAAK,MAAM,UAAU,MAAM,QACzB,KAAI,CAAC,SAAS,IAAK,QAAQ,MAAM,mBAAmB,aAAa,gBAAgB,OAAO,CAAC,CACvF,UAAS,IAAK,QAAQ,KAAK,OAAO;;AAKxC,MAAI,SAAS,SAAS,EAAG,cAAa,IAAI,MAAM,SAAS;AACzD,SAAO;GACL,iBACE,iBAAiB,WAAW,SAAS,UACrC,iBAAiB,MAAM,SAAS,UAAU,CAAC,uBAAuB,SAAS,SAAS,OAAO,CAAC;GAC9F,uBACE,iBAAiB,WAAW,SAAS,UACrC,iBAAiB,MAAM,SAAS,UAAU,CAAC,oBAAoB,SAAS,SAAS,OAAQ,CAAC;GAC7F;;CAGH,SAAS,eAAe,MAAc,MAAc;EAClD,MAAM,WAAW,kBAAkB,MAAM,MAAM;GAC7C,MAAM;GACN,WAAW;GACX;GACD,CAAC;AACF,MAAI,CAAC,SAAS,OAAQ,QAAO;AAC7B,SAAO,iBACL,MACA,SAAS,SAAS,KAAK,aAAa;GAClC,GAAG;GACH,QAAQ;IACN,GAAG,QAAQ;IACX,MAAM,mBAAmB,QAAQ,OAAO,KAAK;IAC9C;GACF,EAAE,CACJ;;CAGH,SAAS,kBAAkB,MAA0B;EACnD,MAAM,sBAAsB,mBAAmB,KAAK;AACpD,SAAO;GACL,iBAAiB;GACjB,uBAAuB;GACxB;;CAGH,SAAS,gBAAgB,YAA+B,SAA2C;AACjG,MAAI,CAAC,WAAY;AACjB,MAAI,WAAW,uBAAuB;AACpC,2BAAwB;AACxB,yBAAsB;AACtB,OAAI,QAAQ,oBAAqB,yBAAwB;AACzD;;AAEF,MAAI,WAAW,gBAAiB,uBAAsB;;CAGxD,SAAS,qBAAqB;AAC5B,OAAK,MAAM,MAAM,OAAO,KAAK,SAAS,CAAE,QAAO,SAAS;AACxD,eAAa,OAAO;AAEpB,OAAK,MAAM,cAAc,aAAa;AACpC,OAAI,CAAC,WAAW,WAAW,CAAE;AAC7B,QAAK,MAAM,QAAQ,iBAAiB,WAAW,CAAC,MAAM,CAEpD,gBAAe,MADF,aAAa,MAAM,QAAQ,CACd;;;CAKhC,SAAS,mBAAmB,MAAc;AACxC,SAAO,SAAS,MAAM,KAAK,CAAC,WAAW,MAAM,IAAI;;AAGnD,QAAO;EACL,MAAM;EACN,SAAS;EAET,eAAe,QAAQ;AACrB,UAAO,OAAO;AACd,WAAQ,OAAO,YAAY;AAC3B,kBAAe,MAAM,QAAQ,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,QAAQ,QAAQ,MAAM,IAAI,CAAC;AAC7F,OACE,GAAG,OAAO,YAAY,OAAO,cAAc,QAAQ,GAAG,MAAM,cAAc,OAAO,aAAa,cAAc,GAAG,MAAM,cAAc,OAAO,oBAAoB,WAAW,WAAW,MAAM,cAAc,MAAM,oBAAoB,aAAa,QAAQ,MAAM,YAAY,OAAO,MAAM,QAAQ,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,KAAK,GAAG,QAC5U;;EAGH,aAAa;AACX,WAAQ,UAAU,QAAQ,MAAM,UAAU,CAAC;AAC3C,uBAAoB;AACpB,OAAI,qBAAqB,CAAC,OAAO;AAC/B,mCAA+B;AAC/B,0CAAsC;AACtC;;AAEF,uBAAoB;AACpB,4BAAyB;AACzB,mBAAgB;AAChB,2BAAwB;AACxB,yBAAsB;AAEtB,OAAI,MAAO,yBAAwB;;EAGrC,gBAAgB,QAAQ;AACtB,UAAO,QAAQ,IAAI,YAAY;GAE/B,MAAM,oBAAoB,SAAiB;AACzC,QAAI,CAAC,eAAe,KAAK,IAAI,CAAC,WAAW,KAAK,CAAE;AAChD,oBAAgB,eAAe,MAAM,aAAa,MAAM,QAAQ,CAAC,EAAE,EAAE,qBAAqB,MAAM,CAAC;;GAGnG,MAAM,0BAA0B,SAAiB;AAC/C,QAAI,CAAC,eAAe,KAAK,CAAE;AAC3B,oBAAgB,kBAAkB,KAAK,EAAE,EAAE,qBAAqB,MAAM,CAAC;;AAGzE,UAAO,QAAQ,GAAG,OAAO,iBAAiB;AAC1C,UAAO,QAAQ,GAAG,UAAU,iBAAiB;AAC7C,UAAO,QAAQ,GAAG,UAAU,uBAAuB;;EAGrD,UAAU,MAAM,IAAI;GAClB,MAAM,UAAU,GAAG,MAAM,KAAK,EAAE,CAAC,MAAM;AACvC,OAAI,CAAC,eAAe,QAAQ,CAAE;GAE9B,MAAM,WAAW,kBAAkB,MAAM,SAAS;IAChD,MAAM;IACN,WAAW;IACX;IACD,CAAC;AAEF,OAAI,SAAS,MAAM,WAAW,EAAG;AACjC,UAAO;IACL,MAAM,WAAW,MAAM,SAAS,MAAM;IACtC,KAAK;IACN;;EAGH,MAAM,iBAAiB;AACrB,OAAI,mBAAmB;AACrB,QAAI,CAAC,MACH,gCAA+B;AAEjC,0CAAsC;SAEtC,OAAM,0BAA0B;AAGlC,OAAI,CAAC,kBACH,OAAM,YAAY;;EAItB,cAAc;AACZ,OAAI,qBAAqB,CAAC,MAAO;AACjC,aAAU,QAAQ,MAAM,UAAU,EAAE,MAAM;;EAE7C;;AAGH,SAAS,kBAAkB,QAAgB,OAAe,KAAe;AAMvE,QAAO,KAAK,OAAO,IAAI,MAAM,IALb,IACb,MAAM,GAAG,EAAE,CACX,KAAK,OAAO,KAAK,UAAU,GAAG,CAAC,CAC/B,KAAK,KAAK,GACE,IAAI,SAAS,IAAI,SAAS,IAAI,SAAS,EAAE,SAAS,GACf;;AAGpD,SAAS,wBAAwB,OAAiC;AAChE,KAAI,kBAAkB,MAAM,CAAE,QAAO;AACrC,KACE,OAAO,UAAU,YACjB,UAAU,QACV,cAAc,SACd,OAAO,MAAM,aAAa,YAC1B,MAAM,aAAa,KAEnB,QAAO,OAAO,YACZ,OAAO,QAAQ,MAAM,SAAS,CAAC,SAAS,CAAC,IAAI,WAC3C,OAAO,UAAU,YAAY,UAAU,QAAQ,iBAAiB,SAAS,OAAO,MAAM,gBAAgB,WAClG,CAAC,CAAC,IAAI,MAAM,YAAY,CAAC,GACzB,EAAE,CACP,CACF;AAEH,QAAO,EAAE;;AAGX,SAAS,mBAAmB,MAAc,UAAkB;AAC1D,KAAI,WAAW,KAAK,IAAI,aAAa,MAAM,QAAQ,KAAK,SAAU,QAAO;AACzE,eAAc,MAAM,SAAS;AAC7B,QAAO;;AAGT,SAAS,iBAAiB,MAAc;CACtC,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,SAAS,YAAY,MAAM,EAAE,eAAe,MAAM,CAAC,EAAE;EAC9D,MAAM,OAAO,QAAQ,MAAM,MAAM,KAAK;AACtC,MAAI,MAAM,aAAa,EAAE;AACvB,OAAI,MAAM,SAAS,eAAgB;AACnC,SAAM,KAAK,GAAG,iBAAiB,KAAK,CAAC;AACrC;;AAEF,QAAM,KAAK,KAAK;;AAElB,QAAO;;AAGT,SAAS,kBAAkB,OAA0C;AACnE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAO,OAAO,MAAM,CAAC,OAAO,UAAU,OAAO,UAAU,SAAS;;AAGxH,SAAS,WAAW,MAAc,OAAmE;CACnG,IAAI,cAAc;AAClB,MAAK,MAAM,QAAQ,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CAC7D,eAAc,GAAG,YAAY,MAAM,GAAG,KAAK,MAAM,GAAG,KAAK,cAAc,YAAY,MAAM,KAAK,IAAI;AAEpG,QAAO;;AAGT,SAAS,kBAAkB,UAA+C;CACxE,MAAM,UAA2B,EAAE;AAEnC,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,WAAW,QAAQ,QAAQ;AACjC,MAAI,YAAY,CAAC,oBAAoB,UAAU,QAAQ,CACrD,OAAM,IAAI,MAAM,qBAAqB,QAAQ,IAAI,UAAU,QAAQ,CAAC;AAEtE,MAAI,CAAC,UAAU;AACb,WAAQ,QAAQ,MAAM;IACpB,gBAAgB,QAAQ;IACxB,MAAM,QAAQ;IACd,cAAc,QAAQ;IACtB,SAAS,CAAC,QAAQ,OAAO;IAC1B;AACD;;AAEF,MAAI,CAAC,SAAS,QAAQ,MAAM,WAAW,aAAa,QAAQ,QAAQ,OAAO,CAAC,CAC1E,UAAS,QAAQ,KAAK,QAAQ,OAAO;;AAIzC,QAAO;;AAGT,SAAS,oBACP,UACA,UACA;AACA,QACE,SAAS,mBAAmB,SAAS,kBACrC,cAAc,SAAS,KAAK,KAAK,cAAc,SAAS,KAAK,IAC7D,KAAK,UAAU,SAAS,aAAa,KAAK,KAAK,UAAU,SAAS,aAAa;;AAInF,SAAS,aAAa,MAAqB,OAAsB;AAC/D,QACE,KAAK,SAAS,MAAM,QACpB,KAAK,SAAS,MAAM,QACpB,KAAK,WAAW,MAAM,UACtB,KAAK,UAAU,MAAM,SACrB,KAAK,QAAQ,MAAM;;AAIvB,SAAS,uBAAuB,MAAwB,OAA0B;AAChF,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO,oBAAoB,MAAM,MAAM,IAAI,aAAa,KAAK,QAAQ,MAAM,OAAO;;AAGpF,SAAS,qBACP,IACA,UACA,UACA;CACA,MAAM,kBAAkB,cAAc,SAAS,QAAQ;CACvD,MAAM,kBAAkB,cAAc,YAAY,WAAW,CAAC,SAAS,OAAO,GAAG,SAAS,QAAQ;AAClG,QAAO;EACL,GAAG,OAAO,sCAAsC,KAAK,GAAG,GAAG,GAAG;EAC9D,aAAa,KAAK,UAAU;GAAE,gBAAgB,SAAS;GAAgB,MAAM,SAAS;GAAM,cAAc,SAAS;GAAc,CAAC;EAClI,qBAAqB;EACrB,aAAa,KAAK,UAAU;GAAE,gBAAgB,SAAS;GAAgB,MAAM,SAAS;GAAM,cAAc,SAAS;GAAc,CAAC;EAClI,qBAAqB;EACtB,CAAC,KAAK,KAAK;;AAGd,SAAS,cAAc,SAA0B;AAC/C,QAAO,QAAQ,KAAK,WAAW,GAAG,OAAO,KAAK,GAAG,OAAO,KAAK,GAAG,OAAO,SAAS,CAAC,KAAK,KAAK"}
|
|
1
|
+
{"version":3,"file":"vite.mjs","names":[],"sources":["../src/cache.ts","../src/extractor.ts","../src/runtime-config.ts","../src/plugin.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { dirname } from \"node:path\"\n\nimport type { TranslationCache } from \"./types.js\"\n\nconst CURRENT_VERSION = 1\n\nexport function createEmptyCache(): TranslationCache {\n return { version: CURRENT_VERSION, entries: {} }\n}\n\n/** Loads the translation cache from disk, resetting it when the schema version changes. */\nexport function loadCache(path: string): TranslationCache {\n if (!existsSync(path)) return createEmptyCache()\n try {\n const data = JSON.parse(readFileSync(path, \"utf-8\")) as TranslationCache\n if (data.version !== CURRENT_VERSION) return createEmptyCache()\n return data\n } catch {\n return createEmptyCache()\n }\n}\n\n/** Persists the translation cache so future runs can reuse existing translations. */\nexport function saveCache(path: string, cache: TranslationCache) {\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n const next = JSON.stringify(cache, null, 2)\n if (existsSync(path) && readFileSync(path, \"utf-8\") === next) return\n writeFileSync(path, next)\n}\n\n/** Builds the cache key used to distinguish translations by stable message id and locale. */\nexport function getCacheKey(messageId: string, locale: string) {\n return `${messageId}\\0${locale}`\n}\n","import type { Argument, JSXChild, StringLiteral } from \"oxc-parser\"\nimport { parseSync, Visitor } from \"oxc-parser\"\n\nimport type { ExtractedMessage, MessageSource, TranslateOptions } from \"./types.js\"\n\nimport { getCallMessageId, getMessageId } from \"./message-id.js\"\n\ninterface Markers {\n call: string[]\n component: string[]\n logging: boolean\n}\n\nexport interface SourceEdit {\n start: number\n end: number\n replacement: string\n}\n\nexport interface SourceAnalysis {\n parsed: boolean\n messages: ExtractedMessage[]\n edits: SourceEdit[]\n}\n\n/** Extracts messages and source edits from a file in one coordinated parse pass. */\nexport function analyzeSourceFile(code: string, filename: string, markers: Markers): SourceAnalysis {\n const messages: ExtractedMessage[] = []\n const edits: SourceEdit[] = []\n const result = parseSync(filename, code)\n if (result.errors.length > 0) return { parsed: false, messages, edits }\n const lineStarts = getLineStarts(code)\n\n const visitor = new Visitor({\n CallExpression(node) {\n if (\n node.callee.type === \"Identifier\" &&\n markers.call.includes(node.callee.name) &&\n node.arguments.length >= 1 &&\n isStringLiteral(node.arguments[0]!)\n ) {\n const value = (node.arguments[0] as StringLiteral).value\n const meta = getCallMetaArgument(node.arguments)\n const id = getCallMessageId(value, meta)\n messages.push({\n id,\n defaultMessage: value,\n meta: meta ?? {},\n placeholders: extractPlaceholdersFromMessage(value),\n source: createSource({\n filename,\n lineStarts,\n marker: node.callee.name,\n kind: \"call\",\n start: node.start,\n end: node.end,\n }),\n })\n\n const callOptionsEdit = createCallOptionsEdit(code, node.arguments, id)\n if (callOptionsEdit) edits.push(callOptionsEdit)\n }\n },\n\n JSXElement(node) {\n const opening = node.openingElement\n if (\n opening.name.type === \"JSXIdentifier\" &&\n opening.name.name === \"Var\" &&\n (opening.attributes as Array<unknown>).length === 0\n ) {\n const identifier = getVarChildIdentifier(node.children)\n if (identifier) {\n edits.push({\n start: node.start,\n end: node.end,\n replacement: `<Var ${identifier}={${identifier}} />`,\n })\n }\n }\n\n if (opening.name.type !== \"JSXIdentifier\") return\n if (!markers.component.includes(opening.name.name)) return\n\n const extraction = extractJSXChildren(node.children)\n if (!extraction.valid) {\n if (markers.logging) {\n console.warn(`[better-translation] Non-static <${opening.name.name}> in ${filename}, skipping`)\n }\n return\n }\n\n const context = getJSXStringAttribute(opening.attributes, \"context\")\n const meta = context ? { context } : undefined\n const id = getJSXStringAttribute(opening.attributes, \"id\") ?? getMessageId(extraction.message, meta)\n messages.push({\n id,\n defaultMessage: extraction.message,\n meta: meta ?? {},\n placeholders: extraction.placeholders,\n source: createSource({\n filename,\n lineStarts,\n marker: opening.name.name,\n kind: \"component\",\n start: node.start,\n end: node.end,\n }),\n })\n\n if (!hasJSXAttribute(opening.attributes as Array<unknown>, \"id\")) {\n edits.push({\n start: opening.name.end,\n end: opening.name.end,\n replacement: ` id=\"${id}\"`,\n })\n }\n },\n })\n\n visitor.visit(result.program)\n return { parsed: true, messages, edits }\n}\n\nfunction isStringLiteral(node: Argument): node is StringLiteral {\n return node.type === \"Literal\" && typeof (node as StringLiteral).value === \"string\"\n}\n\nfunction getMetaArgument(node?: Argument) {\n if (!node) return undefined\n if (isStringLiteral(node)) return { context: node.value }\n\n if (node.type !== \"ObjectExpression\") return undefined\n\n const meta: TranslateOptions = {}\n\n for (const property of node.properties as Array<{\n type: string\n key?: { type: string; name?: string; value?: unknown }\n value?: Argument\n }>) {\n if (\n (property.type === \"ObjectProperty\" || property.type === \"Property\") &&\n ((property.key?.type === \"Identifier\" && (property.key.name === \"context\" || property.key.name === \"id\")) ||\n (property.key?.type === \"Literal\" && (property.key.value === \"context\" || property.key.value === \"id\"))) &&\n property.value &&\n isStringLiteral(property.value)\n ) {\n const key = property.key?.type === \"Identifier\" ? property.key.name : property.key?.value\n if (key === \"context\" || key === \"id\") meta[key] = property.value.value\n }\n }\n\n return Object.keys(meta).length > 0 ? meta : undefined\n}\n\nfunction getCallMetaArgument(args: Argument[]) {\n return getMetaArgument(isTranslateOptionsArgument(args[1]) ? args[1] : args[2])\n}\n\nfunction createCallOptionsEdit(code: string, args: Argument[], id: string) {\n const valuesArg = args[1]\n const optionsArg = isTranslateOptionsArgument(valuesArg) ? valuesArg : args[2]\n\n if (!optionsArg) {\n return {\n start: (valuesArg ?? args[0]!).end,\n end: (valuesArg ?? args[0]!).end,\n replacement: `, { id: ${JSON.stringify(id)} }`,\n }\n }\n\n if (isStringLiteral(optionsArg)) {\n const contextValue = code.slice(optionsArg.start, optionsArg.end)\n return {\n start: optionsArg.start,\n end: optionsArg.end,\n replacement: `{ id: ${JSON.stringify(id)}, context: ${contextValue} }`,\n }\n }\n\n if (optionsArg.type !== \"ObjectExpression\") return undefined\n if (hasObjectProperty(optionsArg, \"id\")) return undefined\n\n const objectSource = code.slice(optionsArg.start, optionsArg.end)\n const innerSource = objectSource.slice(1, -1)\n const replacement =\n innerSource.trim().length > 0 ? `{ id: ${JSON.stringify(id)},${innerSource} }` : `{ id: ${JSON.stringify(id)} }`\n\n return {\n start: optionsArg.start,\n end: optionsArg.end,\n replacement,\n }\n}\n\nfunction isTranslateOptionsArgument(node?: Argument) {\n if (!node) return false\n if (isStringLiteral(node)) return true\n if (node.type !== \"ObjectExpression\") return false\n\n return (node.properties as Array<unknown>).every((entry) => {\n const property = entry as\n | {\n type?: string\n key?: { type?: string; name?: string; value?: unknown }\n }\n | undefined\n\n if (property?.type !== \"ObjectProperty\" && property?.type !== \"Property\") return false\n\n return (\n (property.key?.type === \"Identifier\" && (property.key.name === \"context\" || property.key.name === \"id\")) ||\n (property.key?.type === \"Literal\" && (property.key.value === \"context\" || property.key.value === \"id\"))\n )\n })\n}\n\nfunction getJSXStringAttribute(attributes: Array<unknown>, name: string) {\n for (const attr of attributes as Array<{\n type: string\n name?: { type: string; name?: string }\n value?: { type: string; value?: unknown } | null\n }>) {\n if (\n attr.type === \"JSXAttribute\" &&\n attr.name?.type === \"JSXIdentifier\" &&\n attr.name.name === name &&\n attr.value?.type === \"Literal\" &&\n typeof attr.value.value === \"string\"\n ) {\n return attr.value.value as string\n }\n }\n}\n\nfunction hasJSXAttribute(attributes: Array<unknown>, name: string) {\n return attributes.some((attr) => {\n const attribute = attr as\n | {\n type?: string\n name?: { type?: string; name?: string }\n }\n | undefined\n\n return attribute?.type === \"JSXAttribute\" && attribute.name?.type === \"JSXIdentifier\" && attribute.name.name === name\n })\n}\n\nfunction hasObjectProperty(node: { properties: Array<unknown> }, name: string) {\n return node.properties.some((entry) => {\n const property = entry as\n | {\n type?: string\n key?: { type?: string; name?: string; value?: unknown }\n }\n | undefined\n\n if (property?.type !== \"ObjectProperty\" && property?.type !== \"Property\") return false\n return (\n (property.key?.type === \"Identifier\" && property.key.name === name) ||\n (property.key?.type === \"Literal\" && property.key.value === name)\n )\n })\n}\n\nfunction extractPlaceholdersFromMessage(message: string) {\n const names = new Set<string>()\n for (const match of message.matchAll(/\\{(\\w+)\\}/g)) {\n if (match[1]) names.add(match[1])\n }\n return [...names]\n}\n\nfunction createSource({\n filename,\n lineStarts,\n marker,\n kind,\n start,\n end,\n}: {\n filename: string\n lineStarts: number[]\n marker: string\n kind: MessageSource[\"kind\"]\n start: number\n end: number\n}): MessageSource {\n const startPosition = getPosition(start, lineStarts)\n const endPosition = getPosition(end, lineStarts)\n\n return {\n file: filename,\n kind,\n marker,\n line: startPosition.line,\n column: startPosition.column,\n endLine: endPosition.line,\n endColumn: endPosition.column,\n start,\n end,\n }\n}\n\nfunction getLineStarts(code: string) {\n const starts = [0]\n for (let i = 0; i < code.length; i++) {\n if (code[i] === \"\\n\") starts.push(i + 1)\n }\n return starts\n}\n\nfunction getPosition(offset: number, lineStarts: number[]) {\n let low = 0\n let high = lineStarts.length - 1\n\n while (low <= high) {\n const mid = Math.floor((low + high) / 2)\n const lineStart = lineStarts[mid]!\n const nextLineStart = lineStarts[mid + 1] ?? Number.POSITIVE_INFINITY\n\n if (offset < lineStart) {\n high = mid - 1\n continue\n }\n\n if (offset >= nextLineStart) {\n low = mid + 1\n continue\n }\n\n return { line: mid + 1, column: offset - lineStart + 1 }\n }\n\n const lastLine = lineStarts.length - 1\n const lastStart = lineStarts[lastLine] ?? 0\n return { line: lastLine + 1, column: offset - lastStart + 1 }\n}\n\ninterface ExtractionResult {\n message: string\n placeholders: string[]\n valid: boolean\n}\n\nfunction extractJSXChildren(children: Array<JSXChild>): ExtractionResult {\n const parts: string[] = []\n const placeholders: string[] = []\n\n for (const child of children) {\n switch (child.type) {\n case \"JSXText\":\n parts.push(child.value)\n break\n\n case \"JSXElement\": {\n const name = child.openingElement.name\n if (name.type !== \"JSXIdentifier\" || name.name !== \"Var\") {\n return { message: \"\", placeholders: [], valid: false }\n }\n\n const varName = getVarPlaceholderName(child)\n if (!varName) return { message: \"\", placeholders: [], valid: false }\n\n placeholders.push(varName)\n parts.push(`{${varName}}`)\n break\n }\n\n case \"JSXExpressionContainer\":\n if (child.expression.type !== \"JSXEmptyExpression\") {\n return { message: \"\", placeholders: [], valid: false }\n }\n break\n\n default:\n break\n }\n }\n\n const message = parts.join(\"\").replace(/\\s+/g, \" \").trim()\n return { message, placeholders, valid: message.length > 0 }\n}\n\nfunction getVarPlaceholderName(node: { openingElement: { attributes: Array<unknown> }; children: Array<JSXChild> }) {\n const explicitName = getJSXStringAttribute(node.openingElement.attributes as Array<unknown>, \"name\")\n if (explicitName) return explicitName\n\n const customPropName = getSingleJSXAttributeName(node.openingElement.attributes as Array<unknown>)\n if (customPropName) return customPropName\n\n return getVarChildIdentifier(node.children)\n}\n\nfunction getSingleJSXAttributeName(attributes: Array<unknown>) {\n const keys = attributes.flatMap((attr) => {\n const attribute = attr as\n | {\n type?: string\n name?: { type?: string; name?: string }\n }\n | undefined\n\n if (attribute?.type !== \"JSXAttribute\" || attribute.name?.type !== \"JSXIdentifier\") return []\n return [attribute.name.name]\n })\n\n return keys.length === 1 ? keys[0] : undefined\n}\n\nfunction getVarChildIdentifier(children: Array<JSXChild>) {\n const meaningfulChildren = children.filter((child) => !(child.type === \"JSXText\" && child.value.trim().length === 0))\n if (meaningfulChildren.length !== 1) return undefined\n\n const [child] = meaningfulChildren\n if (!child || child.type !== \"JSXExpressionContainer\" || child.expression.type !== \"Identifier\") return undefined\n return child.expression.name\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nimport type { BetterTranslateRuntimeConfig } from \"./types.js\"\n\nexport const DEFAULT_LOCAL_OUTPUT_DIR = \"src/lib/bt\"\nexport const RUNTIME_CONFIG_FILENAME = \"runtime.json\"\nexport const COMMON_RUNTIME_CONFIG_DIRS = [DEFAULT_LOCAL_OUTPUT_DIR, `assets/${DEFAULT_LOCAL_OUTPUT_DIR}`]\n\nexport function getRuntimeConfigPath(root: string, dir: string) {\n return resolve(root, dir, RUNTIME_CONFIG_FILENAME)\n}\n\nexport function getRuntimeConfigCandidatePaths(importMetaUrl: string, dirs = COMMON_RUNTIME_CONFIG_DIRS) {\n return getSearchBaseDirs(importMetaUrl).flatMap((baseDir) => dirs.map((dir) => resolve(baseDir, dir, RUNTIME_CONFIG_FILENAME)))\n}\n\nexport function getLocalConfigCandidatePaths(dir: string, importMetaUrl: string, fileName: string) {\n return getSearchBaseDirs(importMetaUrl).map((baseDir) => resolve(baseDir, dir, fileName))\n}\n\nexport function readRuntimeConfigFromPaths(paths: string[]) {\n for (const path of dedupe(paths)) {\n if (!existsSync(path)) continue\n try {\n return JSON.parse(readFileSync(path, \"utf-8\")) as BetterTranslateRuntimeConfig\n } catch {\n continue\n }\n }\n return null\n}\n\nfunction dedupe(paths: string[]) {\n return [...new Set(paths)]\n}\n\nfunction getSearchBaseDirs(importMetaUrl: string) {\n const currentDir = dirname(fileURLToPath(importMetaUrl))\n const candidates = [...collectParentDirs(process.cwd()), ...collectParentDirs(currentDir)]\n return dedupe(candidates)\n}\n\nfunction collectParentDirs(startDir: string, maxDepth = 6) {\n const dirs: string[] = []\n let currentDir = startDir\n\n for (let i = 0; i <= maxDepth; i++) {\n dirs.push(currentDir)\n const parentDir = dirname(currentDir)\n if (parentDir === currentDir) break\n currentDir = parentDir\n }\n\n return dirs\n}\n","import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, relative, resolve } from \"node:path\"\nimport type { Plugin } from \"vite\"\n\nimport type {\n BetterTranslatePluginOptions,\n BetterTranslateRuntimeConfig,\n ExtractedMessage,\n ManifestEntry,\n MessageManifest,\n MessageManifestFile,\n MessageSource,\n RuntimeMessages,\n TranslateMessage,\n TranslationCache,\n} from \"./types.js\"\n\nimport { createEmptyCache, getCacheKey, loadCache, saveCache } from \"./cache.js\"\nimport { analyzeSourceFile } from \"./extractor.js\"\nimport { serializeMeta } from \"./message-id.js\"\nimport { DEFAULT_LOCAL_OUTPUT_DIR, getRuntimeConfigPath } from \"./runtime-config.js\"\n\nconst PREFIX = \"\\x1b[36m[better-translation]\\x1b[0m\"\nconst DIM = \"\\x1b[2m\"\nconst RESET = \"\\x1b[0m\"\nconst YELLOW = \"\\x1b[33m\"\nconst BOLD = \"\\x1b[1m\"\nconst CYAN = \"\\x1b[36m\"\nconst REMOTE_API_BASE_URL = \"https://better-translation.com\"\nconst REMOTE_STUB = `${YELLOW}stub${RESET}`\nconst DEFAULT_ROOT_DIR = \"src\"\nconst DEFAULT_SCAN_EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".jsx\"]\nconst CALL_MARKERS = [\"t\", \"useT\"]\nconst COMPONENT_MARKERS = [\"T\"]\nconst PRIVATE_MANIFEST_FILENAME = \"manifest.json\"\nconst LOAD_MESSAGES_FILENAME = \"load-messages.ts\"\nconst LOCALES_SUBDIR = \"locales\"\nconst GITIGNORE_FILENAME = \".gitignore\"\nconst GITIGNORE_CONTENTS = [\"# Generated by better-translation/vite\", \"manifest.json\", \"\"].join(\"\\n\")\n\ninterface SyncResult {\n manifestChanged: boolean\n localeMessagesChanged: boolean\n}\n\nfunction formatLocale(locale: string) {\n return locale.toUpperCase()\n}\n\nfunction formatLocales(locales: string[]) {\n return locales.map(formatLocale).join(\", \")\n}\n\n/** Scans source files for translatable messages and keeps locale JSON files in sync. */\nexport function betterTranslation(options: BetterTranslatePluginOptions): Plugin {\n const {\n locales,\n defaultLocale = locales[0] ?? \"en\",\n rootDir = DEFAULT_ROOT_DIR,\n cacheFile = \".cache/better-translation.json\",\n logging = true,\n storage = { type: \"bundle\", output: DEFAULT_LOCAL_OUTPUT_DIR },\n translate,\n } = options\n const usesBundleStorage = storage.type === \"bundle\"\n const localesDir = storage.type === \"bundle\" ? (storage.output ?? DEFAULT_LOCAL_OUTPUT_DIR) : DEFAULT_LOCAL_OUTPUT_DIR\n const remoteUrl = storage.type === \"remote\" ? (storage.url ?? REMOTE_API_BASE_URL) : REMOTE_API_BASE_URL\n const manifest: MessageManifest = {}\n const fileMessages = new Map<string, ExtractedMessage[]>()\n let cache: TranslationCache = createEmptyCache()\n let root = \"\"\n let isDev = false\n let translateTimer: ReturnType<typeof setTimeout> | null = null\n let warnedRemoteTranslateStub = false\n let warnedRemoteSyncStub = false\n let sourceRoots: string[] = []\n\n function log(message: string) {\n if (logging) console.log(message)\n }\n\n async function remoteTranslate(messages: TranslateMessage[], _locale: string) {\n if (!warnedRemoteTranslateStub) {\n warnedRemoteTranslateStub = true\n log(`${PREFIX} ${REMOTE_STUB} remote translate via ${DIM}${remoteUrl}${RESET} not implemented yet`)\n }\n return Object.fromEntries(messages.map((message) => [message.id, message.text])) as Record<string, string>\n }\n\n async function syncRemote() {\n if (!warnedRemoteSyncStub) {\n warnedRemoteSyncStub = true\n log(`${PREFIX} ${REMOTE_STUB} remote locale sync via ${DIM}${remoteUrl}${RESET} not implemented yet`)\n }\n }\n\n const resolvedTranslate = translate ?? (usesBundleStorage ? undefined : remoteTranslate)\n\n function buildMessageManifest(): MessageManifestFile {\n return Object.fromEntries(\n Object.entries(manifest).map(([id, entry]) => [\n id,\n {\n defaultMessage: entry.defaultMessage,\n meta: entry.meta,\n placeholders: entry.placeholders,\n sources: entry.sources,\n },\n ]),\n )\n }\n\n function shouldScanFile(id: string) {\n const cleanId = id.split(\"?\", 1)[0] ?? id\n if (cleanId.includes(\"node_modules\")) return false\n const extension = DEFAULT_SCAN_EXTENSIONS.find((ext) => cleanId.endsWith(ext))\n if (!extension) return false\n return sourceRoots.some(\n (sourceRoot) => cleanId === sourceRoot || cleanId.startsWith(`${sourceRoot}/`) || cleanId.startsWith(`${sourceRoot}\\\\`),\n )\n }\n\n function getPrivateManifestPath() {\n return resolve(root, localesDir, PRIVATE_MANIFEST_FILENAME)\n }\n\n function getRuntimeConfig(): BetterTranslateRuntimeConfig {\n return {\n storage: usesBundleStorage ? { type: \"bundle\", output: localesDir } : { type: \"remote\", url: remoteUrl },\n defaultLocale,\n locales,\n }\n }\n\n function writeRuntimeConfig() {\n if (!usesBundleStorage) return\n\n const runtimeConfig = JSON.stringify(getRuntimeConfig(), null, 2) + \"\\n\"\n const path = getRuntimeConfigPath(root, localesDir)\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, runtimeConfig)\n }\n\n function getLocalesDirPath() {\n return resolve(root, localesDir, LOCALES_SUBDIR)\n }\n\n function getLocalePath(locale: string) {\n return resolve(getLocalesDirPath(), `${locale}.json`)\n }\n\n function readLocaleMessages(locale: string): RuntimeMessages {\n const path = getLocalePath(locale)\n if (!existsSync(path)) return {}\n\n try {\n const input = JSON.parse(readFileSync(path, \"utf-8\")) as unknown\n return normalizeLocaleMessages(input)\n } catch {\n return {}\n }\n }\n\n function writePrivateManifest() {\n if (!usesBundleStorage) return\n const path = getPrivateManifestPath()\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, JSON.stringify(buildMessageManifest(), null, 2) + \"\\n\")\n }\n\n function assertFileContents(path: string, expected: string, label: string) {\n if (!existsSync(path)) {\n throw new Error(`${PREFIX} missing committed ${label} at ${relative(root, path)}`)\n }\n const actual = readFileSync(path, \"utf-8\")\n if (actual !== expected) {\n throw new Error(\n [\n `${PREFIX} committed ${label} is out of date`,\n `expected committed file at ${relative(root, path)} to match the generated output`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ].join(\"\\n\"),\n )\n }\n }\n\n function assertJsonFileContents(path: string, expected: unknown, label: string) {\n if (!existsSync(path)) {\n throw new Error(`${PREFIX} missing committed ${label} at ${relative(root, path)}`)\n }\n\n let actual: unknown\n try {\n actual = JSON.parse(readFileSync(path, \"utf-8\")) as unknown\n } catch {\n throw new Error(`${PREFIX} committed ${label} is invalid JSON at ${relative(root, path)}`)\n }\n\n if (JSON.stringify(actual) !== JSON.stringify(expected)) {\n throw new Error(\n [\n `${PREFIX} committed ${label} is out of date`,\n `expected committed file at ${relative(root, path)} to match the generated output`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ].join(\"\\n\"),\n )\n }\n }\n\n function buildLoadMessagesModule() {\n const localeValues = locales.map((locale) => JSON.stringify(locale)).join(\", \")\n return [\n \"// Auto-generated by better-translation/vite - do not edit.\",\n \"// Delete this file to regenerate from your plugin `locales` config.\",\n \"\",\n `export const locales = [${localeValues}] as const`,\n \"\",\n \"export type AppLocale = (typeof locales)[number]\",\n \"\",\n \"export async function loadMessages(locale: AppLocale): Promise<Record<string, string>> {\",\n ` const messages = await import(\\`./${LOCALES_SUBDIR}/\\${locale}.json\\`)`,\n \" return messages.default\",\n \"}\",\n \"\",\n ].join(\"\\n\")\n }\n\n function writeLoadMessagesModule() {\n if (!usesBundleStorage) return\n const path = resolve(root, localesDir, LOAD_MESSAGES_FILENAME)\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, buildLoadMessagesModule())\n }\n\n function writeGitignore() {\n if (!usesBundleStorage) return\n const path = resolve(root, localesDir, GITIGNORE_FILENAME)\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, GITIGNORE_CONTENTS)\n }\n\n function assertGeneratedFilesCommitted() {\n if (!usesBundleStorage) return\n assertJsonFileContents(getRuntimeConfigPath(root, localesDir), getRuntimeConfig(), \"runtime config\")\n const loadMessagesPath = resolve(root, localesDir, LOAD_MESSAGES_FILENAME)\n if (!existsSync(loadMessagesPath)) {\n throw new Error(`${PREFIX} missing committed load-messages module at ${relative(root, loadMessagesPath)}`)\n }\n assertFileContents(resolve(root, localesDir, GITIGNORE_FILENAME), GITIGNORE_CONTENTS, \"generated .gitignore\")\n }\n\n function buildLocalLocaleMessages(locale: string): RuntimeMessages {\n const existingMessages = readLocaleMessages(locale)\n const messages: RuntimeMessages = {}\n\n if (locale === defaultLocale) {\n for (const [id, entry] of Object.entries(manifest)) {\n messages[id] = entry.defaultMessage\n }\n return messages\n }\n\n for (const id of Object.keys(manifest)) {\n if (Object.hasOwn(messages, id)) continue\n if (Object.hasOwn(existingMessages, id)) {\n const existingMessage = existingMessages[id]!\n messages[id] = existingMessage\n continue\n }\n const cachedMessage = cache.entries[getCacheKey(id, locale)]?.translation\n if (cachedMessage !== undefined) messages[id] = cachedMessage\n }\n return messages\n }\n\n function writeLocaleFilesToDisk() {\n if (!usesBundleStorage) return\n const dir = getLocalesDirPath()\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n for (const locale of locales) {\n writeFileIfChanged(resolve(dir, `${locale}.json`), JSON.stringify(buildLocalLocaleMessages(locale), null, 2) + \"\\n\")\n }\n writeLoadMessagesModule()\n }\n\n function getMissingMessagesByLocale() {\n const missingByLocale = new Map<string, TranslateMessage[]>()\n\n for (const locale of locales) {\n if (locale === defaultLocale) continue\n const existingMessages = readLocaleMessages(locale)\n for (const [id, entry] of Object.entries(manifest)) {\n if (!Object.hasOwn(existingMessages, id) && !Object.hasOwn(cache.entries, getCacheKey(id, locale))) {\n const misses = missingByLocale.get(locale) ?? []\n misses.push({\n id,\n text: entry.defaultMessage,\n meta: entry.meta,\n placeholders: entry.placeholders,\n sources: entry.sources,\n })\n missingByLocale.set(locale, misses)\n }\n }\n }\n\n return missingByLocale\n }\n\n function assertLocalBuildTranslationsComplete() {\n const expectedIds = new Set(Object.keys(manifest))\n const issues: string[] = []\n\n for (const locale of locales) {\n const localePath = getLocalePath(locale)\n if (!existsSync(localePath)) {\n issues.push(`- ${locale}: missing file at ${relative(root, localePath)}`)\n continue\n }\n\n const localeMessages = readLocaleMessages(locale)\n const missingIds = [...expectedIds].filter((id) => !Object.hasOwn(localeMessages, id))\n const orphanIds = Object.keys(localeMessages).filter((id) => !expectedIds.has(id))\n\n if (locale === defaultLocale) {\n const staleIds = [...expectedIds].filter((id) => localeMessages[id] !== manifest[id]!.defaultMessage)\n if (missingIds.length > 0) issues.push(formatLocaleIssue(locale, \"missing\", missingIds))\n if (orphanIds.length > 0) issues.push(formatLocaleIssue(locale, \"orphaned\", orphanIds))\n if (staleIds.length > 0) issues.push(formatLocaleIssue(locale, \"outdated default messages\", staleIds))\n continue\n }\n\n if (missingIds.length > 0) issues.push(formatLocaleIssue(locale, \"missing\", missingIds))\n if (orphanIds.length > 0) issues.push(formatLocaleIssue(locale, \"orphaned\", orphanIds))\n }\n\n if (issues.length === 0) return\n\n throw new Error(\n [\n `${PREFIX} committed locale artifacts are out of sync for local production build`,\n `local production builds are check-only and never regenerate locale files`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ...issues,\n ].join(\"\\n\"),\n )\n }\n\n async function translateMissingMessages() {\n if (!resolvedTranslate) return false\n const missingByLocale = getMissingMessagesByLocale()\n\n const totalMisses = [...missingByLocale.values()].reduce((count, misses) => count + misses.length, 0)\n if (totalMisses === 0) return false\n\n const missLocales = [...missingByLocale.keys()]\n log(\n `${PREFIX} ${BOLD}Translating${RESET} ${CYAN}${totalMisses}${RESET} ${totalMisses === 1 ? \"Message\" : \"Messages\"} -> ${CYAN}${formatLocales(missLocales)}${RESET}`,\n )\n\n for (const [locale, misses] of missingByLocale) {\n const result = await resolvedTranslate(misses, locale)\n\n for (const miss of misses) {\n const translated = result[miss.id] ?? miss.text\n cache.entries[getCacheKey(miss.id, locale)] = {\n sourceText: miss.text,\n meta: miss.meta,\n locale,\n translation: translated,\n timestamp: Date.now(),\n }\n }\n }\n\n return true\n }\n\n function scheduleDevTranslation() {\n if (!resolvedTranslate) return\n if (!isDev) return\n if (translateTimer) clearTimeout(translateTimer)\n translateTimer = setTimeout(async () => {\n const translated = await translateMissingMessages()\n if (translated) saveCache(resolve(root, cacheFile), cache)\n writeLocaleFilesToDisk()\n writePrivateManifest()\n }, 1000)\n }\n\n function removeFileMessages(file: string) {\n const previous = fileMessages.get(file)\n if (!previous) return false\n\n for (const message of previous) {\n const entry = manifest[message.id]\n if (!entry) continue\n entry.sources = entry.sources.filter((source) => !isSameSource(source, message.source))\n if (entry.sources.length === 0) delete manifest[message.id]\n }\n\n fileMessages.delete(file)\n return true\n }\n\n function syncFileMessages(file: string, messages: ExtractedMessage[]): SyncResult {\n const previousMessages = fileMessages.get(file) ?? []\n const nextEntries = groupMessagesById(messages)\n for (const [id, entry] of Object.entries(nextEntries)) {\n const existing = manifest[id]\n if (existing && !hasSameMessageShape(existing, entry)) {\n throw new Error(formatCollisionError(id, existing, entry))\n }\n }\n\n removeFileMessages(file)\n for (const [id, entry] of Object.entries(nextEntries)) {\n if (!manifest[id]) {\n manifest[id] = entry\n continue\n }\n for (const source of entry.sources) {\n if (!manifest[id]!.sources.some((existingSource) => isSameSource(existingSource, source))) {\n manifest[id]!.sources.push(source)\n }\n }\n }\n\n if (messages.length > 0) fileMessages.set(file, messages)\n return {\n manifestChanged:\n previousMessages.length !== messages.length ||\n previousMessages.some((message, index) => !isSameExtractedMessage(message, messages[index])),\n localeMessagesChanged:\n previousMessages.length !== messages.length ||\n previousMessages.some((message, index) => !hasSameMessageShape(message, messages[index]!)),\n }\n }\n\n function syncSourceCode(file: string, code: string) {\n const analysis = analyzeSourceFile(code, file, {\n call: CALL_MARKERS,\n component: COMPONENT_MARKERS,\n logging,\n })\n if (!analysis.parsed) return null\n return syncFileMessages(\n file,\n analysis.messages.map((message) => ({\n ...message,\n source: {\n ...message.source,\n file: toRootRelativePath(message.source.file),\n },\n })),\n )\n }\n\n function removeTrackedFile(file: string): SyncResult {\n const hadPreviousMessages = removeFileMessages(file)\n return {\n manifestChanged: hadPreviousMessages,\n localeMessagesChanged: hadPreviousMessages,\n }\n }\n\n function applySyncResult(syncResult: SyncResult | null, options: { scheduleTranslation: boolean }) {\n if (!syncResult) return\n if (syncResult.localeMessagesChanged) {\n writeLocaleFilesToDisk()\n writePrivateManifest()\n if (options.scheduleTranslation) scheduleDevTranslation()\n return\n }\n if (syncResult.manifestChanged) writePrivateManifest()\n }\n\n function scanAllSourceFiles() {\n for (const id of Object.keys(manifest)) delete manifest[id]\n fileMessages.clear()\n\n for (const sourceRoot of sourceRoots) {\n if (!existsSync(sourceRoot)) continue\n for (const file of collectScanFiles(sourceRoot).sort()) {\n const code = readFileSync(file, \"utf-8\")\n syncSourceCode(file, code)\n }\n }\n }\n\n function toRootRelativePath(file: string) {\n return relative(root, file).replaceAll(\"\\\\\", \"/\")\n }\n\n return {\n name: \"better-translation-extract\",\n enforce: \"pre\",\n\n configResolved(config) {\n root = config.root\n isDev = config.command === \"serve\"\n sourceRoots = (Array.isArray(rootDir) ? rootDir : [rootDir]).map((dir) => resolve(root, dir))\n log(\n `${PREFIX} Locales: ${CYAN}${formatLocales(locales)}${RESET} | Default: ${CYAN}${formatLocale(defaultLocale)}${RESET} | Storage: ${CYAN}${usesBundleStorage ? \"Bundle\" : \"Remote\"}${RESET} | Out Dir: ${DIM}${usesBundleStorage ? localesDir : \"n/a\"}${RESET} | Roots: ${DIM}${(Array.isArray(rootDir) ? rootDir : [rootDir]).join(\", \")}${RESET}`,\n )\n },\n\n buildStart() {\n cache = loadCache(resolve(root, cacheFile))\n scanAllSourceFiles()\n if (usesBundleStorage && !isDev) {\n assertGeneratedFilesCommitted()\n assertLocalBuildTranslationsComplete()\n return\n }\n writeRuntimeConfig()\n writeLoadMessagesModule()\n writeGitignore()\n writeLocaleFilesToDisk()\n writePrivateManifest()\n\n if (isDev) scheduleDevTranslation()\n },\n\n configureServer(server) {\n server.watcher.add(sourceRoots)\n\n const syncFileFromDisk = (file: string) => {\n if (!shouldScanFile(file) || !existsSync(file)) return\n applySyncResult(syncSourceCode(file, readFileSync(file, \"utf-8\")), { scheduleTranslation: true })\n }\n\n const removeFileFromManifest = (file: string) => {\n if (!shouldScanFile(file)) return\n applySyncResult(removeTrackedFile(file), { scheduleTranslation: true })\n }\n\n server.watcher.on(\"add\", syncFileFromDisk)\n server.watcher.on(\"change\", syncFileFromDisk)\n server.watcher.on(\"unlink\", removeFileFromManifest)\n },\n\n transform(code, id) {\n const cleanId = id.split(\"?\", 1)[0] ?? id\n if (!shouldScanFile(cleanId)) return\n\n const analysis = analyzeSourceFile(code, cleanId, {\n call: CALL_MARKERS,\n component: COMPONENT_MARKERS,\n logging,\n })\n\n if (analysis.edits.length === 0) return\n return {\n code: applyEdits(code, analysis.edits),\n map: null,\n }\n },\n\n async generateBundle() {\n if (usesBundleStorage) {\n if (!isDev) {\n assertGeneratedFilesCommitted()\n }\n assertLocalBuildTranslationsComplete()\n } else {\n await translateMissingMessages()\n }\n\n if (!usesBundleStorage) {\n await syncRemote()\n }\n },\n\n closeBundle() {\n if (usesBundleStorage && !isDev) return\n saveCache(resolve(root, cacheFile), cache)\n },\n }\n}\n\nfunction formatLocaleIssue(locale: string, label: string, ids: string[]) {\n const preview = ids\n .slice(0, 5)\n .map((id) => JSON.stringify(id))\n .join(\", \")\n const suffix = ids.length > 5 ? `, ... ${ids.length - 5} more` : \"\"\n return `- ${locale}: ${label} (${preview}${suffix})`\n}\n\nfunction normalizeLocaleMessages(input: unknown): RuntimeMessages {\n if (isRuntimeMessages(input)) return input\n if (\n typeof input === \"object\" &&\n input !== null &&\n \"messages\" in input &&\n typeof input.messages === \"object\" &&\n input.messages !== null\n ) {\n return Object.fromEntries(\n Object.entries(input.messages).flatMap(([id, entry]) =>\n typeof entry === \"object\" && entry !== null && \"translation\" in entry && typeof entry.translation === \"string\"\n ? [[id, entry.translation]]\n : [],\n ),\n ) as RuntimeMessages\n }\n return {}\n}\n\nfunction writeFileIfChanged(path: string, contents: string) {\n if (existsSync(path) && readFileSync(path, \"utf-8\") === contents) return false\n writeFileSync(path, contents)\n return true\n}\n\nfunction collectScanFiles(root: string) {\n const files: string[] = []\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n const path = resolve(root, entry.name)\n if (entry.isDirectory()) {\n if (entry.name === \"node_modules\") continue\n files.push(...collectScanFiles(path))\n continue\n }\n files.push(path)\n }\n return files\n}\n\nfunction isRuntimeMessages(input: unknown): input is RuntimeMessages {\n return typeof input === \"object\" && input !== null && Object.values(input).every((value) => typeof value === \"string\")\n}\n\nfunction applyEdits(code: string, edits: Array<{ start: number; end: number; replacement: string }>) {\n let transformed = code\n for (const edit of [...edits].sort((a, b) => b.start - a.start)) {\n transformed = `${transformed.slice(0, edit.start)}${edit.replacement}${transformed.slice(edit.end)}`\n }\n return transformed\n}\n\nfunction groupMessagesById(messages: ExtractedMessage[]): MessageManifest {\n const grouped: MessageManifest = {}\n\n for (const message of messages) {\n const existing = grouped[message.id]\n if (existing && !hasSameMessageShape(existing, message)) {\n throw new Error(formatCollisionError(message.id, existing, message))\n }\n if (!existing) {\n grouped[message.id] = {\n defaultMessage: message.defaultMessage,\n meta: message.meta,\n placeholders: message.placeholders,\n sources: [message.source],\n }\n continue\n }\n if (!existing.sources.some((source) => isSameSource(source, message.source))) {\n existing.sources.push(message.source)\n }\n }\n\n return grouped\n}\n\nfunction hasSameMessageShape(\n existing: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\">,\n incoming: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\"> | ExtractedMessage,\n) {\n return (\n existing.defaultMessage === incoming.defaultMessage &&\n serializeMeta(existing.meta) === serializeMeta(incoming.meta) &&\n JSON.stringify(existing.placeholders) === JSON.stringify(incoming.placeholders)\n )\n}\n\nfunction isSameSource(left: MessageSource, right: MessageSource) {\n return (\n left.file === right.file &&\n left.kind === right.kind &&\n left.marker === right.marker &&\n left.start === right.start &&\n left.end === right.end\n )\n}\n\nfunction isSameExtractedMessage(left: ExtractedMessage, right?: ExtractedMessage) {\n if (!right) return false\n return hasSameMessageShape(left, right) && isSameSource(left.source, right.source)\n}\n\nfunction formatCollisionError(\n id: string,\n existing: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\" | \"sources\">,\n incoming: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\" | \"sources\"> | ExtractedMessage,\n) {\n const existingSources = formatSources(existing.sources)\n const incomingSources = formatSources(\"source\" in incoming ? [incoming.source] : incoming.sources)\n return [\n `${PREFIX} conflicting message definition for ${BOLD}\"${id}\"${RESET}`,\n `existing: ${JSON.stringify({ defaultMessage: existing.defaultMessage, meta: existing.meta, placeholders: existing.placeholders })}`,\n `existing sources: ${existingSources}`,\n `incoming: ${JSON.stringify({ defaultMessage: incoming.defaultMessage, meta: incoming.meta, placeholders: incoming.placeholders })}`,\n `incoming sources: ${incomingSources}`,\n ].join(\"\\n\")\n}\n\nfunction formatSources(sources: MessageSource[]) {\n return sources.map((source) => `${source.file}:${source.line}:${source.column}`).join(\", \")\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-translation",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Vite plugin and runtime helpers for Better Translation.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,22 +23,27 @@
|
|
|
23
23
|
"./server": {
|
|
24
24
|
"import": "./dist/server.mjs",
|
|
25
25
|
"types": "./dist/server.d.mts"
|
|
26
|
+
},
|
|
27
|
+
"./ai": {
|
|
28
|
+
"import": "./dist/ai.mjs",
|
|
29
|
+
"types": "./dist/ai.d.mts"
|
|
26
30
|
}
|
|
27
31
|
},
|
|
28
32
|
"publishConfig": {
|
|
29
33
|
"access": "public"
|
|
30
34
|
},
|
|
31
35
|
"scripts": {
|
|
32
|
-
"build": "
|
|
36
|
+
"build": "vp pack",
|
|
33
37
|
"prepack": "bun run build"
|
|
34
38
|
},
|
|
35
39
|
"dependencies": {
|
|
36
|
-
"
|
|
40
|
+
"ai": "6.0.193",
|
|
41
|
+
"oxc-parser": "0.133.0",
|
|
42
|
+
"zod": "4.3.6"
|
|
37
43
|
},
|
|
38
44
|
"devDependencies": {
|
|
39
45
|
"@better-translation/tsconfig": "workspace:*",
|
|
40
46
|
"@types/react": "catalog:",
|
|
41
|
-
"tsdown": "^0.21.9",
|
|
42
47
|
"typescript": "catalog:",
|
|
43
48
|
"vite": "catalog:"
|
|
44
49
|
},
|