glotfile 0.8.9 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  import { Hono as Hono2 } from "hono";
3
3
  import { serve } from "@hono/node-server";
4
4
  import { fileURLToPath } from "url";
5
- import { dirname as dirname4, join as join18, resolve as resolve10, extname as extname3, sep as sep3 } from "path";
5
+ import { dirname as dirname4, join as join19, resolve as resolve10, extname as extname3, sep as sep3 } from "path";
6
6
  import { readFile, stat } from "fs/promises";
7
7
  import { createServer } from "net";
8
8
  import open from "open";
@@ -959,7 +959,7 @@ var PREFIX_PATTERNS = {
959
959
  /(?<!\.)(?<![a-zA-Z0-9_$])\bt\s*\(\s*`([^`$]*)\$\{/g
960
960
  ]
961
961
  };
962
- var CACHE_VERSION = 7;
962
+ var CACHE_VERSION = 8;
963
963
  var EXT_SCANNER = {
964
964
  ".php": "laravel",
965
965
  ".vue": "js-i18n",
@@ -1031,6 +1031,73 @@ function customPatterns(opts) {
1031
1031
  }
1032
1032
  return out;
1033
1033
  }
1034
+ var NEXT_INTL_IMPORT = /(?:from\s*|require\(\s*)['"]next-intl(?:\/[\w-]+)?['"]/;
1035
+ function isNextIntlFile(content) {
1036
+ return NEXT_INTL_IMPORT.test(content);
1037
+ }
1038
+ var NI_BIND = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:await\s+)?(?:useTranslations|getTranslations)\s*\(\s*(?:['"]([^'"]*)['"])?\s*\)/g;
1039
+ var NI_BIND_OBJ = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:await\s+)?getTranslations\s*\(\s*\{[^}]*?\bnamespace\s*:\s*['"]([^'"]+)['"][^}]*?\}\s*\)/g;
1040
+ var NI_METHOD = "(?:\\.(?:rich|markup|raw|has))?";
1041
+ function nextIntlBindings(content) {
1042
+ const out = [];
1043
+ for (const m of content.matchAll(NI_BIND)) out.push({ name: m[1], ns: m[2] ?? "", index: m.index });
1044
+ for (const m of content.matchAll(NI_BIND_OBJ)) out.push({ name: m[1], ns: m[2], index: m.index });
1045
+ return out;
1046
+ }
1047
+ function nsForBindingAt(bindings, name, index) {
1048
+ let best = null;
1049
+ for (const b of bindings) {
1050
+ if (b.name === name && b.index < index && (!best || b.index > best.index)) best = b;
1051
+ }
1052
+ return best ? best.ns : null;
1053
+ }
1054
+ function joinKey(ns, rel) {
1055
+ return ns ? `${ns}.${rel}` : rel;
1056
+ }
1057
+ function uniqueBindingNames(bindings) {
1058
+ return [...new Set(bindings.map((b) => b.name))];
1059
+ }
1060
+ function nextIntlRefMatches(content) {
1061
+ const bindings = nextIntlBindings(content);
1062
+ const out = [];
1063
+ for (const name of uniqueBindingNames(bindings)) {
1064
+ const re = new RegExp(
1065
+ `\\b${escapeRe2(name)}${NI_METHOD}\\s*\\(\\s*(?:'([^'\\n]+)'|"([^"\\n]+)"|\`([^\`$\\n]+)\`)`,
1066
+ "g"
1067
+ );
1068
+ let m;
1069
+ while ((m = re.exec(content)) !== null) {
1070
+ const ns = nsForBindingAt(bindings, name, m.index);
1071
+ if (ns === null) continue;
1072
+ out.push({ key: joinKey(ns, m[1] ?? m[2] ?? m[3]), index: m.index });
1073
+ }
1074
+ }
1075
+ return out;
1076
+ }
1077
+ function nextIntlPrefixMatches(content) {
1078
+ const bindings = nextIntlBindings(content);
1079
+ const out = [];
1080
+ for (const name of uniqueBindingNames(bindings)) {
1081
+ const ev = escapeRe2(name);
1082
+ const headConcat = new RegExp(`\\b${ev}${NI_METHOD}\\s*\\(\\s*(?:'([^'\\n]*)'|"([^"\\n]*)")\\s*\\+`, "g");
1083
+ const headTemplate = new RegExp(`\\b${ev}${NI_METHOD}\\s*\\(\\s*\`([^\`$\\n]*)\\$\\{`, "g");
1084
+ const dynamicArg = new RegExp(`\\b${ev}${NI_METHOD}\\s*\\(\\s*[A-Za-z_$][\\w$.]*\\s*[),]`, "g");
1085
+ let m;
1086
+ for (const re of [headConcat, headTemplate]) {
1087
+ while ((m = re.exec(content)) !== null) {
1088
+ const ns = nsForBindingAt(bindings, name, m.index);
1089
+ if (ns === null) continue;
1090
+ out.push({ prefix: joinKey(ns, m[1] ?? m[2] ?? ""), index: m.index });
1091
+ }
1092
+ }
1093
+ while ((m = dynamicArg.exec(content)) !== null) {
1094
+ const ns = nsForBindingAt(bindings, name, m.index);
1095
+ if (!ns) continue;
1096
+ out.push({ prefix: `${ns}.`, index: m.index });
1097
+ }
1098
+ }
1099
+ return out;
1100
+ }
1034
1101
  function lineStartOffsets(content) {
1035
1102
  const starts = [0];
1036
1103
  let idx = content.indexOf("\n");
@@ -1051,49 +1118,56 @@ function offsetToLineCol(starts, offset) {
1051
1118
  return { line: lo + 1, col: offset - starts[lo] + 1 };
1052
1119
  }
1053
1120
  function extractRefs(content, scanner, opts) {
1054
- const base = scanner === "flutter" ? flutterPatterns(content, opts) : PATTERNS[scanner] ?? [];
1055
- const patterns = [...base, ...customPatterns(opts)];
1056
- if (patterns.length === 0) return [];
1121
+ const useNextIntl = scanner === "next-intl" || scanner === "js-i18n" && isNextIntlFile(content);
1122
+ const effScanner = useNextIntl ? "next-intl" : scanner;
1057
1123
  const starts = lineStartOffsets(content);
1058
1124
  const result = [];
1059
1125
  const seen = /* @__PURE__ */ new Set();
1060
- for (const pattern of patterns) {
1061
- const re = new RegExp(pattern.source, "g");
1062
- let m;
1063
- while ((m = re.exec(content)) !== null) {
1064
- if (m.index === re.lastIndex) re.lastIndex++;
1065
- const key = m[1];
1066
- const { line, col } = offsetToLineCol(starts, m.index);
1067
- const dedup = `${line}:${col}:${key}`;
1068
- if (!seen.has(dedup)) {
1069
- seen.add(dedup);
1070
- result.push({ key, line, col, scanner });
1071
- }
1126
+ const push = (key, index) => {
1127
+ const { line, col } = offsetToLineCol(starts, index);
1128
+ const dedup = `${line}:${col}:${key}`;
1129
+ if (!seen.has(dedup)) {
1130
+ seen.add(dedup);
1131
+ result.push({ key, line, col, scanner: effScanner });
1072
1132
  }
1133
+ };
1134
+ if (useNextIntl) {
1135
+ for (const r of nextIntlRefMatches(content)) push(r.key, r.index);
1136
+ } else {
1137
+ const base = scanner === "flutter" ? flutterPatterns(content, opts) : PATTERNS[scanner] ?? [];
1138
+ for (const pattern of base) eachMatch(content, pattern, push);
1073
1139
  }
1140
+ for (const pattern of customPatterns(opts)) eachMatch(content, pattern, push);
1074
1141
  result.sort((a, b) => a.line - b.line || a.col - b.col);
1075
1142
  return result;
1076
1143
  }
1144
+ function eachMatch(content, pattern, fn) {
1145
+ const re = new RegExp(pattern.source, "g");
1146
+ let m;
1147
+ while ((m = re.exec(content)) !== null) {
1148
+ if (m.index === re.lastIndex) re.lastIndex++;
1149
+ fn(m[1], m.index);
1150
+ }
1151
+ }
1077
1152
  function extractPrefixes(content, scanner) {
1078
- const patterns = PREFIX_PATTERNS[scanner];
1079
- if (!patterns) return [];
1153
+ const useNextIntl = scanner === "next-intl" || scanner === "js-i18n" && isNextIntlFile(content);
1154
+ const effScanner = useNextIntl ? "next-intl" : scanner;
1080
1155
  const starts = lineStartOffsets(content);
1081
1156
  const result = [];
1082
1157
  const seen = /* @__PURE__ */ new Set();
1083
- for (const pattern of patterns) {
1084
- const re = new RegExp(pattern.source, "g");
1085
- let m;
1086
- while ((m = re.exec(content)) !== null) {
1087
- if (m.index === re.lastIndex) re.lastIndex++;
1088
- const prefix = m[1];
1089
- if (!prefix) continue;
1090
- const { line, col } = offsetToLineCol(starts, m.index);
1091
- const dedup = `${line}:${col}:${prefix}`;
1092
- if (!seen.has(dedup)) {
1093
- seen.add(dedup);
1094
- result.push({ prefix, line, col, scanner });
1095
- }
1158
+ const push = (prefix, index) => {
1159
+ if (!prefix) return;
1160
+ const { line, col } = offsetToLineCol(starts, index);
1161
+ const dedup = `${line}:${col}:${prefix}`;
1162
+ if (!seen.has(dedup)) {
1163
+ seen.add(dedup);
1164
+ result.push({ prefix, line, col, scanner: effScanner });
1096
1165
  }
1166
+ };
1167
+ if (useNextIntl) {
1168
+ for (const p of nextIntlPrefixMatches(content)) push(p.prefix, p.index);
1169
+ } else {
1170
+ for (const pattern of PREFIX_PATTERNS[scanner] ?? []) eachMatch(content, pattern, push);
1097
1171
  }
1098
1172
  result.sort((a, b) => a.line - b.line || a.col - b.col);
1099
1173
  return result;
@@ -2696,6 +2770,60 @@ var vueI18nJson = {
2696
2770
  }
2697
2771
  };
2698
2772
 
2773
+ // src/server/adapters/next-intl-json.ts
2774
+ var DEFAULT_LOCALE_CASE8 = "lower-hyphen";
2775
+ var nextIntlJson = {
2776
+ name: "next-intl-json",
2777
+ capabilities: {
2778
+ plural: "native",
2779
+ select: "native",
2780
+ nesting: "both",
2781
+ metadata: false,
2782
+ placeholderStyle: "icu",
2783
+ fileGrouping: "per-locale"
2784
+ },
2785
+ defaultLocaleCase: DEFAULT_LOCALE_CASE8,
2786
+ export(state, output) {
2787
+ const files = [];
2788
+ const warnings = [];
2789
+ warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE8));
2790
+ const { indent, finalNewline } = resolveFormat(state, output);
2791
+ const fmt = { indent, sortKeys: true, finalNewline };
2792
+ const emptyAs = resolveEmptyAs(output, "omit");
2793
+ const flatOutput = output.style === "flat";
2794
+ for (const locale of state.config.locales) {
2795
+ const flat = {};
2796
+ for (const [key, entry] of Object.entries(state.keys)) {
2797
+ if (entry.plural) {
2798
+ const forms = resolveForms(entry, locale, state.config.sourceLocale, emptyAs);
2799
+ if (!forms) continue;
2800
+ flat[key] = formsToIcu(entry.plural.arg, forms);
2801
+ } else {
2802
+ const raw = resolveScalar(entry, locale, state.config.sourceLocale, emptyAs);
2803
+ if (raw === null) continue;
2804
+ flat[key] = raw;
2805
+ }
2806
+ }
2807
+ let payload = flat;
2808
+ if (!flatOutput) {
2809
+ const { tree, collisions } = nestKeys(flat);
2810
+ for (const key of collisions) {
2811
+ warnings.push({
2812
+ code: "key-collision",
2813
+ key,
2814
+ locale,
2815
+ message: "key is both a leaf and a parent; dropped from nested output"
2816
+ });
2817
+ }
2818
+ payload = tree;
2819
+ }
2820
+ files.push({ path: resolvePath(output.path, resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE8)), contents: serializeJson(payload, fmt) });
2821
+ }
2822
+ files.sort((a, b) => a.path.localeCompare(b.path));
2823
+ return { files, warnings };
2824
+ }
2825
+ };
2826
+
2699
2827
  // src/server/adapters/angular-xliff.ts
2700
2828
  function xmlEscape2(s) {
2701
2829
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -2751,7 +2879,7 @@ function renderEmbeddedIcu(value) {
2751
2879
  function renderScalar(value, ids, placeholders) {
2752
2880
  return isIcuPluralOrSelect(value) ? renderEmbeddedIcu(value) : renderInterpolations(value, ids, placeholders);
2753
2881
  }
2754
- var DEFAULT_LOCALE_CASE8 = "bcp47-hyphen";
2882
+ var DEFAULT_LOCALE_CASE9 = "bcp47-hyphen";
2755
2883
  var angularXliff = {
2756
2884
  name: "angular-xliff",
2757
2885
  capabilities: {
@@ -2762,18 +2890,18 @@ var angularXliff = {
2762
2890
  placeholderStyle: "icu",
2763
2891
  fileGrouping: "per-locale"
2764
2892
  },
2765
- defaultLocaleCase: DEFAULT_LOCALE_CASE8,
2893
+ defaultLocaleCase: DEFAULT_LOCALE_CASE9,
2766
2894
  export(state, output) {
2767
2895
  const files = [];
2768
2896
  const warnings = [];
2769
- warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE8));
2897
+ warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE9));
2770
2898
  const sourceLocale = state.config.sourceLocale;
2771
- const sourceToken = resolveLocaleToken(output, sourceLocale, DEFAULT_LOCALE_CASE8);
2899
+ const sourceToken = resolveLocaleToken(output, sourceLocale, DEFAULT_LOCALE_CASE9);
2772
2900
  const emptyAs = resolveEmptyAs(output, "source");
2773
2901
  const keys = Object.keys(state.keys).sort();
2774
2902
  for (const locale of state.config.locales) {
2775
2903
  if (output.skipSourceLocale && locale === sourceLocale) continue;
2776
- const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE8);
2904
+ const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE9);
2777
2905
  const units = [];
2778
2906
  for (const key of keys) {
2779
2907
  const entry = state.keys[key];
@@ -2837,7 +2965,7 @@ function yamlMap(node, indent, level) {
2837
2965
  }
2838
2966
  return lines;
2839
2967
  }
2840
- var DEFAULT_LOCALE_CASE9 = "bcp47-hyphen";
2968
+ var DEFAULT_LOCALE_CASE10 = "bcp47-hyphen";
2841
2969
  var railsYaml = {
2842
2970
  name: "rails-yaml",
2843
2971
  capabilities: {
@@ -2848,10 +2976,10 @@ var railsYaml = {
2848
2976
  placeholderStyle: "named",
2849
2977
  fileGrouping: "per-locale"
2850
2978
  },
2851
- defaultLocaleCase: DEFAULT_LOCALE_CASE9,
2979
+ defaultLocaleCase: DEFAULT_LOCALE_CASE10,
2852
2980
  export(state, output) {
2853
2981
  const warnings = [];
2854
- warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE9));
2982
+ warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE10));
2855
2983
  const { indent, finalNewline } = resolveFormat(state, output);
2856
2984
  const emptyAs = resolveEmptyAs(output, "omit");
2857
2985
  const files = [];
@@ -2883,7 +3011,7 @@ var railsYaml = {
2883
3011
  for (const c of collisions) {
2884
3012
  warnings.push({ code: "key-collision", key: c, locale, message: "key is both a leaf and a parent; dropped from nested output" });
2885
3013
  }
2886
- const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE9);
3014
+ const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE10);
2887
3015
  const body = [`${yamlKey(token)}:`, ...yamlMap(nested, indent, 1)].join("\n");
2888
3016
  files.push({ path: resolvePath(output.path, token), contents: finalNewline ? body + "\n" : body });
2889
3017
  }
@@ -2926,6 +3054,7 @@ function getRegistry() {
2926
3054
  [appleStringsdict.name]: appleStringsdict,
2927
3055
  [appleStrings.name]: appleStrings,
2928
3056
  [vueI18nJson.name]: vueI18nJson,
3057
+ [nextIntlJson.name]: nextIntlJson,
2929
3058
  [angularXliff.name]: angularXliff,
2930
3059
  [railsYaml.name]: railsYaml
2931
3060
  };
@@ -2955,7 +3084,7 @@ function checkOutputs(state, root) {
2955
3084
  }
2956
3085
 
2957
3086
  // src/server/api.ts
2958
- import { readFileSync as readFileSync23, existsSync as existsSync13, readdirSync as readdirSync15, statSync as statSync10, rmSync as rmSync6 } from "fs";
3087
+ import { readFileSync as readFileSync24, existsSync as existsSync13, readdirSync as readdirSync16, statSync as statSync10, rmSync as rmSync6 } from "fs";
2959
3088
  import { dirname as dirname3, resolve as resolve9, basename, relative as relative4, sep as sep2 } from "path";
2960
3089
 
2961
3090
  // src/server/ai/anthropic.ts
@@ -4480,6 +4609,47 @@ function detectVue(root, forced = false) {
4480
4609
  }
4481
4610
  return null;
4482
4611
  }
4612
+ var NEXT_INTL_CONFIG_CANDIDATES = ["src/i18n/request.ts", "i18n/request.ts", "src/i18n/request.js", "i18n/request.js"];
4613
+ var NEXT_INTL_ROUTING_CANDIDATES = ["src/i18n/routing.ts", "i18n/routing.ts", "src/i18n/routing.js", "i18n/routing.js"];
4614
+ var NEXT_INTL_DIR_CANDIDATES = ["messages", "src/messages", "locales", "src/locales", "src/i18n/messages"];
4615
+ function hasNextIntlSignal(root) {
4616
+ if (NEXT_INTL_CONFIG_CANDIDATES.some((rel) => existsSync11(join6(root, rel)))) return true;
4617
+ try {
4618
+ const pkg = JSON.parse(readFileSync11(join6(root, "package.json"), "utf8"));
4619
+ if (pkg.dependencies?.["next-intl"] || pkg.devDependencies?.["next-intl"]) return true;
4620
+ } catch {
4621
+ }
4622
+ return false;
4623
+ }
4624
+ function nextIntlDefaultLocale(root) {
4625
+ for (const rel of NEXT_INTL_ROUTING_CANDIDATES) {
4626
+ try {
4627
+ const m = readFileSync11(join6(root, rel), "utf8").match(/defaultLocale\s*:\s*['"]([^'"]+)['"]/);
4628
+ if (m) return m[1];
4629
+ } catch {
4630
+ }
4631
+ }
4632
+ return void 0;
4633
+ }
4634
+ function detectNextIntl(root, forced = false) {
4635
+ if (!forced && !hasNextIntlSignal(root)) return null;
4636
+ for (const rel of NEXT_INTL_DIR_CANDIDATES) {
4637
+ const localeRoot = join6(root, rel);
4638
+ if (!safeIsDir(localeRoot)) continue;
4639
+ const locales = readdirSync3(localeRoot).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5)).filter((l) => LOCALE_RE.test(l));
4640
+ if (locales.length === 0) continue;
4641
+ const def = nextIntlDefaultLocale(root);
4642
+ const sourceLocale = def && locales.includes(def) ? def : pickSource(locales, (loc) => {
4643
+ try {
4644
+ return statSync3(join6(localeRoot, `${loc}.json`)).size;
4645
+ } catch {
4646
+ return 0;
4647
+ }
4648
+ });
4649
+ return { format: "next-intl-json", localeRoot, locales, sourceLocale };
4650
+ }
4651
+ return null;
4652
+ }
4483
4653
  function detectArb(root) {
4484
4654
  for (const rel of ["lib/l10n", "l10n", "lib/src/l10n"]) {
4485
4655
  const localeRoot = join6(root, rel);
@@ -4626,6 +4796,7 @@ function detectAppleStringsdict(root) {
4626
4796
  }
4627
4797
  var DETECTORS = [
4628
4798
  detectLaravel,
4799
+ detectNextIntl,
4629
4800
  detectVue,
4630
4801
  detectArb,
4631
4802
  detectApple,
@@ -4637,6 +4808,7 @@ var DETECTORS = [
4637
4808
  ];
4638
4809
  var BY_FORMAT = {
4639
4810
  "laravel-php": detectLaravel,
4811
+ "next-intl-json": (root) => detectNextIntl(root, true),
4640
4812
  "vue-i18n-json": (root) => detectVue(root, true),
4641
4813
  "flutter-arb": detectArb,
4642
4814
  "apple-strings": detectApple,
@@ -4717,9 +4889,40 @@ var vueI18nJson2 = {
4717
4889
  }
4718
4890
  };
4719
4891
 
4892
+ // src/server/import/parsers/next-intl-json.ts
4893
+ import { readdirSync as readdirSync5, readFileSync as readFileSync13 } from "fs";
4894
+ import { join as join8 } from "path";
4895
+ var LOCALE_RE3 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
4896
+ var nextIntlJson2 = {
4897
+ name: "next-intl-json",
4898
+ parse(localeRoot, opts) {
4899
+ const warnings = [];
4900
+ const keys = {};
4901
+ const locales = [];
4902
+ for (const file of readdirSync5(localeRoot).sort()) {
4903
+ if (!file.endsWith(".json")) continue;
4904
+ const locale = file.slice(0, -".json".length);
4905
+ if (!LOCALE_RE3.test(locale)) continue;
4906
+ if (opts?.locales && !opts.locales.includes(locale)) continue;
4907
+ let data;
4908
+ try {
4909
+ data = JSON.parse(readFileSync13(join8(localeRoot, file), "utf8"));
4910
+ } catch (e) {
4911
+ warnings.push(`next-intl-json: failed to parse ${file}: ${e.message}`);
4912
+ continue;
4913
+ }
4914
+ if (!locales.includes(locale)) locales.push(locale);
4915
+ for (const [key, value] of Object.entries(flattenObject(data, "", warnings))) {
4916
+ (keys[key] ??= { values: {} }).values[locale] = value;
4917
+ }
4918
+ }
4919
+ return { locales, keys, warnings };
4920
+ }
4921
+ };
4922
+
4720
4923
  // src/server/import/parsers/laravel-php.ts
4721
- import { readdirSync as readdirSync5, statSync as statSync4 } from "fs";
4722
- import { join as join8, relative as relative2 } from "path";
4924
+ import { readdirSync as readdirSync6, statSync as statSync4 } from "fs";
4925
+ import { join as join9, relative as relative2 } from "path";
4723
4926
  import { execFileSync } from "child_process";
4724
4927
 
4725
4928
  // src/server/import/placeholders.ts
@@ -4735,13 +4938,13 @@ function railsToCanonical(value) {
4735
4938
 
4736
4939
  // src/server/import/parsers/laravel-php.ts
4737
4940
  function listDirs2(dir) {
4738
- return readdirSync5(dir).filter((e) => statSync4(join8(dir, e)).isDirectory());
4941
+ return readdirSync6(dir).filter((e) => statSync4(join9(dir, e)).isDirectory());
4739
4942
  }
4740
4943
  function listPhpFiles(dir) {
4741
4944
  const out = [];
4742
4945
  const walk = (d) => {
4743
- for (const e of readdirSync5(d)) {
4744
- const full = join8(d, e);
4946
+ for (const e of readdirSync6(d)) {
4947
+ const full = join9(d, e);
4745
4948
  if (statSync4(full).isDirectory()) walk(full);
4746
4949
  else if (e.endsWith(".php")) out.push(full);
4747
4950
  }
@@ -4778,7 +4981,7 @@ var laravelPhp2 = {
4778
4981
  for (const locale of listDirs2(localeRoot).sort()) {
4779
4982
  if (locale === "vendor") continue;
4780
4983
  if (opts?.locales && !opts.locales.includes(locale)) continue;
4781
- const localeDir = join8(localeRoot, locale);
4984
+ const localeDir = join9(localeRoot, locale);
4782
4985
  locales.push(locale);
4783
4986
  for (const file of listPhpFiles(localeDir)) {
4784
4987
  const group = relative2(localeDir, file).replace(/\\/g, "/").replace(/\.php$/, "");
@@ -4801,15 +5004,15 @@ var laravelPhp2 = {
4801
5004
  };
4802
5005
 
4803
5006
  // src/server/import/parsers/flutter-arb.ts
4804
- import { readdirSync as readdirSync6, readFileSync as readFileSync13 } from "fs";
4805
- import { join as join9 } from "path";
4806
- var LOCALE_RE3 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
5007
+ import { readdirSync as readdirSync7, readFileSync as readFileSync14 } from "fs";
5008
+ import { join as join10 } from "path";
5009
+ var LOCALE_RE4 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
4807
5010
  function localeFromArbName(file) {
4808
5011
  const m = file.match(/^(.+)\.arb$/);
4809
5012
  if (!m) return null;
4810
5013
  let locale = m[1];
4811
5014
  if (locale.startsWith("app_")) locale = locale.slice(4);
4812
- return LOCALE_RE3.test(locale) ? locale : null;
5015
+ return LOCALE_RE4.test(locale) ? locale : null;
4813
5016
  }
4814
5017
  function placeholderMeta(raw) {
4815
5018
  if (!raw || typeof raw !== "object") return void 0;
@@ -4831,14 +5034,14 @@ var flutterArb2 = {
4831
5034
  const warnings = [];
4832
5035
  const keys = {};
4833
5036
  const locales = [];
4834
- for (const file of readdirSync6(localeRoot).sort()) {
5037
+ for (const file of readdirSync7(localeRoot).sort()) {
4835
5038
  if (!file.endsWith(".arb")) continue;
4836
5039
  const locale = localeFromArbName(file);
4837
5040
  if (!locale) continue;
4838
5041
  if (opts?.locales && !opts.locales.includes(locale)) continue;
4839
5042
  let data;
4840
5043
  try {
4841
- data = JSON.parse(readFileSync13(join9(localeRoot, file), "utf8"));
5044
+ data = JSON.parse(readFileSync14(join10(localeRoot, file), "utf8"));
4842
5045
  } catch (e) {
4843
5046
  warnings.push(`flutter-arb: failed to parse ${file}: ${e.message}`);
4844
5047
  continue;
@@ -4863,14 +5066,14 @@ var flutterArb2 = {
4863
5066
  };
4864
5067
 
4865
5068
  // src/server/import/parsers/apple-strings.ts
4866
- import { readdirSync as readdirSync7, readFileSync as readFileSync14, statSync as statSync5 } from "fs";
4867
- import { join as join10 } from "path";
4868
- var LOCALE_RE4 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
5069
+ import { readdirSync as readdirSync8, readFileSync as readFileSync15, statSync as statSync5 } from "fs";
5070
+ import { join as join11 } from "path";
5071
+ var LOCALE_RE5 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
4869
5072
  var TABLE = "Localizable.strings";
4870
5073
  function localeFromLproj(dir) {
4871
5074
  const m = dir.match(/^(.+)\.lproj$/);
4872
5075
  if (!m) return null;
4873
- return LOCALE_RE4.test(m[1]) ? m[1] : null;
5076
+ return LOCALE_RE5.test(m[1]) ? m[1] : null;
4874
5077
  }
4875
5078
  function printfToCanonical(s) {
4876
5079
  return s.replace(/%%/g, "%");
@@ -4976,20 +5179,20 @@ var appleStrings2 = {
4976
5179
  const warnings = [];
4977
5180
  const keys = {};
4978
5181
  const locales = [];
4979
- for (const dir of readdirSync7(localeRoot).sort()) {
5182
+ for (const dir of readdirSync8(localeRoot).sort()) {
4980
5183
  const locale = localeFromLproj(dir);
4981
5184
  if (!locale) continue;
4982
5185
  if (opts?.locales && !opts.locales.includes(locale)) continue;
4983
- const file = join10(localeRoot, dir, TABLE);
5186
+ const file = join11(localeRoot, dir, TABLE);
4984
5187
  let text;
4985
5188
  try {
4986
5189
  if (!statSync5(file).isFile()) continue;
4987
- text = readFileSync14(file, "utf8");
5190
+ text = readFileSync15(file, "utf8");
4988
5191
  } catch {
4989
5192
  continue;
4990
5193
  }
4991
5194
  locales.push(locale);
4992
- const others = readdirSync7(join10(localeRoot, dir)).filter((f) => f.endsWith(".strings") && f !== TABLE);
5195
+ const others = readdirSync8(join11(localeRoot, dir)).filter((f) => f.endsWith(".strings") && f !== TABLE);
4993
5196
  if (others.length) {
4994
5197
  warnings.push(`apple-strings: ${dir} has other .strings tables (${others.join(", ")}); only ${TABLE} is imported`);
4995
5198
  }
@@ -5002,9 +5205,9 @@ var appleStrings2 = {
5002
5205
  };
5003
5206
 
5004
5207
  // src/server/import/parsers/angular-xliff.ts
5005
- import { readdirSync as readdirSync8, readFileSync as readFileSync15 } from "fs";
5006
- import { join as join11 } from "path";
5007
- var LOCALE_RE5 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
5208
+ import { readdirSync as readdirSync9, readFileSync as readFileSync16 } from "fs";
5209
+ import { join as join12 } from "path";
5210
+ var LOCALE_RE6 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
5008
5211
  var FILE_RE = /^messages(?:\.(.+))?\.xlf$/;
5009
5212
  function decodeEntities(s) {
5010
5213
  return s.replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16))).replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(Number(d))).replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
@@ -5064,13 +5267,13 @@ var angularXliff2 = {
5064
5267
  const seen = (loc) => {
5065
5268
  if (!locales.includes(loc)) locales.push(loc);
5066
5269
  };
5067
- const files = readdirSync8(localeRoot).filter((f) => FILE_RE.test(f)).sort((a, b) => (a === "messages.xlf" ? -1 : 0) - (b === "messages.xlf" ? -1 : 0) || a.localeCompare(b));
5270
+ const files = readdirSync9(localeRoot).filter((f) => FILE_RE.test(f)).sort((a, b) => (a === "messages.xlf" ? -1 : 0) - (b === "messages.xlf" ? -1 : 0) || a.localeCompare(b));
5068
5271
  for (const file of files) {
5069
5272
  const fnameLocale = file.match(FILE_RE)[1];
5070
- if (fnameLocale !== void 0 && !LOCALE_RE5.test(fnameLocale)) continue;
5273
+ if (fnameLocale !== void 0 && !LOCALE_RE6.test(fnameLocale)) continue;
5071
5274
  let xml;
5072
5275
  try {
5073
- xml = readFileSync15(join11(localeRoot, file), "utf8");
5276
+ xml = readFileSync16(join12(localeRoot, file), "utf8");
5074
5277
  } catch (e) {
5075
5278
  warnings.push(`angular-xliff: failed to read ${file}: ${e.message}`);
5076
5279
  continue;
@@ -5115,9 +5318,9 @@ var angularXliff2 = {
5115
5318
  };
5116
5319
 
5117
5320
  // src/server/import/parsers/gettext-po.ts
5118
- import { readdirSync as readdirSync9, readFileSync as readFileSync16 } from "fs";
5119
- import { join as join12 } from "path";
5120
- var LOCALE_RE6 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
5321
+ import { readdirSync as readdirSync10, readFileSync as readFileSync17 } from "fs";
5322
+ import { join as join13 } from "path";
5323
+ var LOCALE_RE7 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
5121
5324
  var DIRECTIVE_RE = /^(msgctxt|msgid_plural|msgid|msgstr)(?:\[(\d+)\])?[ \t]+"(.*)"\s*$/;
5122
5325
  var CONT_RE = /^[ \t]*"(.*)"\s*$/;
5123
5326
  function unescapePo(s) {
@@ -5204,21 +5407,21 @@ function parseEntries(text) {
5204
5407
  }
5205
5408
  function discoverPoFiles(root) {
5206
5409
  const found = [];
5207
- const entries = readdirSync9(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
5410
+ const entries = readdirSync10(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
5208
5411
  for (const e of entries) {
5209
5412
  if (e.isFile() && e.name.endsWith(".po")) {
5210
5413
  const base = e.name.slice(0, -3);
5211
- found.push({ path: join12(root, e.name), rel: e.name, locale: LOCALE_RE6.test(base) ? base : null });
5212
- } else if (e.isDirectory() && LOCALE_RE6.test(e.name)) {
5213
- for (const sub of [join12(e.name, "LC_MESSAGES"), e.name]) {
5414
+ found.push({ path: join13(root, e.name), rel: e.name, locale: LOCALE_RE7.test(base) ? base : null });
5415
+ } else if (e.isDirectory() && LOCALE_RE7.test(e.name)) {
5416
+ for (const sub of [join13(e.name, "LC_MESSAGES"), e.name]) {
5214
5417
  let names;
5215
5418
  try {
5216
- names = readdirSync9(join12(root, sub)).sort();
5419
+ names = readdirSync10(join13(root, sub)).sort();
5217
5420
  } catch {
5218
5421
  continue;
5219
5422
  }
5220
5423
  for (const f of names) {
5221
- if (f.endsWith(".po")) found.push({ path: join12(root, sub, f), rel: join12(sub, f), locale: e.name });
5424
+ if (f.endsWith(".po")) found.push({ path: join13(root, sub, f), rel: join13(sub, f), locale: e.name });
5222
5425
  }
5223
5426
  }
5224
5427
  }
@@ -5234,7 +5437,7 @@ var gettextPo2 = {
5234
5437
  for (const file of discoverPoFiles(localeRoot)) {
5235
5438
  let entries;
5236
5439
  try {
5237
- entries = parseEntries(readFileSync16(file.path, "utf8"));
5440
+ entries = parseEntries(readFileSync17(file.path, "utf8"));
5238
5441
  } catch (e) {
5239
5442
  warnings.push(`gettext-po: failed to parse ${file.rel}: ${e.message}`);
5240
5443
  continue;
@@ -5279,9 +5482,9 @@ var gettextPo2 = {
5279
5482
  };
5280
5483
 
5281
5484
  // src/server/import/parsers/i18next-json.ts
5282
- import { readdirSync as readdirSync10, readFileSync as readFileSync17, statSync as statSync6 } from "fs";
5283
- import { join as join13 } from "path";
5284
- var LOCALE_RE7 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
5485
+ import { readdirSync as readdirSync11, readFileSync as readFileSync18, statSync as statSync6 } from "fs";
5486
+ import { join as join14 } from "path";
5487
+ var LOCALE_RE8 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
5285
5488
  var PLURAL_SUFFIX_RE = /^(.+)_(zero|one|two|few|many|other)$/;
5286
5489
  var PLURAL_ARG = "count";
5287
5490
  var DEFAULT_NAMESPACE = "translation";
@@ -5299,7 +5502,7 @@ function fromI18next(value) {
5299
5502
  function ingestFile(path, label, prefix, locale, keys, warnings) {
5300
5503
  let data;
5301
5504
  try {
5302
- data = JSON.parse(readFileSync17(path, "utf8"));
5505
+ data = JSON.parse(readFileSync18(path, "utf8"));
5303
5506
  } catch (e) {
5304
5507
  warnings.push(`i18next-json: failed to parse ${label}: ${e.message}`);
5305
5508
  return false;
@@ -5340,22 +5543,22 @@ var i18nextJson2 = {
5340
5543
  const warnings = [];
5341
5544
  const keys = {};
5342
5545
  const locales = [];
5343
- for (const entry of readdirSync10(localeRoot).sort()) {
5344
- const full = join13(localeRoot, entry);
5546
+ for (const entry of readdirSync11(localeRoot).sort()) {
5547
+ const full = join14(localeRoot, entry);
5345
5548
  if (safeIsDir2(full)) {
5346
- if (!LOCALE_RE7.test(entry)) continue;
5549
+ if (!LOCALE_RE8.test(entry)) continue;
5347
5550
  if (opts?.locales && !opts.locales.includes(entry)) continue;
5348
5551
  let any = false;
5349
- for (const file of readdirSync10(full).sort()) {
5552
+ for (const file of readdirSync11(full).sort()) {
5350
5553
  if (!file.endsWith(".json")) continue;
5351
5554
  const ns = file.slice(0, -".json".length);
5352
5555
  const prefix = ns === DEFAULT_NAMESPACE ? "" : `${ns}.`;
5353
- if (ingestFile(join13(full, file), `${entry}/${file}`, prefix, entry, keys, warnings)) any = true;
5556
+ if (ingestFile(join14(full, file), `${entry}/${file}`, prefix, entry, keys, warnings)) any = true;
5354
5557
  }
5355
5558
  if (any && !locales.includes(entry)) locales.push(entry);
5356
5559
  } else if (entry.endsWith(".json")) {
5357
5560
  const locale = entry.slice(0, -".json".length);
5358
- if (!LOCALE_RE7.test(locale)) continue;
5561
+ if (!LOCALE_RE8.test(locale)) continue;
5359
5562
  if (opts?.locales && !opts.locales.includes(locale)) continue;
5360
5563
  if (ingestFile(full, entry, "", locale, keys, warnings) && !locales.includes(locale)) {
5361
5564
  locales.push(locale);
@@ -5367,9 +5570,9 @@ var i18nextJson2 = {
5367
5570
  };
5368
5571
 
5369
5572
  // src/server/import/parsers/rails-yaml.ts
5370
- import { readdirSync as readdirSync11, readFileSync as readFileSync18 } from "fs";
5371
- import { join as join14 } from "path";
5372
- var LOCALE_RE8 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/i;
5573
+ import { readdirSync as readdirSync12, readFileSync as readFileSync19 } from "fs";
5574
+ import { join as join15 } from "path";
5575
+ var LOCALE_RE9 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/i;
5373
5576
  var CATEGORY_SET = new Set(PLURAL_CATEGORIES);
5374
5577
  function makeNode() {
5375
5578
  return /* @__PURE__ */ Object.create(null);
@@ -5582,18 +5785,18 @@ var railsYaml2 = {
5582
5785
  else flatten(v, key, locale, file);
5583
5786
  }
5584
5787
  };
5585
- for (const file of readdirSync11(localeRoot).sort()) {
5788
+ for (const file of readdirSync12(localeRoot).sort()) {
5586
5789
  if (!file.endsWith(".yml") && !file.endsWith(".yaml")) continue;
5587
5790
  let text;
5588
5791
  try {
5589
- text = readFileSync18(join14(localeRoot, file), "utf8");
5792
+ text = readFileSync19(join15(localeRoot, file), "utf8");
5590
5793
  } catch (e) {
5591
5794
  warnings.push(`rails-yaml: failed to read ${file}: ${e.message}`);
5592
5795
  continue;
5593
5796
  }
5594
5797
  const { roots } = parseYamlSubset(text, file, warnings);
5595
5798
  for (const token of Object.keys(roots).sort()) {
5596
- if (!LOCALE_RE8.test(token)) {
5799
+ if (!LOCALE_RE9.test(token)) {
5597
5800
  warnings.push(`rails-yaml: ${file}: top-level key "${token}" is not a locale; subtree skipped`);
5598
5801
  continue;
5599
5802
  }
@@ -5607,14 +5810,14 @@ var railsYaml2 = {
5607
5810
  };
5608
5811
 
5609
5812
  // src/server/import/parsers/apple-stringsdict.ts
5610
- import { readdirSync as readdirSync12, readFileSync as readFileSync19, statSync as statSync7 } from "fs";
5611
- import { join as join15 } from "path";
5612
- var LOCALE_RE9 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
5813
+ import { readdirSync as readdirSync13, readFileSync as readFileSync20, statSync as statSync7 } from "fs";
5814
+ import { join as join16 } from "path";
5815
+ var LOCALE_RE10 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
5613
5816
  var TABLE2 = "Localizable.stringsdict";
5614
5817
  function localeFromLproj2(dir) {
5615
5818
  const m = dir.match(/^(.+)\.lproj$/);
5616
5819
  if (!m) return null;
5617
- return LOCALE_RE9.test(m[1]) ? m[1] : null;
5820
+ return LOCALE_RE10.test(m[1]) ? m[1] : null;
5618
5821
  }
5619
5822
  function decodeEntities2(s) {
5620
5823
  return s.replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCodePoint(parseInt(h, 16))).replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(Number(d))).replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
@@ -5758,20 +5961,20 @@ var appleStringsdict2 = {
5758
5961
  const warnings = [];
5759
5962
  const keys = {};
5760
5963
  const locales = [];
5761
- for (const dir of readdirSync12(localeRoot).sort()) {
5964
+ for (const dir of readdirSync13(localeRoot).sort()) {
5762
5965
  const locale = localeFromLproj2(dir);
5763
5966
  if (!locale) continue;
5764
5967
  if (opts?.locales && !opts.locales.includes(locale)) continue;
5765
- const file = join15(localeRoot, dir, TABLE2);
5968
+ const file = join16(localeRoot, dir, TABLE2);
5766
5969
  let text;
5767
5970
  try {
5768
5971
  if (!statSync7(file).isFile()) continue;
5769
- text = readFileSync19(file, "utf8");
5972
+ text = readFileSync20(file, "utf8");
5770
5973
  } catch {
5771
5974
  continue;
5772
5975
  }
5773
5976
  locales.push(locale);
5774
- const others = readdirSync12(join15(localeRoot, dir)).filter(
5977
+ const others = readdirSync13(join16(localeRoot, dir)).filter(
5775
5978
  (f) => f.endsWith(".stringsdict") && f !== TABLE2
5776
5979
  );
5777
5980
  if (others.length) {
@@ -5799,6 +6002,7 @@ var appleStringsdict2 = {
5799
6002
  // src/server/import/parsers/index.ts
5800
6003
  var REGISTRY = {
5801
6004
  [vueI18nJson2.name]: vueI18nJson2,
6005
+ [nextIntlJson2.name]: nextIntlJson2,
5802
6006
  [laravelPhp2.name]: laravelPhp2,
5803
6007
  [flutterArb2.name]: flutterArb2,
5804
6008
  [appleStrings2.name]: appleStrings2,
@@ -5818,6 +6022,9 @@ function getParser(name) {
5818
6022
  var OUTPUT_BY_FORMAT = {
5819
6023
  "laravel-php": { adapter: "laravel-php", path: "lang/{locale}/{namespace}.php" },
5820
6024
  "vue-i18n-json": { adapter: "vue-i18n-json", path: "src/locale/{locale}.json" },
6025
+ // rootRelative: write back to wherever the messages dir was found (messages/,
6026
+ // src/messages/, …) rather than assuming the conventional root-level location.
6027
+ "next-intl-json": { adapter: "next-intl-json", path: "{locale}.json", rootRelative: true },
5821
6028
  "flutter-arb": { adapter: "flutter-arb", path: "lib/l10n/app_{locale}.arb" },
5822
6029
  "apple-strings": { adapter: "apple-strings", path: "{locale}.lproj/Localizable.strings", rootRelative: true },
5823
6030
  // skipSourceLocale: ng extract-i18n owns messages.xlf (the source file); glotfile
@@ -6087,7 +6294,7 @@ function refreshLocationUsage(projectRoot, format) {
6087
6294
  }
6088
6295
 
6089
6296
  // src/server/export-run.ts
6090
- import { existsSync as existsSync12, readFileSync as readFileSync20, readdirSync as readdirSync13, rmdirSync, statSync as statSync8, unlinkSync } from "fs";
6297
+ import { existsSync as existsSync12, readFileSync as readFileSync21, readdirSync as readdirSync14, rmdirSync, statSync as statSync8, unlinkSync } from "fs";
6091
6298
  import { dirname as dirname2, resolve as resolve7, sep } from "path";
6092
6299
  function effectiveLocales(config) {
6093
6300
  const limit = config.exportLocales;
@@ -6143,7 +6350,7 @@ function pruneStaleLocaleFiles(output, validTokens, projectRoot) {
6143
6350
  const re = segmentRegExp(segment);
6144
6351
  let entries;
6145
6352
  try {
6146
- entries = readdirSync13(dir, { withFileTypes: true });
6353
+ entries = readdirSync14(dir, { withFileTypes: true });
6147
6354
  } catch {
6148
6355
  return;
6149
6356
  }
@@ -6186,7 +6393,7 @@ function exportToDisk(state, projectRoot, opts) {
6186
6393
  writtenPaths.add(abs);
6187
6394
  let current = null;
6188
6395
  try {
6189
- current = readFileSync20(abs, "utf8");
6396
+ current = readFileSync21(abs, "utf8");
6190
6397
  } catch {
6191
6398
  }
6192
6399
  if (current === f.contents) {
@@ -6203,17 +6410,17 @@ function exportToDisk(state, projectRoot, opts) {
6203
6410
  }
6204
6411
 
6205
6412
  // src/server/ui-prefs.ts
6206
- import { readFileSync as readFileSync21 } from "fs";
6413
+ import { readFileSync as readFileSync22 } from "fs";
6207
6414
  import { homedir } from "os";
6208
- import { join as join16 } from "path";
6415
+ import { join as join17 } from "path";
6209
6416
  var THEMES = ["system", "light", "dark"];
6210
6417
  var isThemeMode = (v) => THEMES.includes(v);
6211
6418
  var isPanelWidth = (v) => typeof v === "number" && Number.isFinite(v) && v >= 120 && v <= 1200;
6212
- var defaultUiPrefsPath = () => join16(homedir(), ".glotfile", "ui.json");
6419
+ var defaultUiPrefsPath = () => join17(homedir(), ".glotfile", "ui.json");
6213
6420
  var DEFAULTS = { theme: "system" };
6214
6421
  function readJson(path) {
6215
6422
  try {
6216
- const parsed = JSON.parse(readFileSync21(path, "utf8"));
6423
+ const parsed = JSON.parse(readFileSync22(path, "utf8"));
6217
6424
  return parsed && typeof parsed === "object" ? parsed : {};
6218
6425
  } catch {
6219
6426
  return {};
@@ -6232,7 +6439,7 @@ function saveUiPrefs(path, prefs) {
6232
6439
  }
6233
6440
 
6234
6441
  // src/server/local-settings.ts
6235
- import { readFileSync as readFileSync22 } from "fs";
6442
+ import { readFileSync as readFileSync23 } from "fs";
6236
6443
  import { resolve as resolve8 } from "path";
6237
6444
  var EDITOR_IDS = ["vscode", "zed", "phpstorm"];
6238
6445
  var isEditorId = (v) => EDITOR_IDS.includes(v);
@@ -6247,7 +6454,7 @@ var DEFAULT_EDITOR = "vscode";
6247
6454
  var settingsPath = (projectRoot) => resolve8(projectRoot, ".glotfile", "settings.json");
6248
6455
  function readJson2(path) {
6249
6456
  try {
6250
- const parsed = JSON.parse(readFileSync22(path, "utf8"));
6457
+ const parsed = JSON.parse(readFileSync23(path, "utf8"));
6251
6458
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
6252
6459
  } catch {
6253
6460
  return {};
@@ -6336,8 +6543,8 @@ function createEventHub() {
6336
6543
  }
6337
6544
 
6338
6545
  // src/server/watch.ts
6339
- import { statSync as statSync9, readdirSync as readdirSync14 } from "fs";
6340
- import { join as join17 } from "path";
6546
+ import { statSync as statSync9, readdirSync as readdirSync15 } from "fs";
6547
+ import { join as join18 } from "path";
6341
6548
  import { createHash as createHash2 } from "crypto";
6342
6549
  function hashState(state) {
6343
6550
  return createHash2("sha1").update(serializeJson(state, state.config.format)).digest("hex");
@@ -6353,15 +6560,15 @@ function signature(statePath) {
6353
6560
  const parts = [];
6354
6561
  for (const rel of ["config.json", "keys.json"]) {
6355
6562
  try {
6356
- const s = statSync9(join17(dir, rel));
6563
+ const s = statSync9(join18(dir, rel));
6357
6564
  parts.push(`${rel}:${s.size}:${s.mtimeMs}`);
6358
6565
  } catch {
6359
6566
  }
6360
6567
  }
6361
6568
  try {
6362
- for (const name of readdirSync14(join17(dir, "locales")).sort()) {
6569
+ for (const name of readdirSync15(join18(dir, "locales")).sort()) {
6363
6570
  if (!name.endsWith(".json")) continue;
6364
- const s = statSync9(join17(dir, "locales", name));
6571
+ const s = statSync9(join18(dir, "locales", name));
6365
6572
  parts.push(`${name}:${s.size}:${s.mtimeMs}`);
6366
6573
  }
6367
6574
  } catch {
@@ -6436,7 +6643,7 @@ function projectName(root) {
6436
6643
  const nameFile = resolve9(root, ".idea", ".name");
6437
6644
  if (existsSync13(nameFile)) {
6438
6645
  try {
6439
- const name = readFileSync23(nameFile, "utf8").trim();
6646
+ const name = readFileSync24(nameFile, "utf8").trim();
6440
6647
  if (name) return name;
6441
6648
  } catch {
6442
6649
  }
@@ -6602,7 +6809,7 @@ function createApi(deps) {
6602
6809
  if (depth > 4) return;
6603
6810
  let entries = [];
6604
6811
  try {
6605
- entries = readdirSync15(dir);
6812
+ entries = readdirSync16(dir);
6606
6813
  } catch {
6607
6814
  return;
6608
6815
  }
@@ -7622,7 +7829,7 @@ function createApi(deps) {
7622
7829
 
7623
7830
  // src/server/server.ts
7624
7831
  var here = dirname4(fileURLToPath(import.meta.url));
7625
- var DEFAULT_UI_DIR = join18(here, "..", "ui");
7832
+ var DEFAULT_UI_DIR = join19(here, "..", "ui");
7626
7833
  var MIME = {
7627
7834
  ".html": "text/html; charset=utf-8",
7628
7835
  ".js": "text/javascript; charset=utf-8",
@@ -7689,7 +7896,7 @@ function buildApp(opts) {
7689
7896
  const file = await readFileResponse(target);
7690
7897
  if (file) return file;
7691
7898
  }
7692
- const index = await readFileResponse(join18(root, "index.html"));
7899
+ const index = await readFileResponse(join19(root, "index.html"));
7693
7900
  if (index) return index;
7694
7901
  return c.notFound();
7695
7902
  });