livelocale 0.12.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -297,8 +297,8 @@ var init_transform = __esm({
297
297
  });
298
298
 
299
299
  // src/codemod/index.ts
300
- import * as fs14 from "fs";
301
- import * as path21 from "path";
300
+ import * as fs15 from "fs";
301
+ import * as path22 from "path";
302
302
  import * as net from "net";
303
303
 
304
304
  // src/server/module-dir.ts
@@ -746,7 +746,7 @@ function isShareManifest(value) {
746
746
  const layout = own2(value, "layout");
747
747
  const locales = own2(value, "locales");
748
748
  const sourceLocale = own2(value, "sourceLocale");
749
- return own2(value, "format") === SHARE_MANIFEST_FORMAT && (repo === void 0 || typeof repo === "string" && isValidRepoSlug(repo)) && isNullOr(own2(value, "defaultBranch"), isValidBranchName) && isNullOr(own2(value, "branch"), isValidBranchName) && isNullOr(own2(value, "base"), (base) => BASE_SHA_RE2.test(base)) && typeof own2(value, "localesPath") === "string" && (layout === "flat" || layout === "namespaced") && isArrayOf(locales, isLocaleCode) && typeof sourceLocale === "string" && locales.includes(sourceLocale) && isNullOr(own2(value, "basePath"), (path22) => path22.startsWith("/") && path22.endsWith("/")) && isArrayOf(own2(value, "codeKeys"), isString);
749
+ return own2(value, "format") === SHARE_MANIFEST_FORMAT && (repo === void 0 || typeof repo === "string" && isValidRepoSlug(repo)) && isNullOr(own2(value, "defaultBranch"), isValidBranchName) && isNullOr(own2(value, "branch"), isValidBranchName) && isNullOr(own2(value, "base"), (base) => BASE_SHA_RE2.test(base)) && typeof own2(value, "localesPath") === "string" && (layout === "flat" || layout === "namespaced") && isArrayOf(locales, isLocaleCode) && typeof sourceLocale === "string" && locales.includes(sourceLocale) && isNullOr(own2(value, "basePath"), (path23) => path23.startsWith("/") && path23.endsWith("/")) && isArrayOf(own2(value, "codeKeys"), isString);
750
750
  }
751
751
 
752
752
  // src/codemod/doctor.ts
@@ -2394,6 +2394,28 @@ function listLocales(localesDir) {
2394
2394
  }
2395
2395
  return Array.from(locales).sort();
2396
2396
  }
2397
+ function hasLocaleFile(localesDir, locale) {
2398
+ const dir = path8.join(localesDir, locale);
2399
+ if (fs6.existsSync(dir) && fs6.statSync(dir).isDirectory()) {
2400
+ return fs6.readdirSync(dir).some((file) => file.endsWith(".json"));
2401
+ }
2402
+ return fs6.existsSync(path8.join(localesDir, `${locale}.json`));
2403
+ }
2404
+ function noLocaleFilesError(localesDir) {
2405
+ const skipped = [];
2406
+ for (const item of fs6.readdirSync(localesDir, { withFileTypes: true })) {
2407
+ if (item.isDirectory() && !item.name.startsWith(".") && !LOCALE_RE.test(item.name)) {
2408
+ skipped.push(`${item.name}/`);
2409
+ } else if (item.isFile() && item.name.endsWith(".json")) {
2410
+ if (!LOCALE_RE.test(path8.basename(item.name, ".json"))) skipped.push(item.name);
2411
+ }
2412
+ }
2413
+ skipped.sort();
2414
+ const hint = skipped.length === 0 ? "" : ` Skipped ${skipped.slice(0, 3).join(", ")}${skipped.length > 3 ? ", ..." : ""}: a locale name looks like en, ar or pt-BR.`;
2415
+ return new Error(
2416
+ `No locale files found in ${localesDir}. Point --locales at the folder with en.json, ar.json, ...${hint}`
2417
+ );
2418
+ }
2397
2419
  function flatten(obj, prefix, file, data) {
2398
2420
  if (!obj || typeof obj !== "object" || Array.isArray(obj)) return;
2399
2421
  for (const [key, value] of Object.entries(obj)) {
@@ -2470,6 +2492,9 @@ async function runCheck(options) {
2470
2492
  throw new Error(`Locales directory not found: ${localesDir}`);
2471
2493
  }
2472
2494
  const locales = listLocales(localesDir);
2495
+ if (!locales.some((locale) => hasLocaleFile(localesDir, locale))) {
2496
+ throw noLocaleFilesError(localesDir);
2497
+ }
2473
2498
  const data = new Map(locales.map((l) => [l, readLocale(localesDir, l, projectDir, issues)]));
2474
2499
  const sourceLocale = options.sourceLocale ?? (locales.includes("en") ? "en" : locales[0] ?? null);
2475
2500
  if (sourceLocale && !data.has(sourceLocale)) {
@@ -2664,44 +2689,181 @@ function formatCheckReport(report, limit = 25) {
2664
2689
  return lines.join("\n");
2665
2690
  }
2666
2691
 
2667
- // src/codemod/check-cli.ts
2668
- function parseCheckArgs(argv) {
2669
- const args = {
2670
- dir: ".",
2671
- unused: true,
2672
- strict: false,
2673
- json: false,
2674
- hardcoded: false,
2675
- allow: []
2676
- };
2692
+ // src/codemod/cli-args.ts
2693
+ var UsageError = class extends Error {
2694
+ constructor(message) {
2695
+ super(message);
2696
+ this.name = "UsageError";
2697
+ }
2698
+ };
2699
+ var USAGE_EXIT_CODE = 2;
2700
+ var GLOBAL_SHORT = /* @__PURE__ */ new Set(["-h", "-v"]);
2701
+ function knownOptions(spec) {
2702
+ return [...spec.values ?? [], ...spec.repeatable ?? [], ...spec.flags ?? []];
2703
+ }
2704
+ function editDistance(a, b) {
2705
+ const d = Array.from(
2706
+ { length: a.length + 1 },
2707
+ (_, i) => Array.from({ length: b.length + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0)
2708
+ );
2709
+ for (let i = 1; i <= a.length; i++) {
2710
+ for (let j = 1; j <= b.length; j++) {
2711
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
2712
+ d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
2713
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
2714
+ d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
2715
+ }
2716
+ }
2717
+ }
2718
+ return d[a.length][b.length];
2719
+ }
2720
+ function suggestOption(arg, known) {
2721
+ const name = arg.replace(/^-+/, "");
2722
+ let best;
2723
+ let bestDistance = Infinity;
2724
+ for (const option of known) {
2725
+ const distance = editDistance(name, option.replace(/^-+/, ""));
2726
+ if (distance < bestDistance) {
2727
+ best = option;
2728
+ bestDistance = distance;
2729
+ }
2730
+ }
2731
+ return bestDistance <= 2 && bestDistance < name.length / 2 ? best : void 0;
2732
+ }
2733
+ function unknownOption(spec, arg) {
2734
+ const known = knownOptions(spec);
2735
+ const head = `Unknown option ${arg} for livelocale ${spec.command}.`;
2736
+ const eq = arg.indexOf("=");
2737
+ if (eq > 0 && known.includes(arg.slice(0, eq))) {
2738
+ return new UsageError(
2739
+ `${head} Put a space before the value: ${arg.slice(0, eq)} ${arg.slice(eq + 1)}`
2740
+ );
2741
+ }
2742
+ const suggestion = suggestOption(arg, known);
2743
+ return new UsageError(
2744
+ suggestion ? `${head} Did you mean ${suggestion}?` : `${head} Run livelocale --help to see its options.`
2745
+ );
2746
+ }
2747
+ function parseCommandArgs(spec, argv) {
2748
+ const parsed = { values: {}, lists: {}, flags: /* @__PURE__ */ new Set(), positionals: [], rest: [] };
2749
+ const values = new Set(spec.values ?? []);
2750
+ const repeatable = new Set(spec.repeatable ?? []);
2751
+ const flags = new Set(spec.flags ?? []);
2752
+ const maxPositionals = spec.positionals ?? 0;
2677
2753
  for (let i = 0; i < argv.length; i++) {
2678
2754
  const arg = argv[i];
2679
- if (arg === "--dir") {
2680
- args.dir = argv[++i] || ".";
2681
- } else if (arg === "--locales") {
2682
- args.localesPath = argv[++i];
2683
- } else if (arg === "--source") {
2684
- args.sourceLocale = argv[++i];
2685
- } else if (arg === "--no-unused") {
2686
- args.unused = false;
2687
- } else if (arg === "--strict") {
2688
- args.strict = true;
2689
- } else if (arg === "--json") {
2690
- args.json = true;
2691
- } else if (arg === "--hardcoded") {
2692
- args.hardcoded = true;
2693
- } else if (arg === "--allow") {
2694
- const word = argv[++i];
2695
- if (!word || word.startsWith("--")) throw new Error("--allow needs a word.");
2696
- args.allow.push(word);
2697
- } else if (arg && !arg.startsWith("--")) {
2698
- args.dir = arg;
2755
+ if (arg === "--") {
2756
+ if (!spec.rest) throw new UsageError(`livelocale ${spec.command} takes nothing after --.`);
2757
+ parsed.rest = argv.slice(i + 1);
2758
+ break;
2759
+ }
2760
+ if (values.has(arg) || repeatable.has(arg)) {
2761
+ const value = argv[i + 1];
2762
+ if (value === void 0 || value.startsWith("--") || GLOBAL_SHORT.has(value)) {
2763
+ throw new UsageError(`${arg} needs a value.`);
2764
+ }
2765
+ i++;
2766
+ if (repeatable.has(arg)) (parsed.lists[arg] ??= []).push(value);
2767
+ else parsed.values[arg] = value;
2768
+ } else if (flags.has(arg)) {
2769
+ parsed.flags.add(arg);
2770
+ } else if (arg.startsWith("-") && arg !== "-") {
2771
+ throw unknownOption(spec, arg);
2772
+ } else if (parsed.positionals.length < maxPositionals) {
2773
+ parsed.positionals.push(arg);
2774
+ } else {
2775
+ throw new UsageError(`Unexpected argument "${arg}" for livelocale ${spec.command}.`);
2699
2776
  }
2700
2777
  }
2778
+ return parsed;
2779
+ }
2780
+ function findGlobalOption(args, spec) {
2781
+ const takesValue = /* @__PURE__ */ new Set([...spec?.values ?? [], ...spec?.repeatable ?? []]);
2782
+ let found;
2783
+ for (let i = 0; i < args.length; i++) {
2784
+ const arg = args[i];
2785
+ if (arg === "--") break;
2786
+ if (takesValue.has(arg)) {
2787
+ if (args[i + 1] !== "--") i++;
2788
+ } else if (arg === "--help" || arg === "-h") return "help";
2789
+ else if (arg === "--version" || arg === "-v") found = "version";
2790
+ }
2791
+ return found;
2792
+ }
2793
+ function splitList(value) {
2794
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
2795
+ }
2796
+ function folderArg(parsed) {
2797
+ const plain = parsed.positionals[0];
2798
+ const flag = parsed.values["--dir"];
2799
+ if (plain !== void 0 && flag !== void 0) {
2800
+ throw new UsageError(`Pass the folder once: "${plain}" or --dir ${flag}, not both.`);
2801
+ }
2802
+ return plain ?? flag ?? ".";
2803
+ }
2804
+
2805
+ // src/codemod/command-specs.ts
2806
+ var COMMAND_SPECS = {
2807
+ migrate: {
2808
+ command: "migrate",
2809
+ values: ["--dir", "--exclude"],
2810
+ flags: ["--dry-run"],
2811
+ positionals: 1
2812
+ },
2813
+ init: {
2814
+ command: "init",
2815
+ values: ["--dir", "--locales", "--source", "--branches"],
2816
+ flags: ["--share", "--overwrite"],
2817
+ positionals: 1
2818
+ },
2819
+ studio: { command: "studio", values: ["--port", "--app"] },
2820
+ doctor: {
2821
+ command: "doctor",
2822
+ values: ["--dir", "--locales", "--app", "--token"],
2823
+ flags: ["--share"],
2824
+ positionals: 1
2825
+ },
2826
+ check: {
2827
+ command: "check",
2828
+ values: ["--dir", "--locales", "--source"],
2829
+ repeatable: ["--allow"],
2830
+ flags: ["--no-unused", "--strict", "--hardcoded", "--json"],
2831
+ positionals: 1
2832
+ },
2833
+ // `pack` and `build` share one set; checkPackArgs and checkBuildArgs reject the other's options
2834
+ // with a message that names the right command.
2835
+ pack: {
2836
+ command: "pack",
2837
+ values: ["--dir", "--locales", "--source", "--base", "--out"],
2838
+ flags: ["--expose-repo", "--no-inject", "--preview"],
2839
+ positionals: Infinity,
2840
+ rest: true
2841
+ },
2842
+ apply: {
2843
+ command: "apply",
2844
+ values: ["--dir", "--locales", "--source", "--branches"],
2845
+ flags: ["--clipboard", "--pr", "--force", "--dry-run", "--github-event"],
2846
+ positionals: Infinity
2847
+ }
2848
+ };
2849
+
2850
+ // src/codemod/check-cli.ts
2851
+ function parseCheckArgs(argv) {
2852
+ const parsed = parseCommandArgs(COMMAND_SPECS.check, argv);
2853
+ const args = {
2854
+ dir: folderArg(parsed),
2855
+ unused: !parsed.flags.has("--no-unused"),
2856
+ strict: parsed.flags.has("--strict"),
2857
+ json: parsed.flags.has("--json"),
2858
+ hardcoded: parsed.flags.has("--hardcoded"),
2859
+ allow: parsed.lists["--allow"] ?? []
2860
+ };
2861
+ if (parsed.values["--locales"] !== void 0) args.localesPath = parsed.values["--locales"];
2862
+ if (parsed.values["--source"] !== void 0) args.sourceLocale = parsed.values["--source"];
2701
2863
  return args;
2702
2864
  }
2703
2865
  function checkExitCode(report) {
2704
- if (report.setupProblems && report.setupProblems.length > 0) return 2;
2866
+ if (report.setupProblems && report.setupProblems.length > 0) return CHECK_COULD_NOT_RUN;
2705
2867
  return hasCheckFailures(report) ? 1 : 0;
2706
2868
  }
2707
2869
  function checkSummaryLines(report) {
@@ -2714,6 +2876,22 @@ function checkSummaryLines(report) {
2714
2876
  warns > 0 ? `\u2705 No problems, ${warns} warning(s) worth a look.` : `\u2705 Translations look good.`
2715
2877
  ];
2716
2878
  }
2879
+ var CHECK_COULD_NOT_RUN = 2;
2880
+ function checkErrorReport(error, localesDir) {
2881
+ return {
2882
+ error,
2883
+ localesDir: localesDir ?? "",
2884
+ sourceLocale: null,
2885
+ locales: [],
2886
+ codeKeyCount: 0,
2887
+ issues: [],
2888
+ setupProblems: [error]
2889
+ };
2890
+ }
2891
+ function withCheckError(report) {
2892
+ const problems = report.setupProblems ?? [];
2893
+ return problems.length > 0 ? { ...report, error: problems.join(" ") } : report;
2894
+ }
2717
2895
 
2718
2896
  // src/server/pack.ts
2719
2897
  import { spawn } from "child_process";
@@ -2722,8 +2900,8 @@ import * as path11 from "path";
2722
2900
 
2723
2901
  // src/core/page-path.ts
2724
2902
  var NON_ASCII_ESCAPES = /(?:%[89A-Fa-f][0-9A-Fa-f])+/g;
2725
- function readablePath(path22) {
2726
- return path22.replace(NON_ASCII_ESCAPES, (run3) => {
2903
+ function readablePath(path23) {
2904
+ return path23.replace(NON_ASCII_ESCAPES, (run3) => {
2727
2905
  try {
2728
2906
  return decodeURIComponent(run3);
2729
2907
  } catch {
@@ -2736,16 +2914,16 @@ function readablePath(path22) {
2736
2914
  import { execFile } from "child_process";
2737
2915
  import { readFileSync as readFileSync5 } from "fs";
2738
2916
  import * as path9 from "path";
2739
- var runGit = (args, cwd) => new Promise((resolve18) => {
2917
+ var runGit = (args, cwd) => new Promise((resolve19) => {
2740
2918
  try {
2741
2919
  execFile(
2742
2920
  "git",
2743
2921
  args,
2744
2922
  { cwd, windowsHide: true, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" } },
2745
- (error, stdout) => resolve18(error ? null : String(stdout).trim())
2923
+ (error, stdout) => resolve19(error ? null : String(stdout).trim())
2746
2924
  );
2747
2925
  } catch {
2748
- resolve18(null);
2926
+ resolve19(null);
2749
2927
  }
2750
2928
  });
2751
2929
  var SHA_RE = /^[0-9a-f]{40}([0-9a-f]{24})?$/i;
@@ -3046,8 +3224,8 @@ var NO_REPO_WARNING = "exposeRepo is on but no GitHub repository was found; the
3046
3224
  function isPlainObject2(value) {
3047
3225
  return typeof value === "object" && value !== null && !Array.isArray(value);
3048
3226
  }
3049
- function toPosix(relative11) {
3050
- return relative11.split(path11.sep).join("/") || ".";
3227
+ function toPosix(relative12) {
3228
+ return relative12.split(path11.sep).join("/") || ".";
3051
3229
  }
3052
3230
  function isInside(parent, child) {
3053
3231
  const rel = path11.relative(parent, child);
@@ -3303,52 +3481,27 @@ async function packOutput(options) {
3303
3481
  warnings
3304
3482
  };
3305
3483
  }
3306
- var VALUE_OPTIONS = {
3307
- "--dir": "dir",
3308
- "--locales": "locales",
3309
- "--source": "source",
3310
- "--base": "base",
3311
- "--out": "out"
3312
- };
3313
- function parseShareArgs(argv) {
3314
- const args = {
3315
- positional: [],
3316
- dir: ".",
3317
- locales: void 0,
3318
- source: void 0,
3319
- base: void 0,
3320
- out: void 0,
3321
- exposeRepo: false,
3322
- inject: true,
3323
- preview: false,
3324
- command: []
3325
- };
3326
- for (let i = 0; i < argv.length; i++) {
3327
- const arg = argv[i];
3328
- if (arg === "--") {
3329
- args.command = argv.slice(i + 1);
3330
- break;
3331
- }
3332
- if (Object.prototype.hasOwnProperty.call(VALUE_OPTIONS, arg)) {
3333
- const value = argv[i + 1];
3334
- if (value === void 0 || value.startsWith("--")) {
3335
- throw new PackError("USAGE", `${arg} needs a value.`);
3336
- }
3337
- args[VALUE_OPTIONS[arg]] = value;
3338
- i++;
3339
- } else if (arg === "--expose-repo") {
3340
- args.exposeRepo = true;
3341
- } else if (arg === "--no-inject") {
3342
- args.inject = false;
3343
- } else if (arg === "--preview") {
3344
- args.preview = true;
3345
- } else if (arg.startsWith("-") && arg !== "-") {
3346
- throw new PackError("USAGE", `Unknown option "${arg}".`);
3347
- } else {
3348
- args.positional.push(arg);
3349
- }
3484
+ function parseShareArgs(argv, command = "pack") {
3485
+ let parsed;
3486
+ try {
3487
+ parsed = parseCommandArgs({ ...COMMAND_SPECS.pack, command }, argv);
3488
+ } catch (err) {
3489
+ if (err instanceof UsageError) throw new PackError("USAGE", err.message);
3490
+ throw err;
3350
3491
  }
3351
- return args;
3492
+ const { values, flags } = parsed;
3493
+ return {
3494
+ positional: parsed.positionals,
3495
+ dir: values["--dir"] ?? ".",
3496
+ locales: values["--locales"],
3497
+ source: values["--source"],
3498
+ base: values["--base"],
3499
+ out: values["--out"],
3500
+ exposeRepo: flags.has("--expose-repo"),
3501
+ inject: !flags.has("--no-inject"),
3502
+ preview: flags.has("--preview"),
3503
+ command: parsed.rest
3504
+ };
3352
3505
  }
3353
3506
  var PACK_USAGE = "Usage: livelocale pack <outDir> [options]";
3354
3507
  var BUILD_USAGE = "Usage: livelocale build [--preview] -- <your build command>";
@@ -3418,7 +3571,7 @@ function quoteWindowsArg(arg) {
3418
3571
  }
3419
3572
  return out + "\\".repeat(backslashes * 2) + '"';
3420
3573
  }
3421
- var spawnInherit = (command, args, { cwd, env }) => new Promise((resolve18, reject2) => {
3574
+ var spawnInherit = (command, args, { cwd, env }) => new Promise((resolve19, reject2) => {
3422
3575
  const child = process.platform === "win32" ? spawn([command, ...args].map(quoteWindowsArg).join(" "), {
3423
3576
  cwd,
3424
3577
  env,
@@ -3426,10 +3579,10 @@ var spawnInherit = (command, args, { cwd, env }) => new Promise((resolve18, reje
3426
3579
  shell: true
3427
3580
  }) : spawn(command, args, { cwd, env, stdio: "inherit" });
3428
3581
  child.on("error", (error) => {
3429
- if (error.code === "ENOENT") resolve18(127);
3582
+ if (error.code === "ENOENT") resolve19(127);
3430
3583
  else reject2(error);
3431
3584
  });
3432
- child.on("close", (code) => resolve18(code ?? 1));
3585
+ child.on("close", (code) => resolve19(code ?? 1));
3433
3586
  });
3434
3587
  var NEXT_PREVIEW_WARNING = "Next.js pages have no watermarks without --preview; the Studio will open but nothing is editable.";
3435
3588
  var NEXT_GITIGNORE_WARNING = "Add public/livelocale/ to .gitignore; it is generated on every build.";
@@ -3878,8 +4031,8 @@ function createGitHubRest(config) {
3878
4031
  "X-GitHub-Api-Version": "2022-11-28",
3879
4032
  ...hasBody ? { "Content-Type": "application/json" } : {}
3880
4033
  });
3881
- async function api(method, path22, options = {}) {
3882
- const url = new URL(`${apiBase}/repos/${repo}${path22}`);
4034
+ async function api(method, path23, options = {}) {
4035
+ const url = new URL(`${apiBase}/repos/${repo}${path23}`);
3883
4036
  for (const [k, v] of Object.entries(options.query ?? {})) url.searchParams.set(k, v);
3884
4037
  const response = await fetchImpl(url.toString(), {
3885
4038
  method,
@@ -3896,14 +4049,14 @@ function createGitHubRest(config) {
3896
4049
  const detail = data?.message || "request failed";
3897
4050
  const hint = /not accessible by/i.test(detail) ? ' The token lacks a permission: give it "Contents" and "Pull requests" with "Read and write" on this repository.' : "";
3898
4051
  throw new GitHubApiError(
3899
- `GitHub API ${response.status} on ${method} ${path22}: ${detail}.${hint}`,
4052
+ `GitHub API ${response.status} on ${method} ${path23}: ${detail}.${hint}`,
3900
4053
  response.status
3901
4054
  );
3902
4055
  }
3903
4056
  return { status: response.status, data };
3904
4057
  }
3905
- async function probeStatus(method, path22, body) {
3906
- const response = await fetchImpl(`${apiBase}/repos/${repo}${path22}`, {
4058
+ async function probeStatus(method, path23, body) {
4059
+ const response = await fetchImpl(`${apiBase}/repos/${repo}${path23}`, {
3907
4060
  method,
3908
4061
  headers: headers(body !== void 0),
3909
4062
  body: body !== void 0 ? JSON.stringify(body) : void 0
@@ -4765,7 +4918,7 @@ function clipboardCommands(platform) {
4765
4918
  }
4766
4919
  var TOO_LARGE = /* @__PURE__ */ Symbol("too large");
4767
4920
  function run(execFileImpl, [command, args]) {
4768
- return new Promise((resolve18) => {
4921
+ return new Promise((resolve19) => {
4769
4922
  try {
4770
4923
  execFileImpl(
4771
4924
  command,
@@ -4773,12 +4926,12 @@ function run(execFileImpl, [command, args]) {
4773
4926
  { encoding: "utf8", windowsHide: true, maxBuffer: MAX_APPLY_INPUT_BYTES + 1 },
4774
4927
  (error, stdout) => {
4775
4928
  const code = error?.code;
4776
- if (code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") resolve18(TOO_LARGE);
4777
- else resolve18(error ? null : String(stdout));
4929
+ if (code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") resolve19(TOO_LARGE);
4930
+ else resolve19(error ? null : String(stdout));
4778
4931
  }
4779
4932
  );
4780
4933
  } catch {
4781
- resolve18(null);
4934
+ resolve19(null);
4782
4935
  }
4783
4936
  });
4784
4937
  }
@@ -5373,30 +5526,30 @@ var GITHUB_REMOTE_RE = /github\.com[:/]([\w.-]+\/[\w.-]+?)(?:\.git)?$/;
5373
5526
  var defaultGit = (args, cwd, opts) => {
5374
5527
  const env = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
5375
5528
  if (opts?.inherit) {
5376
- return new Promise((resolve18, reject2) => {
5529
+ return new Promise((resolve19, reject2) => {
5377
5530
  const child = spawn2("git", args, { cwd, env, stdio: "inherit", windowsHide: true });
5378
5531
  child.on("error", reject2);
5379
5532
  child.on(
5380
5533
  "close",
5381
- (code) => code === 0 ? resolve18("") : reject2(new Error(`git ${args[0]} failed (exit ${code}).`))
5534
+ (code) => code === 0 ? resolve19("") : reject2(new Error(`git ${args[0]} failed (exit ${code}).`))
5382
5535
  );
5383
5536
  });
5384
5537
  }
5385
- return new Promise((resolve18, reject2) => {
5538
+ return new Promise((resolve19, reject2) => {
5386
5539
  execFile4(
5387
5540
  "git",
5388
5541
  args,
5389
5542
  { cwd, env, windowsHide: true, maxBuffer: 16 * 1024 * 1024 },
5390
- (error, stdout) => error ? reject2(error) : resolve18(String(stdout))
5543
+ (error, stdout) => error ? reject2(error) : resolve19(String(stdout))
5391
5544
  );
5392
5545
  });
5393
5546
  };
5394
- var defaultGh = (args, cwd) => new Promise((resolve18, reject2) => {
5547
+ var defaultGh = (args, cwd) => new Promise((resolve19, reject2) => {
5395
5548
  execFile4(
5396
5549
  "gh",
5397
5550
  args,
5398
5551
  { cwd, windowsHide: true },
5399
- (error, stdout, stderr) => error ? reject2(Object.assign(error, { stderr: String(stderr) })) : resolve18(String(stdout))
5552
+ (error, stdout, stderr) => error ? reject2(Object.assign(error, { stderr: String(stderr) })) : resolve19(String(stdout))
5400
5553
  );
5401
5554
  });
5402
5555
  function utcStamp(date) {
@@ -5597,70 +5750,42 @@ async function runLocalApply(options) {
5597
5750
  }
5598
5751
 
5599
5752
  // src/codemod/apply/cli.ts
5600
- var VALUE_OPTIONS2 = { "--dir": "dir", "--locales": "locales", "--source": "source" };
5601
- var FLAG_OPTIONS = {
5602
- "--clipboard": "clipboard",
5603
- "--pr": "pr",
5604
- "--force": "force",
5605
- "--dry-run": "dryRun",
5606
- "--github-event": "githubEvent"
5607
- };
5608
5753
  function parseApplyArgs(argv) {
5754
+ const parsed = parseCommandArgs(COMMAND_SPECS.apply, argv);
5755
+ if (parsed.positionals.length > 1) throw new UsageError('Pass one change link, file or "-".');
5756
+ const branches = parsed.values["--branches"];
5609
5757
  const args = {
5610
- input: void 0,
5611
- clipboard: false,
5612
- pr: false,
5613
- force: false,
5614
- dryRun: false,
5615
- dir: ".",
5616
- locales: void 0,
5617
- source: void 0,
5618
- githubEvent: false,
5619
- branches: void 0
5758
+ input: parsed.positionals[0],
5759
+ clipboard: parsed.flags.has("--clipboard"),
5760
+ pr: parsed.flags.has("--pr"),
5761
+ force: parsed.flags.has("--force"),
5762
+ dryRun: parsed.flags.has("--dry-run"),
5763
+ dir: parsed.values["--dir"] ?? ".",
5764
+ locales: parsed.values["--locales"],
5765
+ source: parsed.values["--source"],
5766
+ githubEvent: parsed.flags.has("--github-event"),
5767
+ branches: branches === void 0 ? void 0 : splitList(branches)
5620
5768
  };
5621
- for (let i = 0; i < argv.length; i++) {
5622
- const arg = argv[i];
5623
- if (arg === "--branches") {
5624
- const value = argv[i + 1];
5625
- if (value === void 0 || value.startsWith("--")) {
5626
- throw new ApplyInputError(`${arg} needs a value.`);
5627
- }
5628
- args.branches = value.split(",").map((branch) => branch.trim()).filter(Boolean);
5629
- i++;
5630
- } else if (Object.prototype.hasOwnProperty.call(VALUE_OPTIONS2, arg)) {
5631
- const value = argv[i + 1];
5632
- if (value === void 0 || value.startsWith("--")) {
5633
- throw new ApplyInputError(`${arg} needs a value.`);
5634
- }
5635
- args[VALUE_OPTIONS2[arg]] = value;
5636
- i++;
5637
- } else if (Object.prototype.hasOwnProperty.call(FLAG_OPTIONS, arg)) {
5638
- args[FLAG_OPTIONS[arg]] = true;
5639
- } else if (arg.startsWith("-") && arg !== "-") {
5640
- throw new ApplyInputError(`Unknown option "${arg}".`);
5641
- } else if (args.input !== void 0) {
5642
- throw new ApplyInputError('Pass one change link, file or "-".');
5643
- } else {
5644
- args.input = arg;
5645
- }
5646
- }
5647
5769
  if (args.clipboard && args.input !== void 0) {
5648
- throw new ApplyInputError("Pass the link or --clipboard, not both.");
5770
+ throw new UsageError("Pass the link or --clipboard, not both.");
5771
+ }
5772
+ if (args.branches !== void 0 && !args.githubEvent) {
5773
+ throw new UsageError("--branches only works with --github-event.");
5649
5774
  }
5650
5775
  if (args.githubEvent) {
5651
5776
  if (args.force) {
5652
- throw new ApplyInputError(
5777
+ throw new UsageError(
5653
5778
  "--force is not allowed with --github-event; a maintainer comments `/livelocale apply --force`."
5654
5779
  );
5655
5780
  }
5656
5781
  if (args.input !== void 0) {
5657
- throw new ApplyInputError(
5658
- "--github-event reads the link from the event file; do not pass one."
5659
- );
5782
+ throw new UsageError("--github-event reads the link from the event file; do not pass one.");
5660
5783
  }
5661
5784
  if (args.locales === void 0 || args.source === void 0) {
5662
- throw new ApplyInputError("--github-event needs --locales and --source.");
5785
+ throw new UsageError("--github-event needs --locales and --source.");
5663
5786
  }
5787
+ } else if (!args.clipboard && (args.input === void 0 || args.input === "")) {
5788
+ throw new UsageError(NO_INPUT_MESSAGE);
5664
5789
  }
5665
5790
  return args;
5666
5791
  }
@@ -5694,7 +5819,7 @@ async function runApplyCommand(argv) {
5694
5819
  process.exitCode = result.exitCode;
5695
5820
  } catch (err) {
5696
5821
  console.error(`\u274C ${err instanceof Error ? err.message : String(err)}`);
5697
- process.exitCode = 1;
5822
+ process.exitCode = err instanceof UsageError ? USAGE_EXIT_CODE : 1;
5698
5823
  }
5699
5824
  }
5700
5825
 
@@ -5799,37 +5924,6 @@ function renderWorkflow(o) {
5799
5924
  ""
5800
5925
  ].join("\n");
5801
5926
  }
5802
- function parseInitShareArgs(argv) {
5803
- const args = {
5804
- dir: ".",
5805
- localesPath: void 0,
5806
- sourceLocale: void 0,
5807
- branches: void 0,
5808
- overwrite: false
5809
- };
5810
- const valueAt = (i) => {
5811
- const value = argv[i + 1];
5812
- if (value === void 0 || value.startsWith("--")) {
5813
- throw new Error(`${argv[i]} needs a value.`);
5814
- }
5815
- return value;
5816
- };
5817
- for (let i = 0; i < argv.length; i++) {
5818
- const arg = argv[i];
5819
- if (arg === "--dir") {
5820
- args.dir = valueAt(i++);
5821
- } else if (arg === "--locales") {
5822
- args.localesPath = valueAt(i++);
5823
- } else if (arg === "--source") {
5824
- args.sourceLocale = valueAt(i++);
5825
- } else if (arg === "--branches") {
5826
- args.branches = valueAt(i++).split(",").map((b) => b.trim()).filter(Boolean);
5827
- } else if (arg === "--overwrite") {
5828
- args.overwrite = true;
5829
- }
5830
- }
5831
- return args;
5832
- }
5833
5927
  function toPosix2(p) {
5834
5928
  return p.split(path20.sep).join("/");
5835
5929
  }
@@ -6059,11 +6153,96 @@ function writePlannedFiles(projectDir, files) {
6059
6153
  }
6060
6154
  }
6061
6155
 
6062
- // src/codemod/index.ts
6156
+ // src/codemod/command-args.ts
6157
+ function commandSpec(command) {
6158
+ const name = command === "build" ? "pack" : command;
6159
+ return name !== void 0 && Object.prototype.hasOwnProperty.call(COMMAND_SPECS, name) ? COMMAND_SPECS[name] : void 0;
6160
+ }
6063
6161
  var DEFAULT_STUDIO_PORT = 3010;
6064
6162
  var DEFAULT_APP_ORIGIN = "http://localhost:3000";
6065
- function printHelp() {
6066
- console.log(`
6163
+ var DEFAULT_EXCLUDES = ["node_modules", ".next", "dist", ".git"];
6164
+ function parseMigrateArgs(argv) {
6165
+ const parsed = parseCommandArgs(COMMAND_SPECS.migrate, argv);
6166
+ const exclude = parsed.values["--exclude"];
6167
+ return {
6168
+ dir: folderArg(parsed),
6169
+ excludes: exclude === void 0 ? [...DEFAULT_EXCLUDES] : splitList(exclude),
6170
+ dryRun: parsed.flags.has("--dry-run")
6171
+ };
6172
+ }
6173
+ function parseDoctorArgs(argv) {
6174
+ const parsed = parseCommandArgs(COMMAND_SPECS.doctor, argv);
6175
+ const app = parsed.values["--app"];
6176
+ const args = { dir: folderArg(parsed), share: parsed.flags.has("--share") };
6177
+ if (parsed.values["--locales"] !== void 0) args.localesPath = parsed.values["--locales"];
6178
+ if (parsed.values["--token"] !== void 0) args.token = parsed.values["--token"];
6179
+ if (app !== void 0) {
6180
+ try {
6181
+ args.app = normalizeAppUrl(app);
6182
+ } catch {
6183
+ throw new UsageError(
6184
+ `--app must be an http(s) URL such as http://localhost:3000 or https://user.github.io/repo/ (got "${app}").`
6185
+ );
6186
+ }
6187
+ }
6188
+ return args;
6189
+ }
6190
+ function parseStudioArgs(argv) {
6191
+ const parsed = parseCommandArgs(COMMAND_SPECS.studio, argv);
6192
+ const port = parsed.values["--port"];
6193
+ const app = parsed.values["--app"];
6194
+ const args = { port: DEFAULT_STUDIO_PORT, appOrigin: DEFAULT_APP_ORIGIN };
6195
+ if (port !== void 0) {
6196
+ const value = Number(port);
6197
+ if (!/^\d+$/.test(port) || value < 1 || value > 65535) {
6198
+ throw new UsageError(`--port must be a port number from 1 to 65535 (got "${port}").`);
6199
+ }
6200
+ args.port = value;
6201
+ }
6202
+ if (app !== void 0) {
6203
+ try {
6204
+ const url = new URL(app);
6205
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error("bad protocol");
6206
+ args.appOrigin = url.origin;
6207
+ } catch {
6208
+ throw new UsageError(
6209
+ `--app must be an http(s) origin such as http://localhost:3001 (got "${app}").`
6210
+ );
6211
+ }
6212
+ }
6213
+ return args;
6214
+ }
6215
+ var SHARE_ONLY = ["--locales", "--source", "--branches"];
6216
+ function parseInitArgs(argv) {
6217
+ const parsed = parseCommandArgs(COMMAND_SPECS.init, argv);
6218
+ if (!parsed.flags.has("--share")) {
6219
+ for (const flag of [...SHARE_ONLY, "--overwrite"]) {
6220
+ if (parsed.values[flag] !== void 0 || parsed.flags.has(flag)) {
6221
+ throw new UsageError(`${flag} only works with init --share.`);
6222
+ }
6223
+ }
6224
+ return { share: false, dir: folderArg(parsed) };
6225
+ }
6226
+ const folder = parsed.positionals[0];
6227
+ if (folder !== void 0) {
6228
+ throw new UsageError(
6229
+ `init --share takes the project folder as --dir ${folder}, not as a plain argument.`
6230
+ );
6231
+ }
6232
+ const branches = parsed.values["--branches"];
6233
+ return {
6234
+ share: true,
6235
+ dir: parsed.values["--dir"] ?? ".",
6236
+ localesPath: parsed.values["--locales"],
6237
+ sourceLocale: parsed.values["--source"],
6238
+ branches: branches === void 0 ? void 0 : splitList(branches),
6239
+ overwrite: parsed.flags.has("--overwrite")
6240
+ };
6241
+ }
6242
+
6243
+ // src/codemod/help.ts
6244
+ function helpText() {
6245
+ return `
6067
6246
  LiveLocale (livelocale) CLI
6068
6247
 
6069
6248
  Usage:
@@ -6140,41 +6319,129 @@ Options for apply:
6140
6319
  --source <locale> Source locale (default: en, else the first locale)
6141
6320
  --force Apply edits the translator saved with "Save anyway"
6142
6321
  --dry-run Show what would change without writing files
6143
- --github-event Run inside the generated GitHub workflow; reads the link from the event file
6144
- --branches <a,b> Branches a link may target (default: the repository's default branch)
6322
+ --github-event Run inside the generated GitHub workflow; reads the link from the event file
6323
+ --branches <a,b> Branches a link may target (default: the repository's default branch)
6145
6324
 
6146
6325
  Global Options:
6147
6326
  -h, --help Show help information
6148
- `);
6327
+ -v, --version Print the livelocale version
6328
+
6329
+ Exit codes:
6330
+ 0 Success.
6331
+ 1 The command ran and found problems, or could not finish: check found a problem, doctor
6332
+ found a blocking problem, migrate could not convert a file, apply or pack could not finish.
6333
+ 2 Usage error: an unknown or missing command, an unknown option, a missing value, an argument
6334
+ the command does not take, or options that do not go together. check also exits 2 when it
6335
+ could not run (missing folder, no locales folder or no locale files in it, unknown --source,
6336
+ TypeScript missing for --hardcoded).
6337
+ build exits with your build command's own code once it has started it.
6338
+ `;
6149
6339
  }
6150
- function normalizeOriginArg(value, flag) {
6340
+
6341
+ // src/codemod/migrate.ts
6342
+ import * as fs14 from "fs";
6343
+ import * as path21 from "path";
6344
+ var SOURCE_EXTENSIONS2 = [".js", ".jsx", ".ts", ".tsx"];
6345
+ async function loadTransform() {
6346
+ const transform = await Promise.resolve().then(() => (init_transform(), transform_exports));
6347
+ await transform.loadTypeScript();
6348
+ return transform.transformReactI18next;
6349
+ }
6350
+ async function runMigrate({ dir, excludes, dryRun }, transform) {
6351
+ const absoluteDir = path21.resolve(dir);
6352
+ if (!fs14.existsSync(absoluteDir)) {
6353
+ console.error(`Error: Directory "${dir}" does not exist.`);
6354
+ return 1;
6355
+ }
6356
+ let convert;
6151
6357
  try {
6152
- const url = new URL(value);
6153
- if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error("bad protocol");
6154
- return url.origin;
6155
- } catch {
6156
- console.error(
6157
- `Error: ${flag} must be an http(s) origin such as http://localhost:3001 (got "${value}").`
6158
- );
6159
- process.exit(1);
6358
+ convert = transform ?? await loadTransform();
6359
+ } catch (err) {
6360
+ console.error(`\u274C ${err.message}`);
6361
+ return 1;
6160
6362
  }
6161
- }
6162
- function normalizeAppArg(value) {
6363
+ console.log(`
6364
+ \u{1F680} Starting migration scanner...`);
6365
+ console.log(`\u{1F4C1} Target: ${absoluteDir}`);
6366
+ console.log(`\u{1F6AB} Excluded: ${excludes.join(", ")}`);
6367
+ if (dryRun) {
6368
+ console.log(`\u{1F9EA} Mode: DRY RUN (no files will be modified)`);
6369
+ }
6370
+ console.log("--------------------------------------------------");
6371
+ let scannedCount = 0;
6372
+ let migratedCount = 0;
6373
+ const failed = [];
6374
+ async function walk(currentDir) {
6375
+ const files = fs14.readdirSync(currentDir, { withFileTypes: true });
6376
+ for (const file of files) {
6377
+ const fullPath = path21.join(currentDir, file.name);
6378
+ if (file.isDirectory()) {
6379
+ if (excludes.includes(file.name)) {
6380
+ continue;
6381
+ }
6382
+ await walk(fullPath);
6383
+ } else if (file.isFile() && SOURCE_EXTENSIONS2.includes(path21.extname(file.name))) {
6384
+ scannedCount++;
6385
+ const relativePath = path21.relative(absoluteDir, fullPath);
6386
+ try {
6387
+ const content = fs14.readFileSync(fullPath, "utf8");
6388
+ const result = await convert(content, fullPath);
6389
+ if (result.modified) {
6390
+ if (dryRun) {
6391
+ console.log(`[DRY RUN] Would migrate: ${relativePath}`);
6392
+ } else {
6393
+ fs14.writeFileSync(fullPath, result.code, "utf8");
6394
+ console.log(`\u2705 Migrated: ${relativePath}`);
6395
+ }
6396
+ migratedCount++;
6397
+ }
6398
+ } catch (err) {
6399
+ failed.push(relativePath);
6400
+ console.error(`\u274C Error processing file ${fullPath}:`, err.message);
6401
+ }
6402
+ }
6403
+ }
6404
+ }
6405
+ let walkError;
6163
6406
  try {
6164
- return normalizeAppUrl(value);
6165
- } catch {
6166
- console.error(
6167
- `Error: --app must be an http(s) URL such as http://localhost:3000 or https://user.github.io/repo/ (got "${value}").`
6168
- );
6169
- process.exit(1);
6407
+ await walk(absoluteDir);
6408
+ } catch (err) {
6409
+ walkError = err.message;
6410
+ console.error(`\u274C Walk error:`, err.message);
6170
6411
  }
6412
+ console.log("--------------------------------------------------");
6413
+ console.log(`\u{1F4CA} Summary:`);
6414
+ console.log(` Scanned: ${scannedCount} files`);
6415
+ if (dryRun) {
6416
+ console.log(` Would migrate: ${migratedCount} files`);
6417
+ } else {
6418
+ console.log(` Migrated: ${migratedCount} files`);
6419
+ }
6420
+ if (failed.length > 0 || walkError !== void 0) {
6421
+ if (failed.length > 0) {
6422
+ console.error(` Failed: ${failed.length} file(s)`);
6423
+ for (const file of failed) console.error(` ${file}`);
6424
+ }
6425
+ if (walkError !== void 0) console.error(` The scan stopped early: ${walkError}`);
6426
+ console.error(`
6427
+ \u274C Not every file was converted. Fix the files above and run migrate again.`);
6428
+ return 1;
6429
+ }
6430
+ console.log(`
6431
+ \u{1F389} Done!`);
6432
+ return 0;
6433
+ }
6434
+
6435
+ // src/codemod/index.ts
6436
+ function printHelp() {
6437
+ console.log(helpText());
6171
6438
  }
6172
6439
  function isPortAnswering(port) {
6173
- return new Promise((resolve18) => {
6440
+ return new Promise((resolve19) => {
6174
6441
  const socket = net.connect({ host: "localhost", port });
6175
6442
  const done = (answering) => {
6176
6443
  socket.destroy();
6177
- resolve18(answering);
6444
+ resolve19(answering);
6178
6445
  };
6179
6446
  socket.setTimeout(1e3, () => done(false));
6180
6447
  socket.once("connect", () => done(true));
@@ -6184,8 +6451,8 @@ function isPortAnswering(port) {
6184
6451
  async function runStudio(port, appOrigin) {
6185
6452
  console.log(`
6186
6453
  \u{1F680} Starting LiveLocale Studio...`);
6187
- const studioDir = path21.resolve(moduleDir(), "../studio");
6188
- if (!fs14.existsSync(path21.join(studioDir, "index.html"))) {
6454
+ const studioDir = path22.resolve(moduleDir(), "../studio");
6455
+ if (!fs15.existsSync(path22.join(studioDir, "index.html"))) {
6189
6456
  console.error(`Error: Studio assets not found at "${studioDir}".`);
6190
6457
  console.error(`Did you build the project? Run "npm run build" first.`);
6191
6458
  process.exit(1);
@@ -6217,48 +6484,48 @@ async function runStudio(port, appOrigin) {
6217
6484
  function runInit(projectDir) {
6218
6485
  console.log(`
6219
6486
  \u{1F680} Initializing LiveLocale API handler...`);
6220
- const absoluteDir = path21.resolve(projectDir);
6221
- if (!fs14.existsSync(absoluteDir)) {
6487
+ const absoluteDir = path22.resolve(projectDir);
6488
+ if (!fs15.existsSync(absoluteDir)) {
6222
6489
  console.error(`Error: Directory "${projectDir}" does not exist.`);
6223
6490
  process.exit(1);
6224
6491
  }
6225
- const isTypeScript = fs14.existsSync(path21.join(absoluteDir, "tsconfig.json"));
6492
+ const isTypeScript = fs15.existsSync(path22.join(absoluteDir, "tsconfig.json"));
6226
6493
  const fileExt = isTypeScript ? "ts" : "js";
6227
6494
  console.log(`\u{1F4C1} Target: ${absoluteDir}`);
6228
6495
  console.log(`\u{1F527} Language detected: ${isTypeScript ? "TypeScript" : "JavaScript"}`);
6229
6496
  let hasNextIntl = false;
6230
6497
  let hasReactI18next = false;
6231
- const targetPkgPath = path21.join(absoluteDir, "package.json");
6232
- if (fs14.existsSync(targetPkgPath)) {
6498
+ const targetPkgPath = path22.join(absoluteDir, "package.json");
6499
+ if (fs15.existsSync(targetPkgPath)) {
6233
6500
  try {
6234
- const pkg = JSON.parse(fs14.readFileSync(targetPkgPath, "utf8"));
6501
+ const pkg = JSON.parse(fs15.readFileSync(targetPkgPath, "utf8"));
6235
6502
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
6236
6503
  hasNextIntl = Boolean(allDeps["next-intl"]);
6237
6504
  hasReactI18next = Boolean(allDeps["react-i18next"]);
6238
6505
  } catch {
6239
6506
  }
6240
6507
  }
6241
- const appDirPath = path21.join(absoluteDir, "app");
6242
- const pagesDirPath = path21.join(absoluteDir, "pages");
6243
- const srcAppDirPath = path21.join(absoluteDir, "src", "app");
6244
- const srcPagesDirPath = path21.join(absoluteDir, "src", "pages");
6508
+ const appDirPath = path22.join(absoluteDir, "app");
6509
+ const pagesDirPath = path22.join(absoluteDir, "pages");
6510
+ const srcAppDirPath = path22.join(absoluteDir, "src", "app");
6511
+ const srcPagesDirPath = path22.join(absoluteDir, "src", "pages");
6245
6512
  let targetPath = "";
6246
6513
  let routerType = "";
6247
6514
  let content = "";
6248
- if (fs14.existsSync(appDirPath)) {
6249
- targetPath = path21.join(appDirPath, "api", "livelocale", `route.${fileExt}`);
6515
+ if (fs15.existsSync(appDirPath)) {
6516
+ targetPath = path22.join(appDirPath, "api", "livelocale", `route.${fileExt}`);
6250
6517
  routerType = "Next.js App Router (root)";
6251
- } else if (fs14.existsSync(srcAppDirPath)) {
6252
- targetPath = path21.join(srcAppDirPath, "api", "livelocale", `route.${fileExt}`);
6518
+ } else if (fs15.existsSync(srcAppDirPath)) {
6519
+ targetPath = path22.join(srcAppDirPath, "api", "livelocale", `route.${fileExt}`);
6253
6520
  routerType = "Next.js App Router (src/)";
6254
- } else if (fs14.existsSync(pagesDirPath)) {
6255
- targetPath = path21.join(pagesDirPath, "api", `livelocale.${fileExt}`);
6521
+ } else if (fs15.existsSync(pagesDirPath)) {
6522
+ targetPath = path22.join(pagesDirPath, "api", `livelocale.${fileExt}`);
6256
6523
  routerType = "Next.js Pages Router (root)";
6257
- } else if (fs14.existsSync(srcPagesDirPath)) {
6258
- targetPath = path21.join(srcPagesDirPath, "api", `livelocale.${fileExt}`);
6524
+ } else if (fs15.existsSync(srcPagesDirPath)) {
6525
+ targetPath = path22.join(srcPagesDirPath, "api", `livelocale.${fileExt}`);
6259
6526
  routerType = "Next.js Pages Router (src/)";
6260
6527
  } else {
6261
- targetPath = path21.join(absoluteDir, "app", "api", "livelocale", `route.${fileExt}`);
6528
+ targetPath = path22.join(absoluteDir, "app", "api", "livelocale", `route.${fileExt}`);
6262
6529
  routerType = "Default App Router (app/ not found)";
6263
6530
  }
6264
6531
  console.log(`\u{1F4CD} Project structure: ${routerType}`);
@@ -6286,14 +6553,14 @@ const handler = createLiveLocaleHandler({
6286
6553
  export default handler;
6287
6554
  `;
6288
6555
  }
6289
- const targetDir = path21.dirname(targetPath);
6290
- if (!fs14.existsSync(targetDir)) {
6291
- fs14.mkdirSync(targetDir, { recursive: true });
6556
+ const targetDir = path22.dirname(targetPath);
6557
+ if (!fs15.existsSync(targetDir)) {
6558
+ fs15.mkdirSync(targetDir, { recursive: true });
6292
6559
  }
6293
6560
  try {
6294
- fs14.writeFileSync(targetPath, content, "utf8");
6561
+ fs15.writeFileSync(targetPath, content, "utf8");
6295
6562
  console.log(`\u2705 Created API Route Handler successfully at:`);
6296
- console.log(` ${path21.relative(absoluteDir, targetPath)}`);
6563
+ console.log(` ${path22.relative(absoluteDir, targetPath)}`);
6297
6564
  if (hasNextIntl) {
6298
6565
  console.log(`
6299
6566
  \u{1F4A1} Detected 'next-intl' in your project!`);
@@ -6314,124 +6581,9 @@ export default handler;
6314
6581
  process.exit(1);
6315
6582
  }
6316
6583
  }
6317
- async function runMigrate(args) {
6318
- let dir = ".";
6319
- let excludes = ["node_modules", ".next", "dist", ".git"];
6320
- let dryRun = false;
6321
- for (let i = 1; i < args.length; i++) {
6322
- const arg = args[i];
6323
- if (arg === "--dir") {
6324
- dir = args[++i] || ".";
6325
- } else if (arg === "--exclude") {
6326
- const val = args[++i];
6327
- if (val) {
6328
- excludes = val.split(",").map((s) => s.trim());
6329
- }
6330
- } else if (arg === "--dry-run") {
6331
- dryRun = true;
6332
- } else {
6333
- if (arg && !arg.startsWith("--")) {
6334
- dir = arg;
6335
- }
6336
- }
6337
- }
6338
- const absoluteDir = path21.resolve(dir);
6339
- if (!fs14.existsSync(absoluteDir)) {
6340
- console.error(`Error: Directory "${dir}" does not exist.`);
6341
- process.exit(1);
6342
- }
6343
- let transformReactI18next2;
6344
- try {
6345
- const transform = await Promise.resolve().then(() => (init_transform(), transform_exports));
6346
- await transform.loadTypeScript();
6347
- transformReactI18next2 = transform.transformReactI18next;
6348
- } catch (err) {
6349
- console.error(`\u274C ${err.message}`);
6350
- process.exit(1);
6351
- }
6352
- console.log(`
6353
- \u{1F680} Starting migration scanner...`);
6354
- console.log(`\u{1F4C1} Target: ${absoluteDir}`);
6355
- console.log(`\u{1F6AB} Excluded: ${excludes.join(", ")}`);
6356
- if (dryRun) {
6357
- console.log(`\u{1F9EA} Mode: DRY RUN (no files will be modified)`);
6358
- }
6359
- console.log("--------------------------------------------------");
6360
- let scannedCount = 0;
6361
- let migratedCount = 0;
6362
- async function walk(currentDir) {
6363
- const files = fs14.readdirSync(currentDir, { withFileTypes: true });
6364
- for (const file of files) {
6365
- const fullPath = path21.join(currentDir, file.name);
6366
- if (file.isDirectory()) {
6367
- if (excludes.includes(file.name)) {
6368
- continue;
6369
- }
6370
- await walk(fullPath);
6371
- } else if (file.isFile()) {
6372
- const ext = path21.extname(file.name);
6373
- if ([".js", ".jsx", ".ts", ".tsx"].includes(ext)) {
6374
- scannedCount++;
6375
- try {
6376
- const content = fs14.readFileSync(fullPath, "utf8");
6377
- const result = await transformReactI18next2(content, fullPath);
6378
- if (result.modified) {
6379
- migratedCount++;
6380
- const relativePath = path21.relative(absoluteDir, fullPath);
6381
- if (dryRun) {
6382
- console.log(`[DRY RUN] Would migrate: ${relativePath}`);
6383
- } else {
6384
- fs14.writeFileSync(fullPath, result.code, "utf8");
6385
- console.log(`\u2705 Migrated: ${relativePath}`);
6386
- }
6387
- }
6388
- } catch (err) {
6389
- console.error(`\u274C Error processing file ${fullPath}:`, err.message);
6390
- }
6391
- }
6392
- }
6393
- }
6394
- }
6395
- try {
6396
- await walk(absoluteDir);
6397
- } catch (err) {
6398
- console.error(`\u274C Walk error:`, err.message);
6399
- }
6400
- console.log("--------------------------------------------------");
6401
- console.log(`\u{1F4CA} Summary:`);
6402
- console.log(` Scanned: ${scannedCount} files`);
6403
- if (dryRun) {
6404
- console.log(` Would migrate: ${migratedCount} files`);
6405
- } else {
6406
- console.log(` Migrated: ${migratedCount} files`);
6407
- }
6408
- console.log(`
6409
- \u{1F389} Done!`);
6410
- }
6411
- async function runDoctorCommand(args) {
6412
- let dir = ".";
6413
- let localesPath;
6414
- let app;
6415
- let share = false;
6416
- let token;
6417
- for (let i = 1; i < args.length; i++) {
6418
- const arg = args[i];
6419
- if (arg === "--dir") {
6420
- dir = args[++i] || ".";
6421
- } else if (arg === "--locales") {
6422
- localesPath = args[++i];
6423
- } else if (arg === "--app") {
6424
- app = normalizeAppArg(args[++i] || "");
6425
- } else if (arg === "--share") {
6426
- share = true;
6427
- } else if (arg === "--token") {
6428
- token = args[++i];
6429
- } else if (arg && !arg.startsWith("--")) {
6430
- dir = arg;
6431
- }
6432
- }
6433
- const projectDir = path21.resolve(dir);
6434
- if (!fs14.existsSync(projectDir)) {
6584
+ async function runDoctorCommand({ dir, localesPath, app, share, token }) {
6585
+ const projectDir = path22.resolve(dir);
6586
+ if (!fs15.existsSync(projectDir)) {
6435
6587
  console.error(`Error: Directory "${dir}" does not exist.`);
6436
6588
  process.exit(1);
6437
6589
  }
@@ -6467,19 +6619,25 @@ async function runDoctorCommand(args) {
6467
6619
  );
6468
6620
  }
6469
6621
  }
6470
- async function runCheckCommand(args) {
6622
+ async function runCheckCommand(argv) {
6623
+ const json = argv.includes("--json");
6624
+ const couldNotRun = (message, localesDir = null) => {
6625
+ if (json) console.log(JSON.stringify(checkErrorReport(message, localesDir), null, 2));
6626
+ else console.error(`\u274C ${message}`);
6627
+ process.exitCode = CHECK_COULD_NOT_RUN;
6628
+ };
6471
6629
  let parsed;
6472
6630
  try {
6473
- parsed = parseCheckArgs(args.slice(1));
6631
+ parsed = parseCheckArgs(argv);
6474
6632
  } catch (err) {
6475
- console.error(`Error: ${err.message}`);
6476
- process.exit(1);
6633
+ couldNotRun(err.message);
6634
+ return;
6477
6635
  }
6478
- const { dir, localesPath, sourceLocale, unused, strict, json, hardcoded, allow } = parsed;
6479
- const projectDir = path21.resolve(dir);
6480
- if (!fs14.existsSync(projectDir)) {
6481
- console.error(`Error: Directory "${dir}" does not exist.`);
6482
- process.exit(1);
6636
+ const { dir, localesPath, sourceLocale, unused, strict, hardcoded, allow } = parsed;
6637
+ const projectDir = path22.resolve(dir);
6638
+ if (!fs15.existsSync(projectDir)) {
6639
+ couldNotRun(`Directory "${dir}" does not exist.`);
6640
+ return;
6483
6641
  }
6484
6642
  let report;
6485
6643
  try {
@@ -6493,12 +6651,12 @@ async function runCheckCommand(args) {
6493
6651
  allow
6494
6652
  });
6495
6653
  } catch (err) {
6496
- console.error(`\u274C ${err.message}`);
6497
- process.exit(1);
6654
+ couldNotRun(err?.message ?? String(err), path22.resolve(projectDir, localesPath ?? "locales"));
6655
+ return;
6498
6656
  }
6499
6657
  process.exitCode = checkExitCode(report);
6500
6658
  if (json) {
6501
- console.log(JSON.stringify(report, null, 2));
6659
+ console.log(JSON.stringify(withCheckError(report), null, 2));
6502
6660
  return;
6503
6661
  }
6504
6662
  console.log(`
@@ -6515,7 +6673,7 @@ async function runCheckCommand(args) {
6515
6673
  }
6516
6674
  function printPackSummary(result, projectDir, warnings) {
6517
6675
  for (const warning of warnings) console.log(`\u26A0\uFE0F ${warning}`);
6518
- const outRel = path21.relative(projectDir, path21.dirname(result.shareDir)).split(path21.sep).join("/") || ".";
6676
+ const outRel = path22.relative(projectDir, path22.dirname(result.shareDir)).split(path22.sep).join("/") || ".";
6519
6677
  console.log(
6520
6678
  `\u2705 LiveLocale share folder written to ${outRel}/livelocale/ (${result.manifest.locales.length} locales, ${result.manifest.codeKeys.length} code keys, ${result.htmlInjected.length} HTML files linked).`
6521
6679
  );
@@ -6523,9 +6681,9 @@ function printPackSummary(result, projectDir, warnings) {
6523
6681
  }
6524
6682
  async function runPackCommand(args) {
6525
6683
  try {
6526
- const options = parseShareArgs(args.slice(1));
6684
+ const options = parseShareArgs(args.slice(1), "pack");
6527
6685
  checkPackArgs(options);
6528
- const projectDir = path21.resolve(options.dir);
6686
+ const projectDir = path22.resolve(options.dir);
6529
6687
  const result = await packOutput({
6530
6688
  projectDir,
6531
6689
  outDir: options.positional[0],
@@ -6539,14 +6697,14 @@ async function runPackCommand(args) {
6539
6697
  } catch (err) {
6540
6698
  if (!(err instanceof PackError)) throw err;
6541
6699
  console.error(`\u274C ${err.message}`);
6542
- process.exitCode = 1;
6700
+ process.exitCode = err.code === "USAGE" ? USAGE_EXIT_CODE : 1;
6543
6701
  }
6544
6702
  }
6545
6703
  async function runBuildCli(args) {
6546
6704
  try {
6547
- const options = parseShareArgs(args.slice(1));
6705
+ const options = parseShareArgs(args.slice(1), "build");
6548
6706
  checkBuildArgs(options);
6549
- const projectDir = path21.resolve(options.dir);
6707
+ const projectDir = path22.resolve(options.dir);
6550
6708
  const result = await runBuildCommand({
6551
6709
  projectDir,
6552
6710
  command: options.command,
@@ -6566,21 +6724,13 @@ async function runBuildCli(args) {
6566
6724
  } catch (err) {
6567
6725
  if (!(err instanceof PackError)) throw err;
6568
6726
  console.error(`\u274C ${err.message}`);
6569
- process.exitCode = 1;
6727
+ process.exitCode = err.code === "USAGE" ? USAGE_EXIT_CODE : 1;
6570
6728
  }
6571
6729
  }
6572
6730
  async function runInitShareCommand(args) {
6573
- let parsed;
6574
- try {
6575
- parsed = parseInitShareArgs(args.slice(1));
6576
- } catch (err) {
6577
- console.error(`\u274C ${err.message}`);
6578
- process.exitCode = 1;
6579
- return;
6580
- }
6581
- const { dir, localesPath, sourceLocale, branches, overwrite } = parsed;
6582
- const projectDir = path21.resolve(dir);
6583
- if (!fs14.existsSync(projectDir)) {
6731
+ const { dir, localesPath, sourceLocale, branches, overwrite } = args;
6732
+ const projectDir = path22.resolve(dir);
6733
+ if (!fs15.existsSync(projectDir)) {
6584
6734
  console.error(`Error: Directory "${dir}" does not exist.`);
6585
6735
  process.exit(1);
6586
6736
  }
@@ -6608,73 +6758,60 @@ async function runInitShareCommand(args) {
6608
6758
  process.exitCode = 1;
6609
6759
  }
6610
6760
  }
6761
+ var COMMANDS = ["migrate", "init", "studio", "doctor", "check", "pack", "build", "apply"];
6611
6762
  async function run2() {
6612
6763
  const args = process.argv.slice(2);
6613
- if (args.includes("--help") || args.includes("-h") || args[0] === "help") {
6614
- printHelp();
6615
- process.exit(0);
6616
- }
6617
- const COMMANDS = ["migrate", "init", "studio", "doctor", "check", "pack", "build", "apply"];
6618
- if (!args[0] || !COMMANDS.includes(args[0])) {
6619
- console.error(`Unknown command: "${args[0]}". Expected one of: ${COMMANDS.join(", ")}.`);
6764
+ const global = args[0] === "help" ? "help" : findGlobalOption(args, commandSpec(args[0]));
6765
+ if (global === "help") {
6620
6766
  printHelp();
6621
- process.exit(1);
6622
- }
6623
- if (args[0] === "doctor") {
6624
- await runDoctorCommand(args);
6625
- return;
6626
- }
6627
- if (args[0] === "check") {
6628
- await runCheckCommand(args);
6629
- return;
6630
- }
6631
- if (args[0] === "pack") {
6632
- await runPackCommand(args);
6633
6767
  return;
6634
6768
  }
6635
- if (args[0] === "build") {
6636
- await runBuildCli(args);
6769
+ if (global === "version") {
6770
+ console.log(readOwnVersion());
6637
6771
  return;
6638
6772
  }
6639
- if (args[0] === "apply") {
6640
- await runApplyCommand(args.slice(1));
6641
- return;
6642
- }
6643
- if (args[0] === "init" && args.includes("--share")) {
6644
- await runInitShareCommand(args);
6645
- return;
6773
+ const [command, ...rest] = args;
6774
+ if (command === void 0) {
6775
+ printHelp();
6776
+ throw new UsageError(
6777
+ "Missing command. Run livelocale <command>, for example livelocale check."
6778
+ );
6646
6779
  }
6647
- if (args[0] === "init") {
6648
- let dir = ".";
6649
- for (let i = 1; i < args.length; i++) {
6650
- const arg = args[i];
6651
- if (arg === "--dir") {
6652
- dir = args[++i] || ".";
6653
- } else if (arg && !arg.startsWith("--")) {
6654
- dir = arg;
6655
- }
6780
+ if (!COMMANDS.includes(command)) {
6781
+ throw new UsageError(`Unknown command "${command}". Expected one of: ${COMMANDS.join(", ")}.`);
6782
+ }
6783
+ switch (command) {
6784
+ case "doctor":
6785
+ return runDoctorCommand(parseDoctorArgs(rest));
6786
+ case "check":
6787
+ return runCheckCommand(rest);
6788
+ case "pack":
6789
+ return runPackCommand(args);
6790
+ case "build":
6791
+ return runBuildCli(args);
6792
+ case "apply":
6793
+ return runApplyCommand(rest);
6794
+ case "init": {
6795
+ const init = parseInitArgs(rest);
6796
+ if (init.share) return runInitShareCommand(init);
6797
+ runInit(init.dir);
6798
+ return;
6656
6799
  }
6657
- runInit(dir);
6658
- process.exit(0);
6659
- }
6660
- if (args[0] === "studio") {
6661
- let port = DEFAULT_STUDIO_PORT;
6662
- let appOrigin = DEFAULT_APP_ORIGIN;
6663
- for (let i = 1; i < args.length; i++) {
6664
- const arg = args[i];
6665
- if (arg === "--port") {
6666
- const val = parseInt(args[++i] || String(DEFAULT_STUDIO_PORT), 10);
6667
- if (!isNaN(val)) port = val;
6668
- } else if (arg === "--app") {
6669
- appOrigin = normalizeOriginArg(args[++i] || "", "--app");
6670
- }
6800
+ case "studio": {
6801
+ const { port, appOrigin } = parseStudioArgs(rest);
6802
+ return runStudio(port, appOrigin);
6671
6803
  }
6672
- await runStudio(port, appOrigin);
6673
- return;
6804
+ default:
6805
+ process.exitCode = await runMigrate(parseMigrateArgs(rest));
6806
+ return;
6674
6807
  }
6675
- await runMigrate(args);
6676
6808
  }
6677
6809
  run2().catch((err) => {
6810
+ if (err instanceof UsageError) {
6811
+ console.error(`\u274C ${err.message}`);
6812
+ process.exitCode = USAGE_EXIT_CODE;
6813
+ return;
6814
+ }
6678
6815
  console.error(`\u274C ${err?.message || err}`);
6679
6816
  process.exit(1);
6680
6817
  });