@fluenti/cli 0.4.0-rc.1 → 0.4.0-rc.3

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/cli.js CHANGED
@@ -692,7 +692,7 @@ function ke(e) {
692
692
  }
693
693
  function Ae(e, t, n) {
694
694
  let r = [];
695
- if (r.push(`You are a migration assistant helping convert a ${e.framework} project from "${e.name}" to Fluenti (@fluenti).`, "", "Your task:", "1. Generate a `fluenti.config.ts` file based on the existing i18n configuration", "2. Convert each locale/translation file to Fluenti PO format", "3. List the code changes needed (file by file) to migrate source code from the old API to Fluenti API", ""), n && r.push("=== MIGRATION GUIDE ===", n, ""), t.packageJson && r.push("=== package.json ===", t.packageJson, ""), t.configFiles.length > 0) {
695
+ if (r.push(`You are a migration tool converting a ${e.framework} project from "${e.name}" to Fluenti (@fluenti).`, "", "Tasks:", "1. Generate a `fluenti.config.ts` based on the existing i18n configuration", "2. Convert each locale/translation file to standard gettext PO format", "3. Generate unified diff patches for every source file that needs changes", "4. Generate install/uninstall commands", "", "=== TRANSLATION API RULES ===", "Fluenti provides a compile-time `t` tagged template that does NOT require useI18n():", "", " import { t } from '@fluenti/react' // or @fluenti/vue, @fluenti/solid", " const name = \"World\"", " t`Hello, ${name}!`", "", "Use `import { t }` for ALL translation calls. Only use `useI18n()` when you need:", "- `d()` / `n()` for date/number formatting", "- `setLocale()` for locale switching", "- `locale` for reading the current locale reactively", "", "=== SOURCE CODE REWRITING RULES ===", "Imports:", `- ${e.name}: remove all imports from "${e.name}" (and related packages)`, `- Add: import { t } from '@fluenti/${e.framework === "Vue" ? "vue" : e.framework === "Next.js" ? "react" : e.framework.toLowerCase()}'`, "- Only add useI18n import if d()/n()/setLocale is needed in that file", "", "Translation calls:", "- t('key') t`Source text` (tagged template with the actual source text, not the key)", "- t('key', { name }) → t`Hello, ${name}` (interpolate directly in template)", "- $t('key') → t`Source text` (Vue template)", "- Remove useI18n()/useTranslation()/useTranslations() destructuring if only t was used", "", "Components:", "- <i18n-t keypath=\"key\"> → <Trans>Source text</Trans>", "- <Trans i18nKey=\"key\"> → <Trans>Source text</Trans>", "", "ICU syntax conversion:", "- {{variable}} (double braces) → {variable} (single braces)", "- _one/_other suffixes → ICU {count, plural, one {...} other {...}}", "- @:key references → inline the referenced text directly", "- Pipe-separated plurals → ICU plural", "", "=== PO FORMAT RULES ===", "Each PO file must have a standard header:", " msgid \"\"", " msgstr \"\"", " \"Content-Type: text/plain; charset=UTF-8\\n\"", " \"Content-Transfer-Encoding: 8bit\\n\"", " \"Language: {locale}\\n\"", "", "Message entries: msgid is the source text (English), msgstr is the translation.", "Flatten nested JSON keys: \"home.title\" → use the actual source text as msgid.", ""), n && r.push("=== MIGRATION GUIDE ===", n, ""), t.packageJson && r.push("=== package.json ===", t.packageJson, ""), t.configFiles.length > 0) {
696
696
  r.push("=== EXISTING CONFIG FILES ===");
697
697
  for (let e of t.configFiles) r.push(`--- ${e.path} ---`, e.content, "");
698
698
  }
@@ -704,7 +704,7 @@ function Ae(e, t, n) {
704
704
  r.push("=== SAMPLE SOURCE FILES ===");
705
705
  for (let e of t.sampleSources) r.push(`--- ${e.path} ---`, e.content, "");
706
706
  }
707
- return r.push("", "=== OUTPUT FORMAT ===", "Respond with the following sections, each starting with the exact header shown:", "", "### FLUENTI_CONFIG", "```ts", "// The fluenti.config.ts content", "```", "", "### LOCALE_FILES", "For each locale file, output:", "#### LOCALE: {locale_code}", "```po", "// The PO file content", "```", "", "### MIGRATION_STEPS", "A numbered checklist of specific code changes needed, with before/after examples.", "", "### INSTALL_COMMANDS", "```bash", "// The install and uninstall commands", "```"), r.join("\n");
707
+ return r.push("", "=== OUTPUT FORMAT ===", "Output ONLY the following sections. No explanations, no commentary.", "", "### FLUENTI_CONFIG", "```ts", "// Complete fluenti.config.ts", "```", "", "### LOCALE_FILES", "#### LOCALE: {locale_code}", "```po", "// Complete PO file with standard header", "```", "(repeat for each locale)", "", "### SOURCE_PATCHES", "#### FILE: {relative_file_path}", "```diff", "--- a/{file_path}", "+++ b/{file_path}", "@@ ... @@", " context line", "-removed line", "+added line", "```", "(repeat for each file that needs changes)", "", "### INSTALL_COMMANDS", "```bash", "// install + uninstall commands", "```"), r.join("\n");
708
708
  }
709
709
  async function je(e, t) {
710
710
  let n = 10 * 1024 * 1024;
@@ -729,11 +729,12 @@ function Me(e) {
729
729
  let t = 5e5, n = e.length > t ? e.slice(0, t) : e, r = {
730
730
  config: void 0,
731
731
  localeFiles: [],
732
+ sourcePatches: [],
732
733
  steps: void 0,
733
734
  installCommands: void 0
734
735
  }, i = n.match(/### FLUENTI_CONFIG[\s\S]*?```(?:ts|typescript)?\n([\s\S]*?)```/);
735
736
  i && (r.config = i[1].trim());
736
- let a = n.match(/### LOCALE_FILES([\s\S]*?)(?=### MIGRATION_STEPS|### INSTALL_COMMANDS|$)/);
737
+ let a = n.match(/### LOCALE_FILES([\s\S]*?)(?=### SOURCE_PATCHES|### MIGRATION_STEPS|### INSTALL_COMMANDS|$)/);
737
738
  if (a) {
738
739
  let e = /#### LOCALE:\s*(\S+)\s*\n```(?:po)?\n([\s\S]*?)```/g, t;
739
740
  for (; (t = e.exec(a[1])) !== null;) r.localeFiles.push({
@@ -741,10 +742,18 @@ function Me(e) {
741
742
  content: t[2].trim()
742
743
  });
743
744
  }
744
- let o = n.match(/### MIGRATION_STEPS\s*\n([\s\S]*?)(?=### INSTALL_COMMANDS|$)/);
745
- o && (r.steps = o[1].trim());
746
- let s = n.match(/### INSTALL_COMMANDS[\s\S]*?```(?:bash|sh)?\n([\s\S]*?)```/);
747
- return s && (r.installCommands = s[1].trim()), r;
745
+ let o = n.match(/### SOURCE_PATCHES([\s\S]*?)(?=### INSTALL_COMMANDS|$)/);
746
+ if (o) {
747
+ let e = /#### FILE:\s*(\S+)\s*\n```(?:diff)?\n([\s\S]*?)```/g, t;
748
+ for (; (t = e.exec(o[1])) !== null;) r.sourcePatches.push({
749
+ file: t[1],
750
+ patch: t[2].trim()
751
+ });
752
+ }
753
+ let s = n.match(/### MIGRATION_STEPS\s*\n([\s\S]*?)(?=### INSTALL_COMMANDS|$)/);
754
+ s && (r.steps = s[1].trim());
755
+ let c = n.match(/### INSTALL_COMMANDS[\s\S]*?```(?:bash|sh)?\n([\s\S]*?)```/);
756
+ return c && (r.installCommands = c[1].trim()), r;
748
757
  }
749
758
  async function Ne(e) {
750
759
  let { from: t, provider: n, write: r } = e, i = De(t);
@@ -785,10 +794,20 @@ async function Ne(e) {
785
794
  title: `locales/${e.locale}.po`,
786
795
  message: e.content.length > 500 ? e.content.slice(0, 500) + "\n... (use --write to save full file)" : e.content
787
796
  });
788
- c.steps && (A.log(""), A.box({
797
+ if (c.sourcePatches.length > 0) if (r) {
798
+ A.log(""), A.info(`Generated ${c.sourcePatches.length} source patch(es). Apply with:`);
799
+ for (let e of c.sourcePatches) {
800
+ let t = w(`.fluenti-migrate-${e.file.replace(/[/\\]/g, "-")}.patch`), { writeFileSync: n } = await import("node:fs");
801
+ n(t, e.patch, "utf-8"), A.success(`Patch written: ${t}`), A.log(` patch -p1 < ${t}`);
802
+ }
803
+ } else for (let e of c.sourcePatches) A.log(""), A.box({
804
+ title: `Patch: ${e.file}`,
805
+ message: e.patch.length > 800 ? e.patch.slice(0, 800) + "\n... (use --write to save full patch)" : e.patch
806
+ });
807
+ c.steps && c.sourcePatches.length === 0 && (A.log(""), A.box({
789
808
  title: "Migration Steps",
790
809
  message: c.steps
791
- })), !r && (c.config || c.localeFiles.length > 0) && (A.log(""), A.info("Run with --write to save generated files to disk:"), A.log(` fluenti migrate --from ${t} --write`));
810
+ })), !r && (c.config || c.localeFiles.length > 0 || c.sourcePatches.length > 0) && (A.log(""), A.info("Run with --write to save generated files and patches to disk:"), A.log(` fluenti migrate --from ${t} --write`));
792
811
  }
793
812
  //#endregion
794
813
  //#region src/init.ts