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.
@@ -318,8 +318,8 @@ var init_transform = __esm({
318
318
  });
319
319
 
320
320
  // src/codemod/index.ts
321
- var fs14 = __toESM(require("fs"), 1);
322
- var path21 = __toESM(require("path"), 1);
321
+ var fs15 = __toESM(require("fs"), 1);
322
+ var path22 = __toESM(require("path"), 1);
323
323
  var net = __toESM(require("net"), 1);
324
324
 
325
325
  // src/server/module-dir.ts
@@ -768,7 +768,7 @@ function isShareManifest(value) {
768
768
  const layout = own2(value, "layout");
769
769
  const locales = own2(value, "locales");
770
770
  const sourceLocale = own2(value, "sourceLocale");
771
- 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);
771
+ 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);
772
772
  }
773
773
 
774
774
  // src/codemod/doctor.ts
@@ -2416,6 +2416,28 @@ function listLocales(localesDir) {
2416
2416
  }
2417
2417
  return Array.from(locales).sort();
2418
2418
  }
2419
+ function hasLocaleFile(localesDir, locale) {
2420
+ const dir = path8.join(localesDir, locale);
2421
+ if (fs6.existsSync(dir) && fs6.statSync(dir).isDirectory()) {
2422
+ return fs6.readdirSync(dir).some((file) => file.endsWith(".json"));
2423
+ }
2424
+ return fs6.existsSync(path8.join(localesDir, `${locale}.json`));
2425
+ }
2426
+ function noLocaleFilesError(localesDir) {
2427
+ const skipped = [];
2428
+ for (const item of fs6.readdirSync(localesDir, { withFileTypes: true })) {
2429
+ if (item.isDirectory() && !item.name.startsWith(".") && !LOCALE_RE.test(item.name)) {
2430
+ skipped.push(`${item.name}/`);
2431
+ } else if (item.isFile() && item.name.endsWith(".json")) {
2432
+ if (!LOCALE_RE.test(path8.basename(item.name, ".json"))) skipped.push(item.name);
2433
+ }
2434
+ }
2435
+ skipped.sort();
2436
+ const hint = skipped.length === 0 ? "" : ` Skipped ${skipped.slice(0, 3).join(", ")}${skipped.length > 3 ? ", ..." : ""}: a locale name looks like en, ar or pt-BR.`;
2437
+ return new Error(
2438
+ `No locale files found in ${localesDir}. Point --locales at the folder with en.json, ar.json, ...${hint}`
2439
+ );
2440
+ }
2419
2441
  function flatten(obj, prefix, file, data) {
2420
2442
  if (!obj || typeof obj !== "object" || Array.isArray(obj)) return;
2421
2443
  for (const [key, value] of Object.entries(obj)) {
@@ -2492,6 +2514,9 @@ async function runCheck(options) {
2492
2514
  throw new Error(`Locales directory not found: ${localesDir}`);
2493
2515
  }
2494
2516
  const locales = listLocales(localesDir);
2517
+ if (!locales.some((locale) => hasLocaleFile(localesDir, locale))) {
2518
+ throw noLocaleFilesError(localesDir);
2519
+ }
2495
2520
  const data = new Map(locales.map((l) => [l, readLocale(localesDir, l, projectDir, issues)]));
2496
2521
  const sourceLocale = options.sourceLocale ?? (locales.includes("en") ? "en" : locales[0] ?? null);
2497
2522
  if (sourceLocale && !data.has(sourceLocale)) {
@@ -2686,44 +2711,181 @@ function formatCheckReport(report, limit = 25) {
2686
2711
  return lines.join("\n");
2687
2712
  }
2688
2713
 
2689
- // src/codemod/check-cli.ts
2690
- function parseCheckArgs(argv) {
2691
- const args = {
2692
- dir: ".",
2693
- unused: true,
2694
- strict: false,
2695
- json: false,
2696
- hardcoded: false,
2697
- allow: []
2698
- };
2714
+ // src/codemod/cli-args.ts
2715
+ var UsageError = class extends Error {
2716
+ constructor(message) {
2717
+ super(message);
2718
+ this.name = "UsageError";
2719
+ }
2720
+ };
2721
+ var USAGE_EXIT_CODE = 2;
2722
+ var GLOBAL_SHORT = /* @__PURE__ */ new Set(["-h", "-v"]);
2723
+ function knownOptions(spec) {
2724
+ return [...spec.values ?? [], ...spec.repeatable ?? [], ...spec.flags ?? []];
2725
+ }
2726
+ function editDistance(a, b) {
2727
+ const d = Array.from(
2728
+ { length: a.length + 1 },
2729
+ (_, i) => Array.from({ length: b.length + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0)
2730
+ );
2731
+ for (let i = 1; i <= a.length; i++) {
2732
+ for (let j = 1; j <= b.length; j++) {
2733
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
2734
+ d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
2735
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
2736
+ d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
2737
+ }
2738
+ }
2739
+ }
2740
+ return d[a.length][b.length];
2741
+ }
2742
+ function suggestOption(arg, known) {
2743
+ const name = arg.replace(/^-+/, "");
2744
+ let best;
2745
+ let bestDistance = Infinity;
2746
+ for (const option of known) {
2747
+ const distance = editDistance(name, option.replace(/^-+/, ""));
2748
+ if (distance < bestDistance) {
2749
+ best = option;
2750
+ bestDistance = distance;
2751
+ }
2752
+ }
2753
+ return bestDistance <= 2 && bestDistance < name.length / 2 ? best : void 0;
2754
+ }
2755
+ function unknownOption(spec, arg) {
2756
+ const known = knownOptions(spec);
2757
+ const head = `Unknown option ${arg} for livelocale ${spec.command}.`;
2758
+ const eq = arg.indexOf("=");
2759
+ if (eq > 0 && known.includes(arg.slice(0, eq))) {
2760
+ return new UsageError(
2761
+ `${head} Put a space before the value: ${arg.slice(0, eq)} ${arg.slice(eq + 1)}`
2762
+ );
2763
+ }
2764
+ const suggestion = suggestOption(arg, known);
2765
+ return new UsageError(
2766
+ suggestion ? `${head} Did you mean ${suggestion}?` : `${head} Run livelocale --help to see its options.`
2767
+ );
2768
+ }
2769
+ function parseCommandArgs(spec, argv) {
2770
+ const parsed = { values: {}, lists: {}, flags: /* @__PURE__ */ new Set(), positionals: [], rest: [] };
2771
+ const values = new Set(spec.values ?? []);
2772
+ const repeatable = new Set(spec.repeatable ?? []);
2773
+ const flags = new Set(spec.flags ?? []);
2774
+ const maxPositionals = spec.positionals ?? 0;
2699
2775
  for (let i = 0; i < argv.length; i++) {
2700
2776
  const arg = argv[i];
2701
- if (arg === "--dir") {
2702
- args.dir = argv[++i] || ".";
2703
- } else if (arg === "--locales") {
2704
- args.localesPath = argv[++i];
2705
- } else if (arg === "--source") {
2706
- args.sourceLocale = argv[++i];
2707
- } else if (arg === "--no-unused") {
2708
- args.unused = false;
2709
- } else if (arg === "--strict") {
2710
- args.strict = true;
2711
- } else if (arg === "--json") {
2712
- args.json = true;
2713
- } else if (arg === "--hardcoded") {
2714
- args.hardcoded = true;
2715
- } else if (arg === "--allow") {
2716
- const word = argv[++i];
2717
- if (!word || word.startsWith("--")) throw new Error("--allow needs a word.");
2718
- args.allow.push(word);
2719
- } else if (arg && !arg.startsWith("--")) {
2720
- args.dir = arg;
2777
+ if (arg === "--") {
2778
+ if (!spec.rest) throw new UsageError(`livelocale ${spec.command} takes nothing after --.`);
2779
+ parsed.rest = argv.slice(i + 1);
2780
+ break;
2781
+ }
2782
+ if (values.has(arg) || repeatable.has(arg)) {
2783
+ const value = argv[i + 1];
2784
+ if (value === void 0 || value.startsWith("--") || GLOBAL_SHORT.has(value)) {
2785
+ throw new UsageError(`${arg} needs a value.`);
2786
+ }
2787
+ i++;
2788
+ if (repeatable.has(arg)) (parsed.lists[arg] ??= []).push(value);
2789
+ else parsed.values[arg] = value;
2790
+ } else if (flags.has(arg)) {
2791
+ parsed.flags.add(arg);
2792
+ } else if (arg.startsWith("-") && arg !== "-") {
2793
+ throw unknownOption(spec, arg);
2794
+ } else if (parsed.positionals.length < maxPositionals) {
2795
+ parsed.positionals.push(arg);
2796
+ } else {
2797
+ throw new UsageError(`Unexpected argument "${arg}" for livelocale ${spec.command}.`);
2721
2798
  }
2722
2799
  }
2800
+ return parsed;
2801
+ }
2802
+ function findGlobalOption(args, spec) {
2803
+ const takesValue = /* @__PURE__ */ new Set([...spec?.values ?? [], ...spec?.repeatable ?? []]);
2804
+ let found;
2805
+ for (let i = 0; i < args.length; i++) {
2806
+ const arg = args[i];
2807
+ if (arg === "--") break;
2808
+ if (takesValue.has(arg)) {
2809
+ if (args[i + 1] !== "--") i++;
2810
+ } else if (arg === "--help" || arg === "-h") return "help";
2811
+ else if (arg === "--version" || arg === "-v") found = "version";
2812
+ }
2813
+ return found;
2814
+ }
2815
+ function splitList(value) {
2816
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
2817
+ }
2818
+ function folderArg(parsed) {
2819
+ const plain = parsed.positionals[0];
2820
+ const flag = parsed.values["--dir"];
2821
+ if (plain !== void 0 && flag !== void 0) {
2822
+ throw new UsageError(`Pass the folder once: "${plain}" or --dir ${flag}, not both.`);
2823
+ }
2824
+ return plain ?? flag ?? ".";
2825
+ }
2826
+
2827
+ // src/codemod/command-specs.ts
2828
+ var COMMAND_SPECS = {
2829
+ migrate: {
2830
+ command: "migrate",
2831
+ values: ["--dir", "--exclude"],
2832
+ flags: ["--dry-run"],
2833
+ positionals: 1
2834
+ },
2835
+ init: {
2836
+ command: "init",
2837
+ values: ["--dir", "--locales", "--source", "--branches"],
2838
+ flags: ["--share", "--overwrite"],
2839
+ positionals: 1
2840
+ },
2841
+ studio: { command: "studio", values: ["--port", "--app"] },
2842
+ doctor: {
2843
+ command: "doctor",
2844
+ values: ["--dir", "--locales", "--app", "--token"],
2845
+ flags: ["--share"],
2846
+ positionals: 1
2847
+ },
2848
+ check: {
2849
+ command: "check",
2850
+ values: ["--dir", "--locales", "--source"],
2851
+ repeatable: ["--allow"],
2852
+ flags: ["--no-unused", "--strict", "--hardcoded", "--json"],
2853
+ positionals: 1
2854
+ },
2855
+ // `pack` and `build` share one set; checkPackArgs and checkBuildArgs reject the other's options
2856
+ // with a message that names the right command.
2857
+ pack: {
2858
+ command: "pack",
2859
+ values: ["--dir", "--locales", "--source", "--base", "--out"],
2860
+ flags: ["--expose-repo", "--no-inject", "--preview"],
2861
+ positionals: Infinity,
2862
+ rest: true
2863
+ },
2864
+ apply: {
2865
+ command: "apply",
2866
+ values: ["--dir", "--locales", "--source", "--branches"],
2867
+ flags: ["--clipboard", "--pr", "--force", "--dry-run", "--github-event"],
2868
+ positionals: Infinity
2869
+ }
2870
+ };
2871
+
2872
+ // src/codemod/check-cli.ts
2873
+ function parseCheckArgs(argv) {
2874
+ const parsed = parseCommandArgs(COMMAND_SPECS.check, argv);
2875
+ const args = {
2876
+ dir: folderArg(parsed),
2877
+ unused: !parsed.flags.has("--no-unused"),
2878
+ strict: parsed.flags.has("--strict"),
2879
+ json: parsed.flags.has("--json"),
2880
+ hardcoded: parsed.flags.has("--hardcoded"),
2881
+ allow: parsed.lists["--allow"] ?? []
2882
+ };
2883
+ if (parsed.values["--locales"] !== void 0) args.localesPath = parsed.values["--locales"];
2884
+ if (parsed.values["--source"] !== void 0) args.sourceLocale = parsed.values["--source"];
2723
2885
  return args;
2724
2886
  }
2725
2887
  function checkExitCode(report) {
2726
- if (report.setupProblems && report.setupProblems.length > 0) return 2;
2888
+ if (report.setupProblems && report.setupProblems.length > 0) return CHECK_COULD_NOT_RUN;
2727
2889
  return hasCheckFailures(report) ? 1 : 0;
2728
2890
  }
2729
2891
  function checkSummaryLines(report) {
@@ -2736,6 +2898,22 @@ function checkSummaryLines(report) {
2736
2898
  warns > 0 ? `\u2705 No problems, ${warns} warning(s) worth a look.` : `\u2705 Translations look good.`
2737
2899
  ];
2738
2900
  }
2901
+ var CHECK_COULD_NOT_RUN = 2;
2902
+ function checkErrorReport(error, localesDir) {
2903
+ return {
2904
+ error,
2905
+ localesDir: localesDir ?? "",
2906
+ sourceLocale: null,
2907
+ locales: [],
2908
+ codeKeyCount: 0,
2909
+ issues: [],
2910
+ setupProblems: [error]
2911
+ };
2912
+ }
2913
+ function withCheckError(report) {
2914
+ const problems = report.setupProblems ?? [];
2915
+ return problems.length > 0 ? { ...report, error: problems.join(" ") } : report;
2916
+ }
2739
2917
 
2740
2918
  // src/server/pack.ts
2741
2919
  var import_child_process2 = require("child_process");
@@ -2744,8 +2922,8 @@ var path11 = __toESM(require("path"), 1);
2744
2922
 
2745
2923
  // src/core/page-path.ts
2746
2924
  var NON_ASCII_ESCAPES = /(?:%[89A-Fa-f][0-9A-Fa-f])+/g;
2747
- function readablePath(path22) {
2748
- return path22.replace(NON_ASCII_ESCAPES, (run3) => {
2925
+ function readablePath(path23) {
2926
+ return path23.replace(NON_ASCII_ESCAPES, (run3) => {
2749
2927
  try {
2750
2928
  return decodeURIComponent(run3);
2751
2929
  } catch {
@@ -2758,16 +2936,16 @@ function readablePath(path22) {
2758
2936
  var import_child_process = require("child_process");
2759
2937
  var import_fs4 = require("fs");
2760
2938
  var path9 = __toESM(require("path"), 1);
2761
- var runGit = (args, cwd) => new Promise((resolve18) => {
2939
+ var runGit = (args, cwd) => new Promise((resolve19) => {
2762
2940
  try {
2763
2941
  (0, import_child_process.execFile)(
2764
2942
  "git",
2765
2943
  args,
2766
2944
  { cwd, windowsHide: true, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" } },
2767
- (error, stdout) => resolve18(error ? null : String(stdout).trim())
2945
+ (error, stdout) => resolve19(error ? null : String(stdout).trim())
2768
2946
  );
2769
2947
  } catch {
2770
- resolve18(null);
2948
+ resolve19(null);
2771
2949
  }
2772
2950
  });
2773
2951
  var SHA_RE = /^[0-9a-f]{40}([0-9a-f]{24})?$/i;
@@ -3068,8 +3246,8 @@ var NO_REPO_WARNING = "exposeRepo is on but no GitHub repository was found; the
3068
3246
  function isPlainObject2(value) {
3069
3247
  return typeof value === "object" && value !== null && !Array.isArray(value);
3070
3248
  }
3071
- function toPosix(relative11) {
3072
- return relative11.split(path11.sep).join("/") || ".";
3249
+ function toPosix(relative12) {
3250
+ return relative12.split(path11.sep).join("/") || ".";
3073
3251
  }
3074
3252
  function isInside(parent, child) {
3075
3253
  const rel = path11.relative(parent, child);
@@ -3325,52 +3503,27 @@ async function packOutput(options) {
3325
3503
  warnings
3326
3504
  };
3327
3505
  }
3328
- var VALUE_OPTIONS = {
3329
- "--dir": "dir",
3330
- "--locales": "locales",
3331
- "--source": "source",
3332
- "--base": "base",
3333
- "--out": "out"
3334
- };
3335
- function parseShareArgs(argv) {
3336
- const args = {
3337
- positional: [],
3338
- dir: ".",
3339
- locales: void 0,
3340
- source: void 0,
3341
- base: void 0,
3342
- out: void 0,
3343
- exposeRepo: false,
3344
- inject: true,
3345
- preview: false,
3346
- command: []
3347
- };
3348
- for (let i = 0; i < argv.length; i++) {
3349
- const arg = argv[i];
3350
- if (arg === "--") {
3351
- args.command = argv.slice(i + 1);
3352
- break;
3353
- }
3354
- if (Object.prototype.hasOwnProperty.call(VALUE_OPTIONS, arg)) {
3355
- const value = argv[i + 1];
3356
- if (value === void 0 || value.startsWith("--")) {
3357
- throw new PackError("USAGE", `${arg} needs a value.`);
3358
- }
3359
- args[VALUE_OPTIONS[arg]] = value;
3360
- i++;
3361
- } else if (arg === "--expose-repo") {
3362
- args.exposeRepo = true;
3363
- } else if (arg === "--no-inject") {
3364
- args.inject = false;
3365
- } else if (arg === "--preview") {
3366
- args.preview = true;
3367
- } else if (arg.startsWith("-") && arg !== "-") {
3368
- throw new PackError("USAGE", `Unknown option "${arg}".`);
3369
- } else {
3370
- args.positional.push(arg);
3371
- }
3506
+ function parseShareArgs(argv, command = "pack") {
3507
+ let parsed;
3508
+ try {
3509
+ parsed = parseCommandArgs({ ...COMMAND_SPECS.pack, command }, argv);
3510
+ } catch (err) {
3511
+ if (err instanceof UsageError) throw new PackError("USAGE", err.message);
3512
+ throw err;
3372
3513
  }
3373
- return args;
3514
+ const { values, flags } = parsed;
3515
+ return {
3516
+ positional: parsed.positionals,
3517
+ dir: values["--dir"] ?? ".",
3518
+ locales: values["--locales"],
3519
+ source: values["--source"],
3520
+ base: values["--base"],
3521
+ out: values["--out"],
3522
+ exposeRepo: flags.has("--expose-repo"),
3523
+ inject: !flags.has("--no-inject"),
3524
+ preview: flags.has("--preview"),
3525
+ command: parsed.rest
3526
+ };
3374
3527
  }
3375
3528
  var PACK_USAGE = "Usage: livelocale pack <outDir> [options]";
3376
3529
  var BUILD_USAGE = "Usage: livelocale build [--preview] -- <your build command>";
@@ -3440,7 +3593,7 @@ function quoteWindowsArg(arg) {
3440
3593
  }
3441
3594
  return out + "\\".repeat(backslashes * 2) + '"';
3442
3595
  }
3443
- var spawnInherit = (command, args, { cwd, env }) => new Promise((resolve18, reject2) => {
3596
+ var spawnInherit = (command, args, { cwd, env }) => new Promise((resolve19, reject2) => {
3444
3597
  const child = process.platform === "win32" ? (0, import_child_process2.spawn)([command, ...args].map(quoteWindowsArg).join(" "), {
3445
3598
  cwd,
3446
3599
  env,
@@ -3448,10 +3601,10 @@ var spawnInherit = (command, args, { cwd, env }) => new Promise((resolve18, reje
3448
3601
  shell: true
3449
3602
  }) : (0, import_child_process2.spawn)(command, args, { cwd, env, stdio: "inherit" });
3450
3603
  child.on("error", (error) => {
3451
- if (error.code === "ENOENT") resolve18(127);
3604
+ if (error.code === "ENOENT") resolve19(127);
3452
3605
  else reject2(error);
3453
3606
  });
3454
- child.on("close", (code) => resolve18(code ?? 1));
3607
+ child.on("close", (code) => resolve19(code ?? 1));
3455
3608
  });
3456
3609
  var NEXT_PREVIEW_WARNING = "Next.js pages have no watermarks without --preview; the Studio will open but nothing is editable.";
3457
3610
  var NEXT_GITIGNORE_WARNING = "Add public/livelocale/ to .gitignore; it is generated on every build.";
@@ -3900,8 +4053,8 @@ function createGitHubRest(config) {
3900
4053
  "X-GitHub-Api-Version": "2022-11-28",
3901
4054
  ...hasBody ? { "Content-Type": "application/json" } : {}
3902
4055
  });
3903
- async function api(method, path22, options = {}) {
3904
- const url = new URL(`${apiBase}/repos/${repo}${path22}`);
4056
+ async function api(method, path23, options = {}) {
4057
+ const url = new URL(`${apiBase}/repos/${repo}${path23}`);
3905
4058
  for (const [k, v] of Object.entries(options.query ?? {})) url.searchParams.set(k, v);
3906
4059
  const response = await fetchImpl(url.toString(), {
3907
4060
  method,
@@ -3918,14 +4071,14 @@ function createGitHubRest(config) {
3918
4071
  const detail = data?.message || "request failed";
3919
4072
  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.' : "";
3920
4073
  throw new GitHubApiError(
3921
- `GitHub API ${response.status} on ${method} ${path22}: ${detail}.${hint}`,
4074
+ `GitHub API ${response.status} on ${method} ${path23}: ${detail}.${hint}`,
3922
4075
  response.status
3923
4076
  );
3924
4077
  }
3925
4078
  return { status: response.status, data };
3926
4079
  }
3927
- async function probeStatus(method, path22, body) {
3928
- const response = await fetchImpl(`${apiBase}/repos/${repo}${path22}`, {
4080
+ async function probeStatus(method, path23, body) {
4081
+ const response = await fetchImpl(`${apiBase}/repos/${repo}${path23}`, {
3929
4082
  method,
3930
4083
  headers: headers(body !== void 0),
3931
4084
  body: body !== void 0 ? JSON.stringify(body) : void 0
@@ -4787,7 +4940,7 @@ function clipboardCommands(platform) {
4787
4940
  }
4788
4941
  var TOO_LARGE = /* @__PURE__ */ Symbol("too large");
4789
4942
  function run(execFileImpl, [command, args]) {
4790
- return new Promise((resolve18) => {
4943
+ return new Promise((resolve19) => {
4791
4944
  try {
4792
4945
  execFileImpl(
4793
4946
  command,
@@ -4795,12 +4948,12 @@ function run(execFileImpl, [command, args]) {
4795
4948
  { encoding: "utf8", windowsHide: true, maxBuffer: MAX_APPLY_INPUT_BYTES + 1 },
4796
4949
  (error, stdout) => {
4797
4950
  const code = error?.code;
4798
- if (code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") resolve18(TOO_LARGE);
4799
- else resolve18(error ? null : String(stdout));
4951
+ if (code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") resolve19(TOO_LARGE);
4952
+ else resolve19(error ? null : String(stdout));
4800
4953
  }
4801
4954
  );
4802
4955
  } catch {
4803
- resolve18(null);
4956
+ resolve19(null);
4804
4957
  }
4805
4958
  });
4806
4959
  }
@@ -5395,30 +5548,30 @@ var GITHUB_REMOTE_RE = /github\.com[:/]([\w.-]+\/[\w.-]+?)(?:\.git)?$/;
5395
5548
  var defaultGit = (args, cwd, opts) => {
5396
5549
  const env = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
5397
5550
  if (opts?.inherit) {
5398
- return new Promise((resolve18, reject2) => {
5551
+ return new Promise((resolve19, reject2) => {
5399
5552
  const child = (0, import_child_process5.spawn)("git", args, { cwd, env, stdio: "inherit", windowsHide: true });
5400
5553
  child.on("error", reject2);
5401
5554
  child.on(
5402
5555
  "close",
5403
- (code) => code === 0 ? resolve18("") : reject2(new Error(`git ${args[0]} failed (exit ${code}).`))
5556
+ (code) => code === 0 ? resolve19("") : reject2(new Error(`git ${args[0]} failed (exit ${code}).`))
5404
5557
  );
5405
5558
  });
5406
5559
  }
5407
- return new Promise((resolve18, reject2) => {
5560
+ return new Promise((resolve19, reject2) => {
5408
5561
  (0, import_child_process5.execFile)(
5409
5562
  "git",
5410
5563
  args,
5411
5564
  { cwd, env, windowsHide: true, maxBuffer: 16 * 1024 * 1024 },
5412
- (error, stdout) => error ? reject2(error) : resolve18(String(stdout))
5565
+ (error, stdout) => error ? reject2(error) : resolve19(String(stdout))
5413
5566
  );
5414
5567
  });
5415
5568
  };
5416
- var defaultGh = (args, cwd) => new Promise((resolve18, reject2) => {
5569
+ var defaultGh = (args, cwd) => new Promise((resolve19, reject2) => {
5417
5570
  (0, import_child_process5.execFile)(
5418
5571
  "gh",
5419
5572
  args,
5420
5573
  { cwd, windowsHide: true },
5421
- (error, stdout, stderr) => error ? reject2(Object.assign(error, { stderr: String(stderr) })) : resolve18(String(stdout))
5574
+ (error, stdout, stderr) => error ? reject2(Object.assign(error, { stderr: String(stderr) })) : resolve19(String(stdout))
5422
5575
  );
5423
5576
  });
5424
5577
  function utcStamp(date) {
@@ -5619,70 +5772,42 @@ async function runLocalApply(options) {
5619
5772
  }
5620
5773
 
5621
5774
  // src/codemod/apply/cli.ts
5622
- var VALUE_OPTIONS2 = { "--dir": "dir", "--locales": "locales", "--source": "source" };
5623
- var FLAG_OPTIONS = {
5624
- "--clipboard": "clipboard",
5625
- "--pr": "pr",
5626
- "--force": "force",
5627
- "--dry-run": "dryRun",
5628
- "--github-event": "githubEvent"
5629
- };
5630
5775
  function parseApplyArgs(argv) {
5776
+ const parsed = parseCommandArgs(COMMAND_SPECS.apply, argv);
5777
+ if (parsed.positionals.length > 1) throw new UsageError('Pass one change link, file or "-".');
5778
+ const branches = parsed.values["--branches"];
5631
5779
  const args = {
5632
- input: void 0,
5633
- clipboard: false,
5634
- pr: false,
5635
- force: false,
5636
- dryRun: false,
5637
- dir: ".",
5638
- locales: void 0,
5639
- source: void 0,
5640
- githubEvent: false,
5641
- branches: void 0
5780
+ input: parsed.positionals[0],
5781
+ clipboard: parsed.flags.has("--clipboard"),
5782
+ pr: parsed.flags.has("--pr"),
5783
+ force: parsed.flags.has("--force"),
5784
+ dryRun: parsed.flags.has("--dry-run"),
5785
+ dir: parsed.values["--dir"] ?? ".",
5786
+ locales: parsed.values["--locales"],
5787
+ source: parsed.values["--source"],
5788
+ githubEvent: parsed.flags.has("--github-event"),
5789
+ branches: branches === void 0 ? void 0 : splitList(branches)
5642
5790
  };
5643
- for (let i = 0; i < argv.length; i++) {
5644
- const arg = argv[i];
5645
- if (arg === "--branches") {
5646
- const value = argv[i + 1];
5647
- if (value === void 0 || value.startsWith("--")) {
5648
- throw new ApplyInputError(`${arg} needs a value.`);
5649
- }
5650
- args.branches = value.split(",").map((branch) => branch.trim()).filter(Boolean);
5651
- i++;
5652
- } else if (Object.prototype.hasOwnProperty.call(VALUE_OPTIONS2, arg)) {
5653
- const value = argv[i + 1];
5654
- if (value === void 0 || value.startsWith("--")) {
5655
- throw new ApplyInputError(`${arg} needs a value.`);
5656
- }
5657
- args[VALUE_OPTIONS2[arg]] = value;
5658
- i++;
5659
- } else if (Object.prototype.hasOwnProperty.call(FLAG_OPTIONS, arg)) {
5660
- args[FLAG_OPTIONS[arg]] = true;
5661
- } else if (arg.startsWith("-") && arg !== "-") {
5662
- throw new ApplyInputError(`Unknown option "${arg}".`);
5663
- } else if (args.input !== void 0) {
5664
- throw new ApplyInputError('Pass one change link, file or "-".');
5665
- } else {
5666
- args.input = arg;
5667
- }
5668
- }
5669
5791
  if (args.clipboard && args.input !== void 0) {
5670
- throw new ApplyInputError("Pass the link or --clipboard, not both.");
5792
+ throw new UsageError("Pass the link or --clipboard, not both.");
5793
+ }
5794
+ if (args.branches !== void 0 && !args.githubEvent) {
5795
+ throw new UsageError("--branches only works with --github-event.");
5671
5796
  }
5672
5797
  if (args.githubEvent) {
5673
5798
  if (args.force) {
5674
- throw new ApplyInputError(
5799
+ throw new UsageError(
5675
5800
  "--force is not allowed with --github-event; a maintainer comments `/livelocale apply --force`."
5676
5801
  );
5677
5802
  }
5678
5803
  if (args.input !== void 0) {
5679
- throw new ApplyInputError(
5680
- "--github-event reads the link from the event file; do not pass one."
5681
- );
5804
+ throw new UsageError("--github-event reads the link from the event file; do not pass one.");
5682
5805
  }
5683
5806
  if (args.locales === void 0 || args.source === void 0) {
5684
- throw new ApplyInputError("--github-event needs --locales and --source.");
5807
+ throw new UsageError("--github-event needs --locales and --source.");
5685
5808
  }
5809
+ } else if (!args.clipboard && (args.input === void 0 || args.input === "")) {
5810
+ throw new UsageError(NO_INPUT_MESSAGE);
5686
5811
  }
5687
5812
  return args;
5688
5813
  }
@@ -5716,7 +5841,7 @@ async function runApplyCommand(argv) {
5716
5841
  process.exitCode = result.exitCode;
5717
5842
  } catch (err) {
5718
5843
  console.error(`\u274C ${err instanceof Error ? err.message : String(err)}`);
5719
- process.exitCode = 1;
5844
+ process.exitCode = err instanceof UsageError ? USAGE_EXIT_CODE : 1;
5720
5845
  }
5721
5846
  }
5722
5847
 
@@ -5821,37 +5946,6 @@ function renderWorkflow(o) {
5821
5946
  ""
5822
5947
  ].join("\n");
5823
5948
  }
5824
- function parseInitShareArgs(argv) {
5825
- const args = {
5826
- dir: ".",
5827
- localesPath: void 0,
5828
- sourceLocale: void 0,
5829
- branches: void 0,
5830
- overwrite: false
5831
- };
5832
- const valueAt = (i) => {
5833
- const value = argv[i + 1];
5834
- if (value === void 0 || value.startsWith("--")) {
5835
- throw new Error(`${argv[i]} needs a value.`);
5836
- }
5837
- return value;
5838
- };
5839
- for (let i = 0; i < argv.length; i++) {
5840
- const arg = argv[i];
5841
- if (arg === "--dir") {
5842
- args.dir = valueAt(i++);
5843
- } else if (arg === "--locales") {
5844
- args.localesPath = valueAt(i++);
5845
- } else if (arg === "--source") {
5846
- args.sourceLocale = valueAt(i++);
5847
- } else if (arg === "--branches") {
5848
- args.branches = valueAt(i++).split(",").map((b) => b.trim()).filter(Boolean);
5849
- } else if (arg === "--overwrite") {
5850
- args.overwrite = true;
5851
- }
5852
- }
5853
- return args;
5854
- }
5855
5949
  function toPosix2(p) {
5856
5950
  return p.split(path20.sep).join("/");
5857
5951
  }
@@ -6081,11 +6175,96 @@ function writePlannedFiles(projectDir, files) {
6081
6175
  }
6082
6176
  }
6083
6177
 
6084
- // src/codemod/index.ts
6178
+ // src/codemod/command-args.ts
6179
+ function commandSpec(command) {
6180
+ const name = command === "build" ? "pack" : command;
6181
+ return name !== void 0 && Object.prototype.hasOwnProperty.call(COMMAND_SPECS, name) ? COMMAND_SPECS[name] : void 0;
6182
+ }
6085
6183
  var DEFAULT_STUDIO_PORT = 3010;
6086
6184
  var DEFAULT_APP_ORIGIN = "http://localhost:3000";
6087
- function printHelp() {
6088
- console.log(`
6185
+ var DEFAULT_EXCLUDES = ["node_modules", ".next", "dist", ".git"];
6186
+ function parseMigrateArgs(argv) {
6187
+ const parsed = parseCommandArgs(COMMAND_SPECS.migrate, argv);
6188
+ const exclude = parsed.values["--exclude"];
6189
+ return {
6190
+ dir: folderArg(parsed),
6191
+ excludes: exclude === void 0 ? [...DEFAULT_EXCLUDES] : splitList(exclude),
6192
+ dryRun: parsed.flags.has("--dry-run")
6193
+ };
6194
+ }
6195
+ function parseDoctorArgs(argv) {
6196
+ const parsed = parseCommandArgs(COMMAND_SPECS.doctor, argv);
6197
+ const app = parsed.values["--app"];
6198
+ const args = { dir: folderArg(parsed), share: parsed.flags.has("--share") };
6199
+ if (parsed.values["--locales"] !== void 0) args.localesPath = parsed.values["--locales"];
6200
+ if (parsed.values["--token"] !== void 0) args.token = parsed.values["--token"];
6201
+ if (app !== void 0) {
6202
+ try {
6203
+ args.app = normalizeAppUrl(app);
6204
+ } catch {
6205
+ throw new UsageError(
6206
+ `--app must be an http(s) URL such as http://localhost:3000 or https://user.github.io/repo/ (got "${app}").`
6207
+ );
6208
+ }
6209
+ }
6210
+ return args;
6211
+ }
6212
+ function parseStudioArgs(argv) {
6213
+ const parsed = parseCommandArgs(COMMAND_SPECS.studio, argv);
6214
+ const port = parsed.values["--port"];
6215
+ const app = parsed.values["--app"];
6216
+ const args = { port: DEFAULT_STUDIO_PORT, appOrigin: DEFAULT_APP_ORIGIN };
6217
+ if (port !== void 0) {
6218
+ const value = Number(port);
6219
+ if (!/^\d+$/.test(port) || value < 1 || value > 65535) {
6220
+ throw new UsageError(`--port must be a port number from 1 to 65535 (got "${port}").`);
6221
+ }
6222
+ args.port = value;
6223
+ }
6224
+ if (app !== void 0) {
6225
+ try {
6226
+ const url = new URL(app);
6227
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error("bad protocol");
6228
+ args.appOrigin = url.origin;
6229
+ } catch {
6230
+ throw new UsageError(
6231
+ `--app must be an http(s) origin such as http://localhost:3001 (got "${app}").`
6232
+ );
6233
+ }
6234
+ }
6235
+ return args;
6236
+ }
6237
+ var SHARE_ONLY = ["--locales", "--source", "--branches"];
6238
+ function parseInitArgs(argv) {
6239
+ const parsed = parseCommandArgs(COMMAND_SPECS.init, argv);
6240
+ if (!parsed.flags.has("--share")) {
6241
+ for (const flag of [...SHARE_ONLY, "--overwrite"]) {
6242
+ if (parsed.values[flag] !== void 0 || parsed.flags.has(flag)) {
6243
+ throw new UsageError(`${flag} only works with init --share.`);
6244
+ }
6245
+ }
6246
+ return { share: false, dir: folderArg(parsed) };
6247
+ }
6248
+ const folder = parsed.positionals[0];
6249
+ if (folder !== void 0) {
6250
+ throw new UsageError(
6251
+ `init --share takes the project folder as --dir ${folder}, not as a plain argument.`
6252
+ );
6253
+ }
6254
+ const branches = parsed.values["--branches"];
6255
+ return {
6256
+ share: true,
6257
+ dir: parsed.values["--dir"] ?? ".",
6258
+ localesPath: parsed.values["--locales"],
6259
+ sourceLocale: parsed.values["--source"],
6260
+ branches: branches === void 0 ? void 0 : splitList(branches),
6261
+ overwrite: parsed.flags.has("--overwrite")
6262
+ };
6263
+ }
6264
+
6265
+ // src/codemod/help.ts
6266
+ function helpText() {
6267
+ return `
6089
6268
  LiveLocale (livelocale) CLI
6090
6269
 
6091
6270
  Usage:
@@ -6162,41 +6341,129 @@ Options for apply:
6162
6341
  --source <locale> Source locale (default: en, else the first locale)
6163
6342
  --force Apply edits the translator saved with "Save anyway"
6164
6343
  --dry-run Show what would change without writing files
6165
- --github-event Run inside the generated GitHub workflow; reads the link from the event file
6166
- --branches <a,b> Branches a link may target (default: the repository's default branch)
6344
+ --github-event Run inside the generated GitHub workflow; reads the link from the event file
6345
+ --branches <a,b> Branches a link may target (default: the repository's default branch)
6167
6346
 
6168
6347
  Global Options:
6169
6348
  -h, --help Show help information
6170
- `);
6349
+ -v, --version Print the livelocale version
6350
+
6351
+ Exit codes:
6352
+ 0 Success.
6353
+ 1 The command ran and found problems, or could not finish: check found a problem, doctor
6354
+ found a blocking problem, migrate could not convert a file, apply or pack could not finish.
6355
+ 2 Usage error: an unknown or missing command, an unknown option, a missing value, an argument
6356
+ the command does not take, or options that do not go together. check also exits 2 when it
6357
+ could not run (missing folder, no locales folder or no locale files in it, unknown --source,
6358
+ TypeScript missing for --hardcoded).
6359
+ build exits with your build command's own code once it has started it.
6360
+ `;
6171
6361
  }
6172
- function normalizeOriginArg(value, flag) {
6362
+
6363
+ // src/codemod/migrate.ts
6364
+ var fs14 = __toESM(require("fs"), 1);
6365
+ var path21 = __toESM(require("path"), 1);
6366
+ var SOURCE_EXTENSIONS2 = [".js", ".jsx", ".ts", ".tsx"];
6367
+ async function loadTransform() {
6368
+ const transform = await Promise.resolve().then(() => (init_transform(), transform_exports));
6369
+ await transform.loadTypeScript();
6370
+ return transform.transformReactI18next;
6371
+ }
6372
+ async function runMigrate({ dir, excludes, dryRun }, transform) {
6373
+ const absoluteDir = path21.resolve(dir);
6374
+ if (!fs14.existsSync(absoluteDir)) {
6375
+ console.error(`Error: Directory "${dir}" does not exist.`);
6376
+ return 1;
6377
+ }
6378
+ let convert;
6173
6379
  try {
6174
- const url = new URL(value);
6175
- if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error("bad protocol");
6176
- return url.origin;
6177
- } catch {
6178
- console.error(
6179
- `Error: ${flag} must be an http(s) origin such as http://localhost:3001 (got "${value}").`
6180
- );
6181
- process.exit(1);
6380
+ convert = transform ?? await loadTransform();
6381
+ } catch (err) {
6382
+ console.error(`\u274C ${err.message}`);
6383
+ return 1;
6182
6384
  }
6183
- }
6184
- function normalizeAppArg(value) {
6385
+ console.log(`
6386
+ \u{1F680} Starting migration scanner...`);
6387
+ console.log(`\u{1F4C1} Target: ${absoluteDir}`);
6388
+ console.log(`\u{1F6AB} Excluded: ${excludes.join(", ")}`);
6389
+ if (dryRun) {
6390
+ console.log(`\u{1F9EA} Mode: DRY RUN (no files will be modified)`);
6391
+ }
6392
+ console.log("--------------------------------------------------");
6393
+ let scannedCount = 0;
6394
+ let migratedCount = 0;
6395
+ const failed = [];
6396
+ async function walk(currentDir) {
6397
+ const files = fs14.readdirSync(currentDir, { withFileTypes: true });
6398
+ for (const file of files) {
6399
+ const fullPath = path21.join(currentDir, file.name);
6400
+ if (file.isDirectory()) {
6401
+ if (excludes.includes(file.name)) {
6402
+ continue;
6403
+ }
6404
+ await walk(fullPath);
6405
+ } else if (file.isFile() && SOURCE_EXTENSIONS2.includes(path21.extname(file.name))) {
6406
+ scannedCount++;
6407
+ const relativePath = path21.relative(absoluteDir, fullPath);
6408
+ try {
6409
+ const content = fs14.readFileSync(fullPath, "utf8");
6410
+ const result = await convert(content, fullPath);
6411
+ if (result.modified) {
6412
+ if (dryRun) {
6413
+ console.log(`[DRY RUN] Would migrate: ${relativePath}`);
6414
+ } else {
6415
+ fs14.writeFileSync(fullPath, result.code, "utf8");
6416
+ console.log(`\u2705 Migrated: ${relativePath}`);
6417
+ }
6418
+ migratedCount++;
6419
+ }
6420
+ } catch (err) {
6421
+ failed.push(relativePath);
6422
+ console.error(`\u274C Error processing file ${fullPath}:`, err.message);
6423
+ }
6424
+ }
6425
+ }
6426
+ }
6427
+ let walkError;
6185
6428
  try {
6186
- return normalizeAppUrl(value);
6187
- } catch {
6188
- console.error(
6189
- `Error: --app must be an http(s) URL such as http://localhost:3000 or https://user.github.io/repo/ (got "${value}").`
6190
- );
6191
- process.exit(1);
6429
+ await walk(absoluteDir);
6430
+ } catch (err) {
6431
+ walkError = err.message;
6432
+ console.error(`\u274C Walk error:`, err.message);
6192
6433
  }
6434
+ console.log("--------------------------------------------------");
6435
+ console.log(`\u{1F4CA} Summary:`);
6436
+ console.log(` Scanned: ${scannedCount} files`);
6437
+ if (dryRun) {
6438
+ console.log(` Would migrate: ${migratedCount} files`);
6439
+ } else {
6440
+ console.log(` Migrated: ${migratedCount} files`);
6441
+ }
6442
+ if (failed.length > 0 || walkError !== void 0) {
6443
+ if (failed.length > 0) {
6444
+ console.error(` Failed: ${failed.length} file(s)`);
6445
+ for (const file of failed) console.error(` ${file}`);
6446
+ }
6447
+ if (walkError !== void 0) console.error(` The scan stopped early: ${walkError}`);
6448
+ console.error(`
6449
+ \u274C Not every file was converted. Fix the files above and run migrate again.`);
6450
+ return 1;
6451
+ }
6452
+ console.log(`
6453
+ \u{1F389} Done!`);
6454
+ return 0;
6455
+ }
6456
+
6457
+ // src/codemod/index.ts
6458
+ function printHelp() {
6459
+ console.log(helpText());
6193
6460
  }
6194
6461
  function isPortAnswering(port) {
6195
- return new Promise((resolve18) => {
6462
+ return new Promise((resolve19) => {
6196
6463
  const socket = net.connect({ host: "localhost", port });
6197
6464
  const done = (answering) => {
6198
6465
  socket.destroy();
6199
- resolve18(answering);
6466
+ resolve19(answering);
6200
6467
  };
6201
6468
  socket.setTimeout(1e3, () => done(false));
6202
6469
  socket.once("connect", () => done(true));
@@ -6206,8 +6473,8 @@ function isPortAnswering(port) {
6206
6473
  async function runStudio(port, appOrigin) {
6207
6474
  console.log(`
6208
6475
  \u{1F680} Starting LiveLocale Studio...`);
6209
- const studioDir = path21.resolve(moduleDir(), "../studio");
6210
- if (!fs14.existsSync(path21.join(studioDir, "index.html"))) {
6476
+ const studioDir = path22.resolve(moduleDir(), "../studio");
6477
+ if (!fs15.existsSync(path22.join(studioDir, "index.html"))) {
6211
6478
  console.error(`Error: Studio assets not found at "${studioDir}".`);
6212
6479
  console.error(`Did you build the project? Run "npm run build" first.`);
6213
6480
  process.exit(1);
@@ -6239,48 +6506,48 @@ async function runStudio(port, appOrigin) {
6239
6506
  function runInit(projectDir) {
6240
6507
  console.log(`
6241
6508
  \u{1F680} Initializing LiveLocale API handler...`);
6242
- const absoluteDir = path21.resolve(projectDir);
6243
- if (!fs14.existsSync(absoluteDir)) {
6509
+ const absoluteDir = path22.resolve(projectDir);
6510
+ if (!fs15.existsSync(absoluteDir)) {
6244
6511
  console.error(`Error: Directory "${projectDir}" does not exist.`);
6245
6512
  process.exit(1);
6246
6513
  }
6247
- const isTypeScript = fs14.existsSync(path21.join(absoluteDir, "tsconfig.json"));
6514
+ const isTypeScript = fs15.existsSync(path22.join(absoluteDir, "tsconfig.json"));
6248
6515
  const fileExt = isTypeScript ? "ts" : "js";
6249
6516
  console.log(`\u{1F4C1} Target: ${absoluteDir}`);
6250
6517
  console.log(`\u{1F527} Language detected: ${isTypeScript ? "TypeScript" : "JavaScript"}`);
6251
6518
  let hasNextIntl = false;
6252
6519
  let hasReactI18next = false;
6253
- const targetPkgPath = path21.join(absoluteDir, "package.json");
6254
- if (fs14.existsSync(targetPkgPath)) {
6520
+ const targetPkgPath = path22.join(absoluteDir, "package.json");
6521
+ if (fs15.existsSync(targetPkgPath)) {
6255
6522
  try {
6256
- const pkg = JSON.parse(fs14.readFileSync(targetPkgPath, "utf8"));
6523
+ const pkg = JSON.parse(fs15.readFileSync(targetPkgPath, "utf8"));
6257
6524
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
6258
6525
  hasNextIntl = Boolean(allDeps["next-intl"]);
6259
6526
  hasReactI18next = Boolean(allDeps["react-i18next"]);
6260
6527
  } catch {
6261
6528
  }
6262
6529
  }
6263
- const appDirPath = path21.join(absoluteDir, "app");
6264
- const pagesDirPath = path21.join(absoluteDir, "pages");
6265
- const srcAppDirPath = path21.join(absoluteDir, "src", "app");
6266
- const srcPagesDirPath = path21.join(absoluteDir, "src", "pages");
6530
+ const appDirPath = path22.join(absoluteDir, "app");
6531
+ const pagesDirPath = path22.join(absoluteDir, "pages");
6532
+ const srcAppDirPath = path22.join(absoluteDir, "src", "app");
6533
+ const srcPagesDirPath = path22.join(absoluteDir, "src", "pages");
6267
6534
  let targetPath = "";
6268
6535
  let routerType = "";
6269
6536
  let content = "";
6270
- if (fs14.existsSync(appDirPath)) {
6271
- targetPath = path21.join(appDirPath, "api", "livelocale", `route.${fileExt}`);
6537
+ if (fs15.existsSync(appDirPath)) {
6538
+ targetPath = path22.join(appDirPath, "api", "livelocale", `route.${fileExt}`);
6272
6539
  routerType = "Next.js App Router (root)";
6273
- } else if (fs14.existsSync(srcAppDirPath)) {
6274
- targetPath = path21.join(srcAppDirPath, "api", "livelocale", `route.${fileExt}`);
6540
+ } else if (fs15.existsSync(srcAppDirPath)) {
6541
+ targetPath = path22.join(srcAppDirPath, "api", "livelocale", `route.${fileExt}`);
6275
6542
  routerType = "Next.js App Router (src/)";
6276
- } else if (fs14.existsSync(pagesDirPath)) {
6277
- targetPath = path21.join(pagesDirPath, "api", `livelocale.${fileExt}`);
6543
+ } else if (fs15.existsSync(pagesDirPath)) {
6544
+ targetPath = path22.join(pagesDirPath, "api", `livelocale.${fileExt}`);
6278
6545
  routerType = "Next.js Pages Router (root)";
6279
- } else if (fs14.existsSync(srcPagesDirPath)) {
6280
- targetPath = path21.join(srcPagesDirPath, "api", `livelocale.${fileExt}`);
6546
+ } else if (fs15.existsSync(srcPagesDirPath)) {
6547
+ targetPath = path22.join(srcPagesDirPath, "api", `livelocale.${fileExt}`);
6281
6548
  routerType = "Next.js Pages Router (src/)";
6282
6549
  } else {
6283
- targetPath = path21.join(absoluteDir, "app", "api", "livelocale", `route.${fileExt}`);
6550
+ targetPath = path22.join(absoluteDir, "app", "api", "livelocale", `route.${fileExt}`);
6284
6551
  routerType = "Default App Router (app/ not found)";
6285
6552
  }
6286
6553
  console.log(`\u{1F4CD} Project structure: ${routerType}`);
@@ -6308,14 +6575,14 @@ const handler = createLiveLocaleHandler({
6308
6575
  export default handler;
6309
6576
  `;
6310
6577
  }
6311
- const targetDir = path21.dirname(targetPath);
6312
- if (!fs14.existsSync(targetDir)) {
6313
- fs14.mkdirSync(targetDir, { recursive: true });
6578
+ const targetDir = path22.dirname(targetPath);
6579
+ if (!fs15.existsSync(targetDir)) {
6580
+ fs15.mkdirSync(targetDir, { recursive: true });
6314
6581
  }
6315
6582
  try {
6316
- fs14.writeFileSync(targetPath, content, "utf8");
6583
+ fs15.writeFileSync(targetPath, content, "utf8");
6317
6584
  console.log(`\u2705 Created API Route Handler successfully at:`);
6318
- console.log(` ${path21.relative(absoluteDir, targetPath)}`);
6585
+ console.log(` ${path22.relative(absoluteDir, targetPath)}`);
6319
6586
  if (hasNextIntl) {
6320
6587
  console.log(`
6321
6588
  \u{1F4A1} Detected 'next-intl' in your project!`);
@@ -6336,124 +6603,9 @@ export default handler;
6336
6603
  process.exit(1);
6337
6604
  }
6338
6605
  }
6339
- async function runMigrate(args) {
6340
- let dir = ".";
6341
- let excludes = ["node_modules", ".next", "dist", ".git"];
6342
- let dryRun = false;
6343
- for (let i = 1; i < args.length; i++) {
6344
- const arg = args[i];
6345
- if (arg === "--dir") {
6346
- dir = args[++i] || ".";
6347
- } else if (arg === "--exclude") {
6348
- const val = args[++i];
6349
- if (val) {
6350
- excludes = val.split(",").map((s) => s.trim());
6351
- }
6352
- } else if (arg === "--dry-run") {
6353
- dryRun = true;
6354
- } else {
6355
- if (arg && !arg.startsWith("--")) {
6356
- dir = arg;
6357
- }
6358
- }
6359
- }
6360
- const absoluteDir = path21.resolve(dir);
6361
- if (!fs14.existsSync(absoluteDir)) {
6362
- console.error(`Error: Directory "${dir}" does not exist.`);
6363
- process.exit(1);
6364
- }
6365
- let transformReactI18next2;
6366
- try {
6367
- const transform = await Promise.resolve().then(() => (init_transform(), transform_exports));
6368
- await transform.loadTypeScript();
6369
- transformReactI18next2 = transform.transformReactI18next;
6370
- } catch (err) {
6371
- console.error(`\u274C ${err.message}`);
6372
- process.exit(1);
6373
- }
6374
- console.log(`
6375
- \u{1F680} Starting migration scanner...`);
6376
- console.log(`\u{1F4C1} Target: ${absoluteDir}`);
6377
- console.log(`\u{1F6AB} Excluded: ${excludes.join(", ")}`);
6378
- if (dryRun) {
6379
- console.log(`\u{1F9EA} Mode: DRY RUN (no files will be modified)`);
6380
- }
6381
- console.log("--------------------------------------------------");
6382
- let scannedCount = 0;
6383
- let migratedCount = 0;
6384
- async function walk(currentDir) {
6385
- const files = fs14.readdirSync(currentDir, { withFileTypes: true });
6386
- for (const file of files) {
6387
- const fullPath = path21.join(currentDir, file.name);
6388
- if (file.isDirectory()) {
6389
- if (excludes.includes(file.name)) {
6390
- continue;
6391
- }
6392
- await walk(fullPath);
6393
- } else if (file.isFile()) {
6394
- const ext = path21.extname(file.name);
6395
- if ([".js", ".jsx", ".ts", ".tsx"].includes(ext)) {
6396
- scannedCount++;
6397
- try {
6398
- const content = fs14.readFileSync(fullPath, "utf8");
6399
- const result = await transformReactI18next2(content, fullPath);
6400
- if (result.modified) {
6401
- migratedCount++;
6402
- const relativePath = path21.relative(absoluteDir, fullPath);
6403
- if (dryRun) {
6404
- console.log(`[DRY RUN] Would migrate: ${relativePath}`);
6405
- } else {
6406
- fs14.writeFileSync(fullPath, result.code, "utf8");
6407
- console.log(`\u2705 Migrated: ${relativePath}`);
6408
- }
6409
- }
6410
- } catch (err) {
6411
- console.error(`\u274C Error processing file ${fullPath}:`, err.message);
6412
- }
6413
- }
6414
- }
6415
- }
6416
- }
6417
- try {
6418
- await walk(absoluteDir);
6419
- } catch (err) {
6420
- console.error(`\u274C Walk error:`, err.message);
6421
- }
6422
- console.log("--------------------------------------------------");
6423
- console.log(`\u{1F4CA} Summary:`);
6424
- console.log(` Scanned: ${scannedCount} files`);
6425
- if (dryRun) {
6426
- console.log(` Would migrate: ${migratedCount} files`);
6427
- } else {
6428
- console.log(` Migrated: ${migratedCount} files`);
6429
- }
6430
- console.log(`
6431
- \u{1F389} Done!`);
6432
- }
6433
- async function runDoctorCommand(args) {
6434
- let dir = ".";
6435
- let localesPath;
6436
- let app;
6437
- let share = false;
6438
- let token;
6439
- for (let i = 1; i < args.length; i++) {
6440
- const arg = args[i];
6441
- if (arg === "--dir") {
6442
- dir = args[++i] || ".";
6443
- } else if (arg === "--locales") {
6444
- localesPath = args[++i];
6445
- } else if (arg === "--app") {
6446
- app = normalizeAppArg(args[++i] || "");
6447
- } else if (arg === "--share") {
6448
- share = true;
6449
- } else if (arg === "--token") {
6450
- token = args[++i];
6451
- } else if (arg && !arg.startsWith("--")) {
6452
- dir = arg;
6453
- }
6454
- }
6455
- const projectDir = path21.resolve(dir);
6456
- if (!fs14.existsSync(projectDir)) {
6606
+ async function runDoctorCommand({ dir, localesPath, app, share, token }) {
6607
+ const projectDir = path22.resolve(dir);
6608
+ if (!fs15.existsSync(projectDir)) {
6457
6609
  console.error(`Error: Directory "${dir}" does not exist.`);
6458
6610
  process.exit(1);
6459
6611
  }
@@ -6489,19 +6641,25 @@ async function runDoctorCommand(args) {
6489
6641
  );
6490
6642
  }
6491
6643
  }
6492
- async function runCheckCommand(args) {
6644
+ async function runCheckCommand(argv) {
6645
+ const json = argv.includes("--json");
6646
+ const couldNotRun = (message, localesDir = null) => {
6647
+ if (json) console.log(JSON.stringify(checkErrorReport(message, localesDir), null, 2));
6648
+ else console.error(`\u274C ${message}`);
6649
+ process.exitCode = CHECK_COULD_NOT_RUN;
6650
+ };
6493
6651
  let parsed;
6494
6652
  try {
6495
- parsed = parseCheckArgs(args.slice(1));
6653
+ parsed = parseCheckArgs(argv);
6496
6654
  } catch (err) {
6497
- console.error(`Error: ${err.message}`);
6498
- process.exit(1);
6655
+ couldNotRun(err.message);
6656
+ return;
6499
6657
  }
6500
- const { dir, localesPath, sourceLocale, unused, strict, json, hardcoded, allow } = parsed;
6501
- const projectDir = path21.resolve(dir);
6502
- if (!fs14.existsSync(projectDir)) {
6503
- console.error(`Error: Directory "${dir}" does not exist.`);
6504
- process.exit(1);
6658
+ const { dir, localesPath, sourceLocale, unused, strict, hardcoded, allow } = parsed;
6659
+ const projectDir = path22.resolve(dir);
6660
+ if (!fs15.existsSync(projectDir)) {
6661
+ couldNotRun(`Directory "${dir}" does not exist.`);
6662
+ return;
6505
6663
  }
6506
6664
  let report;
6507
6665
  try {
@@ -6515,12 +6673,12 @@ async function runCheckCommand(args) {
6515
6673
  allow
6516
6674
  });
6517
6675
  } catch (err) {
6518
- console.error(`\u274C ${err.message}`);
6519
- process.exit(1);
6676
+ couldNotRun(err?.message ?? String(err), path22.resolve(projectDir, localesPath ?? "locales"));
6677
+ return;
6520
6678
  }
6521
6679
  process.exitCode = checkExitCode(report);
6522
6680
  if (json) {
6523
- console.log(JSON.stringify(report, null, 2));
6681
+ console.log(JSON.stringify(withCheckError(report), null, 2));
6524
6682
  return;
6525
6683
  }
6526
6684
  console.log(`
@@ -6537,7 +6695,7 @@ async function runCheckCommand(args) {
6537
6695
  }
6538
6696
  function printPackSummary(result, projectDir, warnings) {
6539
6697
  for (const warning of warnings) console.log(`\u26A0\uFE0F ${warning}`);
6540
- const outRel = path21.relative(projectDir, path21.dirname(result.shareDir)).split(path21.sep).join("/") || ".";
6698
+ const outRel = path22.relative(projectDir, path22.dirname(result.shareDir)).split(path22.sep).join("/") || ".";
6541
6699
  console.log(
6542
6700
  `\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).`
6543
6701
  );
@@ -6545,9 +6703,9 @@ function printPackSummary(result, projectDir, warnings) {
6545
6703
  }
6546
6704
  async function runPackCommand(args) {
6547
6705
  try {
6548
- const options = parseShareArgs(args.slice(1));
6706
+ const options = parseShareArgs(args.slice(1), "pack");
6549
6707
  checkPackArgs(options);
6550
- const projectDir = path21.resolve(options.dir);
6708
+ const projectDir = path22.resolve(options.dir);
6551
6709
  const result = await packOutput({
6552
6710
  projectDir,
6553
6711
  outDir: options.positional[0],
@@ -6561,14 +6719,14 @@ async function runPackCommand(args) {
6561
6719
  } catch (err) {
6562
6720
  if (!(err instanceof PackError)) throw err;
6563
6721
  console.error(`\u274C ${err.message}`);
6564
- process.exitCode = 1;
6722
+ process.exitCode = err.code === "USAGE" ? USAGE_EXIT_CODE : 1;
6565
6723
  }
6566
6724
  }
6567
6725
  async function runBuildCli(args) {
6568
6726
  try {
6569
- const options = parseShareArgs(args.slice(1));
6727
+ const options = parseShareArgs(args.slice(1), "build");
6570
6728
  checkBuildArgs(options);
6571
- const projectDir = path21.resolve(options.dir);
6729
+ const projectDir = path22.resolve(options.dir);
6572
6730
  const result = await runBuildCommand({
6573
6731
  projectDir,
6574
6732
  command: options.command,
@@ -6588,21 +6746,13 @@ async function runBuildCli(args) {
6588
6746
  } catch (err) {
6589
6747
  if (!(err instanceof PackError)) throw err;
6590
6748
  console.error(`\u274C ${err.message}`);
6591
- process.exitCode = 1;
6749
+ process.exitCode = err.code === "USAGE" ? USAGE_EXIT_CODE : 1;
6592
6750
  }
6593
6751
  }
6594
6752
  async function runInitShareCommand(args) {
6595
- let parsed;
6596
- try {
6597
- parsed = parseInitShareArgs(args.slice(1));
6598
- } catch (err) {
6599
- console.error(`\u274C ${err.message}`);
6600
- process.exitCode = 1;
6601
- return;
6602
- }
6603
- const { dir, localesPath, sourceLocale, branches, overwrite } = parsed;
6604
- const projectDir = path21.resolve(dir);
6605
- if (!fs14.existsSync(projectDir)) {
6753
+ const { dir, localesPath, sourceLocale, branches, overwrite } = args;
6754
+ const projectDir = path22.resolve(dir);
6755
+ if (!fs15.existsSync(projectDir)) {
6606
6756
  console.error(`Error: Directory "${dir}" does not exist.`);
6607
6757
  process.exit(1);
6608
6758
  }
@@ -6630,73 +6780,60 @@ async function runInitShareCommand(args) {
6630
6780
  process.exitCode = 1;
6631
6781
  }
6632
6782
  }
6783
+ var COMMANDS = ["migrate", "init", "studio", "doctor", "check", "pack", "build", "apply"];
6633
6784
  async function run2() {
6634
6785
  const args = process.argv.slice(2);
6635
- if (args.includes("--help") || args.includes("-h") || args[0] === "help") {
6636
- printHelp();
6637
- process.exit(0);
6638
- }
6639
- const COMMANDS = ["migrate", "init", "studio", "doctor", "check", "pack", "build", "apply"];
6640
- if (!args[0] || !COMMANDS.includes(args[0])) {
6641
- console.error(`Unknown command: "${args[0]}". Expected one of: ${COMMANDS.join(", ")}.`);
6786
+ const global = args[0] === "help" ? "help" : findGlobalOption(args, commandSpec(args[0]));
6787
+ if (global === "help") {
6642
6788
  printHelp();
6643
- process.exit(1);
6644
- }
6645
- if (args[0] === "doctor") {
6646
- await runDoctorCommand(args);
6647
- return;
6648
- }
6649
- if (args[0] === "check") {
6650
- await runCheckCommand(args);
6651
- return;
6652
- }
6653
- if (args[0] === "pack") {
6654
- await runPackCommand(args);
6655
6789
  return;
6656
6790
  }
6657
- if (args[0] === "build") {
6658
- await runBuildCli(args);
6791
+ if (global === "version") {
6792
+ console.log(readOwnVersion());
6659
6793
  return;
6660
6794
  }
6661
- if (args[0] === "apply") {
6662
- await runApplyCommand(args.slice(1));
6663
- return;
6664
- }
6665
- if (args[0] === "init" && args.includes("--share")) {
6666
- await runInitShareCommand(args);
6667
- return;
6795
+ const [command, ...rest] = args;
6796
+ if (command === void 0) {
6797
+ printHelp();
6798
+ throw new UsageError(
6799
+ "Missing command. Run livelocale <command>, for example livelocale check."
6800
+ );
6668
6801
  }
6669
- if (args[0] === "init") {
6670
- let dir = ".";
6671
- for (let i = 1; i < args.length; i++) {
6672
- const arg = args[i];
6673
- if (arg === "--dir") {
6674
- dir = args[++i] || ".";
6675
- } else if (arg && !arg.startsWith("--")) {
6676
- dir = arg;
6677
- }
6802
+ if (!COMMANDS.includes(command)) {
6803
+ throw new UsageError(`Unknown command "${command}". Expected one of: ${COMMANDS.join(", ")}.`);
6804
+ }
6805
+ switch (command) {
6806
+ case "doctor":
6807
+ return runDoctorCommand(parseDoctorArgs(rest));
6808
+ case "check":
6809
+ return runCheckCommand(rest);
6810
+ case "pack":
6811
+ return runPackCommand(args);
6812
+ case "build":
6813
+ return runBuildCli(args);
6814
+ case "apply":
6815
+ return runApplyCommand(rest);
6816
+ case "init": {
6817
+ const init = parseInitArgs(rest);
6818
+ if (init.share) return runInitShareCommand(init);
6819
+ runInit(init.dir);
6820
+ return;
6678
6821
  }
6679
- runInit(dir);
6680
- process.exit(0);
6681
- }
6682
- if (args[0] === "studio") {
6683
- let port = DEFAULT_STUDIO_PORT;
6684
- let appOrigin = DEFAULT_APP_ORIGIN;
6685
- for (let i = 1; i < args.length; i++) {
6686
- const arg = args[i];
6687
- if (arg === "--port") {
6688
- const val = parseInt(args[++i] || String(DEFAULT_STUDIO_PORT), 10);
6689
- if (!isNaN(val)) port = val;
6690
- } else if (arg === "--app") {
6691
- appOrigin = normalizeOriginArg(args[++i] || "", "--app");
6692
- }
6822
+ case "studio": {
6823
+ const { port, appOrigin } = parseStudioArgs(rest);
6824
+ return runStudio(port, appOrigin);
6693
6825
  }
6694
- await runStudio(port, appOrigin);
6695
- return;
6826
+ default:
6827
+ process.exitCode = await runMigrate(parseMigrateArgs(rest));
6828
+ return;
6696
6829
  }
6697
- await runMigrate(args);
6698
6830
  }
6699
6831
  run2().catch((err) => {
6832
+ if (err instanceof UsageError) {
6833
+ console.error(`\u274C ${err.message}`);
6834
+ process.exitCode = USAGE_EXIT_CODE;
6835
+ return;
6836
+ }
6700
6837
  console.error(`\u274C ${err?.message || err}`);
6701
6838
  process.exit(1);
6702
6839
  });