@xndrjs/i18n 0.8.1 → 0.8.2-alpha.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.
Files changed (93) hide show
  1. package/README.md +1 -2
  2. package/dist/cli/audit.js +739 -0
  3. package/dist/cli/audit.js.map +1 -0
  4. package/dist/cli/codegen.js +1471 -0
  5. package/dist/cli/codegen.js.map +1 -0
  6. package/dist/cli/setup.js +148 -0
  7. package/dist/cli/setup.js.map +1 -0
  8. package/dist/codegen/index.js +43 -40
  9. package/dist/codegen/index.js.map +1 -1
  10. package/package.json +6 -9
  11. package/bin/audit.mjs +0 -14
  12. package/bin/codegen.mjs +0 -17
  13. package/bin/setup.mjs +0 -14
  14. package/src/IcuTranslationProviderMulti.test.ts +0 -215
  15. package/src/IcuTranslationProviderMulti.ts +0 -188
  16. package/src/audit/audit-dictionaries.test.ts +0 -134
  17. package/src/audit/audit-dictionaries.ts +0 -178
  18. package/src/audit/run-audit.test.ts +0 -169
  19. package/src/audit/run-audit.ts +0 -94
  20. package/src/builder-load-registry.test.ts +0 -44
  21. package/src/builder-load-registry.ts +0 -78
  22. package/src/builder-loaders.ts +0 -15
  23. package/src/builder-types.test.ts +0 -57
  24. package/src/builder-types.ts +0 -24
  25. package/src/builder.test.ts +0 -303
  26. package/src/builder.ts +0 -15
  27. package/src/codegen/codegen-config-schema.ts +0 -156
  28. package/src/codegen/config.test.ts +0 -231
  29. package/src/codegen/config.ts +0 -42
  30. package/src/codegen/constants.ts +0 -9
  31. package/src/codegen/delivery-artifacts.test.ts +0 -167
  32. package/src/codegen/delivery-artifacts.ts +0 -154
  33. package/src/codegen/dictionary-spec-contract.test.ts +0 -28
  34. package/src/codegen/dictionary-spec-contract.ts +0 -138
  35. package/src/codegen/emit/dictionary-schema-file.ts +0 -113
  36. package/src/codegen/emit/instance-file.test.ts +0 -71
  37. package/src/codegen/emit/instance-file.ts +0 -257
  38. package/src/codegen/emit/namespace-loaders-file.test.ts +0 -251
  39. package/src/codegen/emit/namespace-loaders-file.ts +0 -229
  40. package/src/codegen/emit/types-file.test.ts +0 -96
  41. package/src/codegen/emit/types-file.ts +0 -142
  42. package/src/codegen/fixtures/billing-namespace.json +0 -6
  43. package/src/codegen/generate-i18n-types.test.ts +0 -1137
  44. package/src/codegen/generate-i18n-types.ts +0 -20
  45. package/src/codegen/icu-analysis.ts +0 -110
  46. package/src/codegen/locale-fallback.ts +0 -73
  47. package/src/codegen/locale-policy.test.ts +0 -35
  48. package/src/codegen/locale-policy.ts +0 -30
  49. package/src/codegen/paths.ts +0 -44
  50. package/src/codegen/project-locales-set-namespace.test.ts +0 -134
  51. package/src/codegen/read-dictionary.test.ts +0 -594
  52. package/src/codegen/read-dictionary.ts +0 -260
  53. package/src/codegen/regenerate-namespaces.ts +0 -180
  54. package/src/codegen/run-codegen.test.ts +0 -230
  55. package/src/codegen/run-codegen.ts +0 -252
  56. package/src/codegen/types.ts +0 -10
  57. package/src/codegen/write-file-if-changed.test.ts +0 -42
  58. package/src/codegen/write-file-if-changed.ts +0 -20
  59. package/src/codegen-config/build-config.ts +0 -15
  60. package/src/codegen-config/codegen-config.test.ts +0 -36
  61. package/src/codegen-config/index.ts +0 -37
  62. package/src/codegen-config/type-names.ts +0 -20
  63. package/src/codegen-config/write-config.ts +0 -8
  64. package/src/engine.ts +0 -47
  65. package/src/fetch-artifact.ts +0 -16
  66. package/src/format-core.ts +0 -91
  67. package/src/i18n-handle.ts +0 -216
  68. package/src/icu/extract-variables.test.ts +0 -199
  69. package/src/icu/extract-variables.ts +0 -227
  70. package/src/icu/parse-template.ts +0 -31
  71. package/src/index.ts +0 -63
  72. package/src/load-state.ts +0 -15
  73. package/src/project-locales.test.ts +0 -280
  74. package/src/project-locales.ts +0 -211
  75. package/src/resolve-locale.test.ts +0 -91
  76. package/src/resolve-locale.ts +0 -88
  77. package/src/scope-multi.ts +0 -105
  78. package/src/scope-types.ts +0 -35
  79. package/src/scope.test.ts +0 -172
  80. package/src/serialized-state.test.ts +0 -114
  81. package/src/serialized-state.ts +0 -28
  82. package/src/setup/setup-i18n.test.ts +0 -85
  83. package/src/setup/setup-i18n.ts +0 -156
  84. package/src/types.ts +0 -65
  85. package/src/validation/create-args-schema.ts +0 -76
  86. package/src/validation/create-normalized-schema.ts +0 -52
  87. package/src/validation/errors.ts +0 -43
  88. package/src/validation/index.ts +0 -202
  89. package/src/validation/normalize.ts +0 -299
  90. package/src/validation/to-dictionary.ts +0 -67
  91. package/src/validation/types.ts +0 -82
  92. package/src/validation/validate-normalized.ts +0 -133
  93. package/src/validation/validation.test.ts +0 -562
@@ -0,0 +1,1471 @@
1
+ #!/usr/bin/env node
2
+ import path5 from 'path';
3
+ import fs2 from 'fs';
4
+ import { z } from 'zod';
5
+ import { parse } from '@formatjs/icu-messageformat-parser';
6
+ import { parse as parse$1 } from 'yaml';
7
+
8
+ // src/codegen/constants.ts
9
+ var IDENTIFIER_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
10
+ var IDENTIFIER_NAME_REQUIREMENT = "allowed: letters, digits, underscore; must not start with a digit";
11
+
12
+ // src/codegen/delivery-artifacts.ts
13
+ var DELIVERY_AREA_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
14
+ function getDeliveryArtifactsStructureIssues(deliveryArtifacts) {
15
+ const issues = [];
16
+ for (const [area, locales] of Object.entries(deliveryArtifacts)) {
17
+ if (!DELIVERY_AREA_NAME_PATTERN.test(area)) {
18
+ issues.push({
19
+ path: ["deliveryArtifacts", area],
20
+ message: `Invalid delivery area name "${area}": must match ${DELIVERY_AREA_NAME_PATTERN.source}`
21
+ });
22
+ }
23
+ if (locales.length === 0) {
24
+ issues.push({
25
+ path: ["deliveryArtifacts", area],
26
+ message: `Delivery area "${area}" must include at least one locale.`
27
+ });
28
+ }
29
+ }
30
+ const localeToArea = /* @__PURE__ */ new Map();
31
+ for (const [area, locales] of Object.entries(deliveryArtifacts)) {
32
+ for (const locale of locales) {
33
+ const previousArea = localeToArea.get(locale);
34
+ if (previousArea) {
35
+ issues.push({
36
+ path: ["deliveryArtifacts", area],
37
+ message: `Locale "${locale}" appears in both "${previousArea}" and "${area}".`
38
+ });
39
+ } else {
40
+ localeToArea.set(locale, area);
41
+ }
42
+ }
43
+ }
44
+ return issues;
45
+ }
46
+ function getDeliveryArtifactsPartitionIssues(deliveryArtifacts, requestLocales) {
47
+ const issues = [];
48
+ const artifactLocales = /* @__PURE__ */ new Set();
49
+ for (const locales of Object.values(deliveryArtifacts)) {
50
+ for (const locale of locales) {
51
+ artifactLocales.add(locale);
52
+ }
53
+ }
54
+ const missing = [...requestLocales].filter((locale) => !artifactLocales.has(locale)).sort();
55
+ if (missing.length > 0) {
56
+ issues.push({
57
+ path: ["deliveryArtifacts"],
58
+ message: `deliveryArtifacts is missing locales required by dictionaries and localeFallback: ${missing.join(", ")}`
59
+ });
60
+ }
61
+ const excess = [...artifactLocales].filter((locale) => !requestLocales.has(locale)).sort();
62
+ if (excess.length > 0) {
63
+ issues.push({
64
+ path: ["deliveryArtifacts"],
65
+ message: `deliveryArtifacts includes locales not required by dictionaries and localeFallback: ${excess.join(", ")}`
66
+ });
67
+ }
68
+ return issues;
69
+ }
70
+ function getDeliveryArtifactsIssues(deliveryArtifacts, requestLocales) {
71
+ return [
72
+ ...getDeliveryArtifactsStructureIssues(deliveryArtifacts),
73
+ ...getDeliveryArtifactsPartitionIssues(deliveryArtifacts, requestLocales)
74
+ ];
75
+ }
76
+ function getDeliveryAreaNames(deliveryArtifacts) {
77
+ return Object.keys(deliveryArtifacts).sort();
78
+ }
79
+ function getLocaleDeliveryAreaMap(deliveryArtifacts) {
80
+ const localeToArea = {};
81
+ for (const [area, locales] of Object.entries(deliveryArtifacts)) {
82
+ for (const locale of locales) {
83
+ localeToArea[locale] = area;
84
+ }
85
+ }
86
+ return localeToArea;
87
+ }
88
+ function formatLocaleDeliveryAreaBlock(deliveryArtifacts, constName, localeTypeName, deliveryAreaTypeName) {
89
+ const localeToArea = getLocaleDeliveryAreaMap(deliveryArtifacts);
90
+ const lines = Object.keys(localeToArea).sort().map((locale) => ` ${JSON.stringify(locale)}: ${JSON.stringify(localeToArea[locale])},`).join("\n");
91
+ return `export const ${constName} = {
92
+ ${lines}
93
+ } as const satisfies Record<${localeTypeName}, ${deliveryAreaTypeName}>;
94
+
95
+ `;
96
+ }
97
+ function getDeliveryArtifactsTypeName(deliveryAreaTypeName) {
98
+ return deliveryAreaTypeName.endsWith("DeliveryArea") ? deliveryAreaTypeName.replace(/DeliveryArea$/, "DeliveryArtifacts") : `${deliveryAreaTypeName}Artifacts`;
99
+ }
100
+ function formatDeliveryArtifactsBlock(deliveryArtifacts, constName, localeTypeName, deliveryAreaTypeName) {
101
+ const deliveryArtifactsTypeName = getDeliveryArtifactsTypeName(deliveryAreaTypeName);
102
+ const areaEntries = getDeliveryAreaNames(deliveryArtifacts).map((area) => {
103
+ const locales = [...deliveryArtifacts[area]].sort().map((locale) => JSON.stringify(locale)).join(", ");
104
+ return ` ${JSON.stringify(area)}: [${locales}] as const,`;
105
+ }).join("\n");
106
+ return `export const ${constName} = {
107
+ ${areaEntries}
108
+ } as const satisfies Record<${deliveryAreaTypeName}, readonly ${localeTypeName}[]>;
109
+
110
+ export type ${deliveryArtifactsTypeName} = typeof ${constName};
111
+
112
+ export type LocalesForDeliveryArea<A extends ${deliveryAreaTypeName}> = ${deliveryArtifactsTypeName}[A][number];
113
+
114
+ `;
115
+ }
116
+
117
+ // src/codegen-config/type-names.ts
118
+ function typeNamesForProject(project) {
119
+ return {
120
+ paramsTypeName: `${project}Params`,
121
+ schemaTypeName: `${project}Schema`,
122
+ localeTypeName: `${project}Locale`
123
+ };
124
+ }
125
+
126
+ // src/codegen/codegen-config-schema.ts
127
+ var localeFallbackSchema = z.record(z.string(), z.union([z.string(), z.null()]));
128
+ var deliveryArtifactsSchema = z.record(z.string(), z.array(z.string().min(1)));
129
+ var DELIVERY_MODES = ["split-by-locale", "custom"];
130
+ var LOADER_STRATEGIES = ["import", "fetch"];
131
+ var PROJECT_NAME_PATTERN = /^[A-Z][a-zA-Z0-9]*$/;
132
+ var codegenConfigShape = {
133
+ /** PascalCase project id — generates `{project}Params`, `{project}Schema`, `{project}Locale`. */
134
+ projectName: z.string().min(1).regex(PROJECT_NAME_PATTERN, 'projectName must be PascalCase (e.g. "MyApp")'),
135
+ namespaces: z.record(z.string(), z.string().min(1)),
136
+ /**
137
+ * Directory for generated TypeScript modules:
138
+ * `i18n-types.generated.ts`, `instance.generated.ts`,
139
+ * `namespace-loaders.generated.ts`, `dictionary-schema.generated.ts`.
140
+ */
141
+ codegenPath: z.string().min(1),
142
+ localeFallback: localeFallbackSchema.optional(),
143
+ delivery: z.enum(DELIVERY_MODES).optional().default("split-by-locale"),
144
+ deliveryArtifacts: deliveryArtifactsSchema.optional(),
145
+ /** Directory for delivery JSON (`translations/`). Defaults to {@link codegenPath}. */
146
+ artifactsPath: z.string().min(1).optional(),
147
+ /**
148
+ * How generated `namespaceLoaders` resolve artifacts.
149
+ * - `import` (default): dynamic `import()` — JSON is bundled; content updates need a rebuild.
150
+ * - `fetch`: runtime `fetchImpl({ locale, namespace, area? })` via required `createI18n({ fetchImpl })`.
151
+ * Codegen does not know URLs — mapping id → transport is an application concern.
152
+ */
153
+ loaderStrategy: z.enum(LOADER_STRATEGIES).optional().default("import")
154
+ };
155
+ var codegenConfigKeys = Object.keys(
156
+ codegenConfigShape
157
+ );
158
+ var codegenConfigSchema = z.object(codegenConfigShape).strict().superRefine((config, ctx) => {
159
+ for (const namespace of Object.keys(config.namespaces)) {
160
+ if (!IDENTIFIER_NAME_PATTERN.test(namespace)) {
161
+ ctx.addIssue({
162
+ code: "custom",
163
+ path: ["namespaces", namespace],
164
+ message: `Invalid namespace name "${namespace}" (${IDENTIFIER_NAME_REQUIREMENT}).`
165
+ });
166
+ }
167
+ }
168
+ if (config.delivery === "custom") {
169
+ if (!config.deliveryArtifacts) {
170
+ ctx.addIssue({
171
+ code: "custom",
172
+ path: ["deliveryArtifacts"],
173
+ message: 'deliveryArtifacts is required when delivery is "custom".'
174
+ });
175
+ } else {
176
+ for (const issue of getDeliveryArtifactsStructureIssues(config.deliveryArtifacts)) {
177
+ ctx.addIssue({
178
+ code: "custom",
179
+ path: issue.path,
180
+ message: issue.message
181
+ });
182
+ }
183
+ }
184
+ } else if (config.deliveryArtifacts !== void 0) {
185
+ ctx.addIssue({
186
+ code: "custom",
187
+ path: ["deliveryArtifacts"],
188
+ message: 'deliveryArtifacts is only allowed when delivery is "custom".'
189
+ });
190
+ }
191
+ });
192
+ var GENERATED_BASENAMES = {
193
+ types: "i18n-types.generated.ts",
194
+ instance: "instance.generated.ts",
195
+ namespaceLoaders: "namespace-loaders.generated.ts",
196
+ dictionarySchema: "dictionary-schema.generated.ts"
197
+ };
198
+ var DEFAULT_FACTORY_NAME = "createI18n";
199
+ var DEFAULT_LOCALE_FALLBACK_CONST_NAME = "LOCALE_FALLBACK";
200
+ function resolveCodegenPaths(config) {
201
+ const typeNames = typeNamesForProject(config.projectName);
202
+ const codegenPath = config.codegenPath;
203
+ return {
204
+ codegenPath,
205
+ typesOutput: path5.join(codegenPath, GENERATED_BASENAMES.types),
206
+ instanceOutput: path5.join(codegenPath, GENERATED_BASENAMES.instance),
207
+ namespaceLoadersOutput: path5.join(codegenPath, GENERATED_BASENAMES.namespaceLoaders),
208
+ dictionarySchemaOutput: path5.join(codegenPath, GENERATED_BASENAMES.dictionarySchema),
209
+ artifactsPath: config.artifactsPath ?? codegenPath,
210
+ paramsTypeName: typeNames.paramsTypeName,
211
+ schemaTypeName: typeNames.schemaTypeName,
212
+ localeTypeName: typeNames.localeTypeName,
213
+ localeFallbackConstName: DEFAULT_LOCALE_FALLBACK_CONST_NAME,
214
+ factoryName: DEFAULT_FACTORY_NAME,
215
+ deliveryAreaTypeName: `${config.projectName}DeliveryArea`
216
+ };
217
+ }
218
+ function resolveArtifactsPath(config) {
219
+ return config.artifactsPath ?? config.codegenPath;
220
+ }
221
+ function formatCodegenConfigIssues(error) {
222
+ const issueLines = error.issues.map((issue) => {
223
+ const path9 = issue.path.length > 0 ? issue.path.join(".") : "(root)";
224
+ return ` - ${path9}: ${issue.message}`;
225
+ });
226
+ return [
227
+ "[Codegen Error] Invalid i18n.codegen.json:",
228
+ ...issueLines,
229
+ "",
230
+ `Allowed keys: ${codegenConfigKeys.join(", ")}`
231
+ ].join("\n");
232
+ }
233
+
234
+ // src/codegen/config.ts
235
+ function loadConfig(configPath) {
236
+ let raw;
237
+ try {
238
+ raw = JSON.parse(fs2.readFileSync(configPath, "utf8"));
239
+ } catch (error) {
240
+ const message = error instanceof Error ? error.message : String(error);
241
+ throw new Error(`[Codegen Error] Failed to parse config JSON (${configPath}): ${message}`);
242
+ }
243
+ const result = codegenConfigSchema.safeParse(raw);
244
+ if (!result.success) {
245
+ throw new Error(formatCodegenConfigIssues(result.error));
246
+ }
247
+ return result.data;
248
+ }
249
+ function resolveNamespaces(config) {
250
+ return Object.entries(config.namespaces).map(([namespace, filePath]) => ({
251
+ namespace,
252
+ filePath
253
+ }));
254
+ }
255
+ var GENERATED_FILE_BANNER = "// Automatically generated code. Do not edit manually.\n";
256
+ var DEFAULT_IMPORT_EXTENSION = "none";
257
+ function relativePosixPath(from, to) {
258
+ return path5.relative(from, to).replace(/\\/g, "/");
259
+ }
260
+ function reportCodegenIssues(issues) {
261
+ for (const issue of issues) {
262
+ console.error(`[Codegen Error] ${issue.message}`);
263
+ }
264
+ }
265
+ function toImportPath(fromFile, toFile) {
266
+ const relative = path5.relative(path5.dirname(fromFile), toFile).replace(/\\/g, "/");
267
+ const withoutExt = relative.replace(/\.(json|ya?ml)$/i, "");
268
+ return withoutExt.startsWith(".") ? withoutExt : `./${withoutExt}`;
269
+ }
270
+ function toModuleBasename(filePath) {
271
+ return path5.basename(filePath).replace(/\.ts$/, "");
272
+ }
273
+ function importExtensionSuffix(importExtension) {
274
+ return importExtension === "none" ? "" : importExtension;
275
+ }
276
+ function toRelativeModuleImport(moduleBasename, importExtension) {
277
+ return `./${moduleBasename}${importExtensionSuffix(importExtension)}`;
278
+ }
279
+
280
+ // src/codegen/emit/dictionary-schema-file.ts
281
+ function formatVariableSpecObject(spec) {
282
+ const entries = Object.entries(spec);
283
+ if (entries.length === 0) {
284
+ return "{}";
285
+ }
286
+ const lines = entries.map(
287
+ ([name, type]) => ` ${JSON.stringify(name)}: ${JSON.stringify(type)},`
288
+ );
289
+ return `{
290
+ ${lines.join("\n")}
291
+ }`;
292
+ }
293
+ function formatDictionarySpecBlock(entries, argsSpecByNamespace) {
294
+ const requiredKeysLines = entries.map((entry) => {
295
+ const keys = Object.keys(argsSpecByNamespace[entry.namespace] ?? {}).map((key) => JSON.stringify(key)).join(", ");
296
+ return ` ${JSON.stringify(entry.namespace)}: [${keys}] as const,`;
297
+ }).join("\n");
298
+ const argsByKeyLines = entries.map((entry) => {
299
+ const argsByKey = argsSpecByNamespace[entry.namespace] ?? {};
300
+ const keyLines = Object.entries(argsByKey).map(([key, spec]) => ` ${JSON.stringify(key)}: ${formatVariableSpecObject(spec)},`).join("\n");
301
+ return ` ${JSON.stringify(entry.namespace)}: {
302
+ ${keyLines}
303
+ },`;
304
+ }).join("\n");
305
+ return `export const DICTIONARY_SPEC = {
306
+ mode: 'multi' as const,
307
+ requiredKeys: {
308
+ ${requiredKeysLines}
309
+ },
310
+ argsByKey: {
311
+ ${argsByKeyLines}
312
+ },
313
+ } satisfies DictionarySpec;
314
+ `;
315
+ }
316
+ function formatDictionarySchemaFile(schemaTypeName, typesModule, dictionarySpecBlock, importExtension) {
317
+ const typesImport = toRelativeModuleImport(typesModule, importExtension);
318
+ const partialImport = ` validateExternalDictionaryPartial as validateExternalDictionaryPartialCore,
319
+ validateExternalNamespacePartial as validateExternalNamespacePartialCore,
320
+ validateExternalKey as validateExternalKeyCore,
321
+ `;
322
+ const multiValidators = `export function validateExternalNamespacePartial<NS extends keyof ${schemaTypeName}>(
323
+ namespace: NS,
324
+ input: unknown,
325
+ ): ValidationResult<Partial<${schemaTypeName}[NS]>> {
326
+ return validateExternalNamespacePartialCore<Partial<${schemaTypeName}[NS]>>(
327
+ namespace as string,
328
+ input,
329
+ DICTIONARY_SPEC,
330
+ );
331
+ }
332
+
333
+ export function validateExternalKey<
334
+ NS extends keyof ${schemaTypeName},
335
+ K extends keyof ${schemaTypeName}[NS],
336
+ >(
337
+ namespace: NS,
338
+ key: K,
339
+ input: unknown,
340
+ ): ValidationResult<Pick<${schemaTypeName}[NS], K>> {
341
+ return validateExternalKeyCore<Pick<${schemaTypeName}[NS], K>>(
342
+ namespace as string,
343
+ key as string,
344
+ input,
345
+ DICTIONARY_SPEC,
346
+ );
347
+ }
348
+
349
+ `;
350
+ return `${GENERATED_FILE_BANNER}import {
351
+ ` + partialImport + ` type DictionarySpec,
352
+ type ValidationResult,
353
+ } from '@xndrjs/i18n/validation';
354
+ import type { ${schemaTypeName} } from '${typesImport}';
355
+
356
+ ${dictionarySpecBlock}
357
+ export function validateExternalDictionaryPartial(
358
+ input: unknown,
359
+ ): ValidationResult<Partial<${schemaTypeName}>> {
360
+ return validateExternalDictionaryPartialCore<Partial<${schemaTypeName}>>(
361
+ input,
362
+ DICTIONARY_SPEC,
363
+ );
364
+ }
365
+
366
+ ` + multiValidators;
367
+ }
368
+
369
+ // src/codegen/emit/instance-file.ts
370
+ var STATE_TYPE = `{ dictionary: InitialSchema; resources?: readonly (readonly [string, string])[] }`;
371
+ function formatInstanceFile(options) {
372
+ const {
373
+ typesOutputPath,
374
+ namespaceLoadersOutputPath,
375
+ paramsTypeName,
376
+ schemaTypeName,
377
+ localeTypeName,
378
+ localeFallbackConstName,
379
+ factoryName,
380
+ hasLocaleFallback,
381
+ hasLocaleType,
382
+ importExtension,
383
+ delivery,
384
+ localeDeliveryAreaConstName = "LOCALE_DELIVERY_AREA",
385
+ loaderStrategy = "import"
386
+ } = options;
387
+ const providerClass = "IcuTranslationProviderMulti";
388
+ const typesModule = toModuleBasename(typesOutputPath);
389
+ const typesImport = toRelativeModuleImport(typesModule, importExtension);
390
+ const loadersModule = toModuleBasename(namespaceLoadersOutputPath);
391
+ const loadersImport = toRelativeModuleImport(loadersModule, importExtension);
392
+ const schemaTypesImport = `import type { ${paramsTypeName}, ${schemaTypeName}, InitialSchema } from '${typesImport}';
393
+ `;
394
+ const typesImportLine = formatTypesValueImport({
395
+ hasLocaleType,
396
+ hasLocaleFallback,
397
+ localeTypeName,
398
+ localeFallbackConstName,
399
+ localeDeliveryAreaConstName,
400
+ delivery,
401
+ typesImport
402
+ });
403
+ const packageImports = formatPackageImports(providerClass, loaderStrategy);
404
+ const loadersImportLine = loaderStrategy === "fetch" ? `import { createNamespaceLoaders } from '${loadersImport}';
405
+ ` : `import { namespaceLoaders } from '${loadersImport}';
406
+ `;
407
+ const createFactoryBlock = formatCreateI18nFactory({
408
+ providerClass,
409
+ schemaTypeName,
410
+ paramsTypeName,
411
+ localeTypeName,
412
+ localeFallbackConstName,
413
+ factoryName,
414
+ hasLocaleFallback,
415
+ delivery,
416
+ localeDeliveryAreaConstName,
417
+ loaderStrategy
418
+ });
419
+ return `${GENERATED_FILE_BANNER}import {
420
+ ${packageImports},
421
+ } from '@xndrjs/i18n';
422
+ ` + schemaTypesImport + typesImportLine + loadersImportLine + `
423
+ ` + createFactoryBlock;
424
+ }
425
+ function formatTypesValueImport(options) {
426
+ const {
427
+ hasLocaleType,
428
+ hasLocaleFallback,
429
+ localeTypeName,
430
+ localeFallbackConstName,
431
+ localeDeliveryAreaConstName,
432
+ delivery,
433
+ typesImport
434
+ } = options;
435
+ if (!hasLocaleType && delivery !== "custom") {
436
+ return "";
437
+ }
438
+ const valueNames = [];
439
+ if (hasLocaleFallback) {
440
+ valueNames.push(localeFallbackConstName);
441
+ }
442
+ if (delivery === "custom") {
443
+ valueNames.push(localeDeliveryAreaConstName);
444
+ }
445
+ if (valueNames.length > 0 && hasLocaleType) {
446
+ return `import { ${valueNames.join(", ")}, type ${localeTypeName} } from '${typesImport}';
447
+ `;
448
+ }
449
+ if (valueNames.length > 0) {
450
+ return `import { ${valueNames.join(", ")} } from '${typesImport}';
451
+ `;
452
+ }
453
+ if (hasLocaleType) {
454
+ return `import type { ${localeTypeName} } from '${typesImport}';
455
+ `;
456
+ }
457
+ return "";
458
+ }
459
+ function formatCreateI18nFactory(options) {
460
+ const {
461
+ providerClass,
462
+ schemaTypeName,
463
+ paramsTypeName,
464
+ localeTypeName,
465
+ localeFallbackConstName,
466
+ factoryName,
467
+ hasLocaleFallback,
468
+ delivery,
469
+ localeDeliveryAreaConstName,
470
+ loaderStrategy
471
+ } = options;
472
+ const providerTypeArgs = hasLocaleFallback ? `${schemaTypeName}, ${paramsTypeName}, ${localeTypeName}, typeof ${localeFallbackConstName}` : `${schemaTypeName}, ${paramsTypeName}`;
473
+ const handleReturnType = `I18nHandle<${schemaTypeName}, ${paramsTypeName}, ${localeTypeName}>`;
474
+ const handleTypeArgs = `<${schemaTypeName}, ${paramsTypeName}, ${localeTypeName}>`;
475
+ const partitionForLocale = delivery === "custom" ? ` partitionForLocale: (locale) => ${localeDeliveryAreaConstName}[locale],
476
+ ` : ` partitionForLocale: (locale) => locale,
477
+ `;
478
+ const optionsType = loaderStrategy === "fetch" ? `{ fetchImpl: FetchArtifact; state?: ${STATE_TYPE}; onMissing?: OnMissingTranslation }` : `{ state?: ${STATE_TYPE}; onMissing?: OnMissingTranslation }`;
479
+ const optionsParam = loaderStrategy === "fetch" ? ` options: ${optionsType},
480
+ ` : ` options?: ${optionsType},
481
+ `;
482
+ const namespaceLoadersLine = loaderStrategy === "fetch" ? ` namespaceLoaders: createNamespaceLoaders(fetchImpl),
483
+ ` : ` namespaceLoaders,
484
+ `;
485
+ return `export function ${factoryName}(
486
+ ` + optionsParam + `): ${handleReturnType} {
487
+ ` + formatBody({
488
+ providerClass,
489
+ providerTypeArgs,
490
+ hasLocaleFallback,
491
+ localeFallbackConstName,
492
+ loaderStrategy
493
+ }) + ` engine.seedBuilderResources(normalized.resources);
494
+ return createI18nHandle${handleTypeArgs}(engine, {
495
+ ` + namespaceLoadersLine + partitionForLocale + ` } as I18nHandleOptions<${schemaTypeName}, ${localeTypeName}>);
496
+ }
497
+ `;
498
+ }
499
+ function formatBody(options) {
500
+ const {
501
+ providerClass,
502
+ providerTypeArgs,
503
+ hasLocaleFallback,
504
+ localeFallbackConstName,
505
+ loaderStrategy
506
+ } = options;
507
+ if (loaderStrategy === "fetch") {
508
+ const engineBlock2 = hasLocaleFallback ? ` const engine = new ${providerClass}<${providerTypeArgs}>(normalized.dictionary, {
509
+ localeFallback: ${localeFallbackConstName},
510
+ ...providerOptions,
511
+ });
512
+ ` : ` const engine = new ${providerClass}<${providerTypeArgs}>(normalized.dictionary, providerOptions);
513
+ `;
514
+ return ` const { fetchImpl, state, ...providerOptions } = options;
515
+ const normalized = normalizeI18nCreateInput(state);
516
+ ` + engineBlock2;
517
+ }
518
+ const engineBlock = hasLocaleFallback ? ` const engine = new ${providerClass}<${providerTypeArgs}>(normalized.dictionary, {
519
+ localeFallback: ${localeFallbackConstName},
520
+ ...providerOptions,
521
+ });
522
+ ` : ` const engine = new ${providerClass}<${providerTypeArgs}>(normalized.dictionary, providerOptions);
523
+ `;
524
+ return ` const { state, ...providerOptions } = options ?? {};
525
+ const normalized = normalizeI18nCreateInput(state);
526
+ ` + engineBlock;
527
+ }
528
+ function formatPackageImports(providerClass, loaderStrategy) {
529
+ const names = [
530
+ providerClass,
531
+ "createI18nHandle",
532
+ "normalizeI18nCreateInput",
533
+ "type I18nHandle",
534
+ "type I18nHandleOptions",
535
+ "type OnMissingTranslation"
536
+ ];
537
+ if (loaderStrategy === "fetch") {
538
+ names.push("type FetchArtifact");
539
+ }
540
+ return names.join(",\n ");
541
+ }
542
+ function formatDefaultNamespacesLiteral(lazyEntries) {
543
+ return [...lazyEntries].map((entry) => entry.namespace).sort().map((namespace) => JSON.stringify(namespace)).join(", ");
544
+ }
545
+ function formatPartitionedTypesImport(options, paramTypeName) {
546
+ const { schemaTypeName, typesModule, importExtension } = options;
547
+ return `import type { ${schemaTypeName}, LazyNamespace, ${paramTypeName} } from '${toRelativeModuleImport(typesModule, importExtension)}';
548
+
549
+ `;
550
+ }
551
+ function formatImportCase(loadersOutputPath, projectRoot, splitRelativePath) {
552
+ const importPath = toImportPath(loadersOutputPath, path5.resolve(projectRoot, splitRelativePath));
553
+ return `return import('${importPath}.json').then((m) => m.default);`;
554
+ }
555
+ function indentBlock(text, spaces) {
556
+ const pad = " ".repeat(spaces);
557
+ return text.split("\n").map((line) => line.length === 0 ? line : `${pad}${line}`).join("\n");
558
+ }
559
+ function formatImportNamespaceLoaderEntry(options) {
560
+ const {
561
+ namespace,
562
+ paramName,
563
+ partitionKeys,
564
+ splitPathsByNamespace,
565
+ loadersOutputPath,
566
+ projectRoot
567
+ } = options;
568
+ const caseBodies = partitionKeys.map((partitionKey) => {
569
+ const splitRelativePath = splitPathsByNamespace[namespace]?.[partitionKey];
570
+ if (!splitRelativePath) {
571
+ throw new Error(
572
+ `[Codegen Error] Missing split path for namespace "${namespace}", ${paramName} "${partitionKey}".`
573
+ );
574
+ }
575
+ const body = formatImportCase(loadersOutputPath, projectRoot, splitRelativePath);
576
+ return [`case ${JSON.stringify(partitionKey)}:`, ` ${body}`].join("\n");
577
+ });
578
+ const switchBody = [
579
+ ...caseBodies,
580
+ `default:`,
581
+ ` throw new Error(\`[i18n] No translation artifact for namespace ${JSON.stringify(namespace)} and ${paramName} "\${String(${paramName})}".\`);`
582
+ ].join("\n");
583
+ const entry = [
584
+ `${namespace}: (${paramName}) => {`,
585
+ ` switch (${paramName}) {`,
586
+ indentBlock(switchBody, 4),
587
+ ` }`,
588
+ `},`
589
+ ].join("\n");
590
+ return indentBlock(entry, 2);
591
+ }
592
+ function formatFetchNamespaceLoaderEntry(options) {
593
+ const { namespace, paramName, schemaTypeName, delivery } = options;
594
+ const nsLiteral = JSON.stringify(namespace);
595
+ const resourceId = delivery === "custom" ? `{ locale, namespace: ${nsLiteral}, area }` : `{ locale, namespace: ${nsLiteral} }`;
596
+ const signature = delivery === "custom" ? `(${paramName}, { locale })` : `(${paramName})`;
597
+ return indentBlock(
598
+ `${namespace}: ${signature} =>
599
+ fetchImpl(${resourceId}) as Promise<${schemaTypeName}[${nsLiteral}]>,`,
600
+ 4
601
+ );
602
+ }
603
+ function formatPartitionedNamespaceLoadersFile(options, params) {
604
+ const { partitionKeys, paramName, paramTypeName } = params;
605
+ const {
606
+ loadersOutputPath,
607
+ lazyEntries,
608
+ schemaTypeName,
609
+ projectRoot,
610
+ delivery,
611
+ splitPathsByNamespace = {}
612
+ } = options;
613
+ const loaderStrategy = options.loaderStrategy ?? "import";
614
+ const loaderValueType = `${schemaTypeName}[K]`;
615
+ const typesImport = formatPartitionedTypesImport(options, paramTypeName);
616
+ if (loaderStrategy === "fetch") {
617
+ const fetchLoadersType = `{
618
+ [K in LazyNamespace]: (${delivery === "custom" ? `${paramName}: ${paramTypeName}, context: { locale: string }` : `${paramName}: ${paramTypeName}`}) => Promise<${loaderValueType}>;
619
+ }`;
620
+ const loaderEntries2 = lazyEntries.map(
621
+ (entry) => formatFetchNamespaceLoaderEntry({
622
+ namespace: entry.namespace,
623
+ paramName,
624
+ schemaTypeName,
625
+ delivery
626
+ })
627
+ ).join("\n");
628
+ return `${GENERATED_FILE_BANNER}import type { FetchArtifact } from '@xndrjs/i18n';
629
+ ` + typesImport + `export type NamespaceLoaders = ${fetchLoadersType};
630
+
631
+ /** Build loaders that resolve artifacts via the injected {@link FetchArtifact} (resource id only). */
632
+ export function createNamespaceLoaders(fetchImpl: FetchArtifact): NamespaceLoaders {
633
+ return {
634
+ ${loaderEntries2}
635
+ };
636
+ }
637
+
638
+ export const defaultLazyNamespaces = [${formatDefaultNamespacesLiteral(lazyEntries)}] as const;
639
+ `;
640
+ }
641
+ const loaderEntries = lazyEntries.map(
642
+ (entry) => formatImportNamespaceLoaderEntry({
643
+ namespace: entry.namespace,
644
+ paramName,
645
+ partitionKeys,
646
+ splitPathsByNamespace,
647
+ loadersOutputPath,
648
+ projectRoot
649
+ })
650
+ ).join("\n");
651
+ const importLoadersType = `{
652
+ [K in LazyNamespace]: (${paramName}: ${paramTypeName}) => Promise<${loaderValueType}>;
653
+ }`;
654
+ return `${GENERATED_FILE_BANNER}` + typesImport + `export const namespaceLoaders: ${importLoadersType} = {
655
+ ${loaderEntries}
656
+ };
657
+
658
+ export const defaultLazyNamespaces = [${formatDefaultNamespacesLiteral(lazyEntries)}] as const;
659
+ `;
660
+ }
661
+ function formatNamespaceLoadersFile(options) {
662
+ if (options.delivery === "split-by-locale") {
663
+ return formatPartitionedNamespaceLoadersFile(options, {
664
+ partitionKeys: options.requestLocales ?? [],
665
+ paramName: "locale",
666
+ paramTypeName: options.localeTypeName
667
+ });
668
+ }
669
+ if (!options.deliveryAreaTypeName) {
670
+ throw new Error("[Codegen Error] deliveryAreaTypeName is required for custom delivery.");
671
+ }
672
+ return formatPartitionedNamespaceLoadersFile(options, {
673
+ partitionKeys: options.deliveryAreaNames ?? [],
674
+ paramName: "area",
675
+ paramTypeName: options.deliveryAreaTypeName
676
+ });
677
+ }
678
+
679
+ // src/resolve-locale.ts
680
+ function validateLocaleFallback(map) {
681
+ for (const startLocale of Object.keys(map)) {
682
+ const visited = /* @__PURE__ */ new Set();
683
+ let current = startLocale;
684
+ while (current !== void 0) {
685
+ if (visited.has(current)) {
686
+ throw new Error(`[i18n] Circular locale fallback detected involving "${current}"`);
687
+ }
688
+ visited.add(current);
689
+ const next = map[current];
690
+ if (next === void 0 || next === null) {
691
+ break;
692
+ }
693
+ current = next;
694
+ }
695
+ }
696
+ }
697
+ function resolveLocaleTemplate(localeByKey, requestedLocale, fallbackMap) {
698
+ const chain = [requestedLocale];
699
+ let currentLocale = requestedLocale;
700
+ while (true) {
701
+ const template = localeByKey?.[currentLocale];
702
+ if (template !== void 0) {
703
+ return { template, resolvedLocale: currentLocale, fallbackChain: chain };
704
+ }
705
+ if (!fallbackMap) {
706
+ return void 0;
707
+ }
708
+ const fallback = fallbackMap[currentLocale];
709
+ if (fallback === void 0 || fallback === null) {
710
+ return void 0;
711
+ }
712
+ if (chain.includes(fallback)) {
713
+ throw new Error(
714
+ `[i18n] Circular locale fallback detected: ${[...chain, fallback].join(" \u2192 ")}`
715
+ );
716
+ }
717
+ chain.push(fallback);
718
+ currentLocale = fallback;
719
+ }
720
+ }
721
+
722
+ // src/codegen/locale-fallback.ts
723
+ function collectRequestLocales(dictionaryLocales, fallback) {
724
+ const all = new Set(dictionaryLocales);
725
+ if (!fallback) {
726
+ return all;
727
+ }
728
+ for (const [locale, target] of Object.entries(fallback)) {
729
+ all.add(locale);
730
+ if (target !== null) {
731
+ all.add(target);
732
+ }
733
+ }
734
+ return all;
735
+ }
736
+ function getCodegenLocaleFallbackIssues(fallback, dictionaryLocales) {
737
+ const issues = [];
738
+ try {
739
+ validateLocaleFallback(fallback);
740
+ } catch (error) {
741
+ const message = error instanceof Error ? error.message : String(error);
742
+ issues.push({ path: ["localeFallback"], message });
743
+ }
744
+ for (const [locale, target] of Object.entries(fallback)) {
745
+ if (target !== null && !(target in fallback) && !dictionaryLocales.has(target)) {
746
+ issues.push({
747
+ path: ["localeFallback", locale],
748
+ message: `localeFallback: "${locale}" points to "${target}" which is not defined in the fallback map or dictionary locales`
749
+ });
750
+ }
751
+ }
752
+ return issues;
753
+ }
754
+ function formatLocaleFallbackBlock(fallback, constName, typeName) {
755
+ const lines = Object.entries(fallback).map(
756
+ ([locale, target]) => ` ${JSON.stringify(locale)}: ${target === null ? "null" : JSON.stringify(target)},`
757
+ ).join("\n");
758
+ return `export const ${constName} = {
759
+ ${lines}
760
+ } as const satisfies Record<string, string | null>;
761
+
762
+ export type ${typeName} = typeof ${constName};
763
+
764
+ `;
765
+ }
766
+
767
+ // src/codegen/emit/types-file.ts
768
+ function formatLazyTypesBlock(lazyEntries) {
769
+ const lazyUnion = lazyEntries.length > 0 ? lazyEntries.map((entry) => `'${entry.namespace}'`).join(" | ") : "never";
770
+ return `export type LazyNamespace = ${lazyUnion};
771
+ /** Empty cold-start schema \u2014 namespaces arrive via \`namespaceLoaders\`. */
772
+ export type InitialSchema = Record<string, never>;
773
+
774
+ `;
775
+ }
776
+ function formatLocaleTemplateType(localeTypeName, hasLocaleUnion) {
777
+ if (!hasLocaleUnion) {
778
+ return "Partial<Record<string, string>>";
779
+ }
780
+ return `Partial<Record<${localeTypeName}, string>>`;
781
+ }
782
+ function formatTypesFile(options) {
783
+ const {
784
+ entries,
785
+ paramsTypeName,
786
+ schemaTypeName,
787
+ localeTypeName,
788
+ localeFallbackConstName,
789
+ localeFallbackTypeName,
790
+ localeFallback,
791
+ paramsByNamespace,
792
+ requestLocales,
793
+ deliveryAreaTypeName,
794
+ deliveryAreaNames,
795
+ deliveryArtifacts,
796
+ localeDeliveryAreaConstName = "LOCALE_DELIVERY_AREA",
797
+ lazyEntries
798
+ } = options;
799
+ const hasLocaleUnion = requestLocales.length > 0;
800
+ const localeTemplateType = formatLocaleTemplateType(localeTypeName, hasLocaleUnion);
801
+ const localesConstName = `${localeTypeName}s`;
802
+ const localeBlock = hasLocaleUnion ? `${localeFallback ? formatLocaleFallbackBlock(localeFallback, localeFallbackConstName, localeFallbackTypeName) : ""}export const ${localesConstName} = [${requestLocales.map((locale) => JSON.stringify(locale)).join(", ")}] as const;
803
+ export type ${localeTypeName} = (typeof ${localesConstName})[number];
804
+
805
+ ` : "";
806
+ const deliveryAreasConstName = deliveryAreaTypeName ? `${deliveryAreaTypeName}s` : void 0;
807
+ const deliveryAreaBlock = deliveryAreaTypeName && deliveryAreasConstName && deliveryAreaNames && deliveryAreaNames.length > 0 ? `export const ${deliveryAreasConstName} = [${deliveryAreaNames.map((area) => JSON.stringify(area)).join(", ")}] as const;
808
+ export type ${deliveryAreaTypeName} = (typeof ${deliveryAreasConstName})[number];
809
+
810
+ ` + (deliveryArtifacts ? formatDeliveryArtifactsBlock(
811
+ deliveryArtifacts,
812
+ "DELIVERY_ARTIFACTS",
813
+ localeTypeName,
814
+ deliveryAreaTypeName
815
+ ) + formatLocaleDeliveryAreaBlock(
816
+ deliveryArtifacts,
817
+ localeDeliveryAreaConstName,
818
+ localeTypeName,
819
+ deliveryAreaTypeName
820
+ ) : "") : "";
821
+ const namespaceBlocks = entries.map((entry) => {
822
+ const keyTypes = paramsByNamespace[entry.namespace] ?? {};
823
+ const lines = Object.entries(keyTypes).map(([key, type]) => ` ${key}: ${type};`).join("\n");
824
+ return ` ${entry.namespace}: {
825
+ ${lines}
826
+ };`;
827
+ }).join("\n");
828
+ const paramsBlock = `export type ${paramsTypeName} = {
829
+ ${namespaceBlocks}
830
+ };`;
831
+ const schemaLines = entries.map((entry) => {
832
+ const keyTypes = paramsByNamespace[entry.namespace] ?? {};
833
+ const lines = Object.keys(keyTypes).map((key) => ` ${key}: ${localeTemplateType};`).join("\n");
834
+ return ` ${entry.namespace}: {
835
+ ${lines}
836
+ };`;
837
+ }).join("\n");
838
+ const schemaBlock = `export type ${schemaTypeName} = {
839
+ ${schemaLines}
840
+ };`;
841
+ const lazyTypesBlock = formatLazyTypesBlock(lazyEntries);
842
+ return `${GENERATED_FILE_BANNER}export const I18N_MODE = 'multi' as const;
843
+
844
+ ${localeBlock}${deliveryAreaBlock}${paramsBlock}
845
+
846
+ ${schemaBlock}
847
+ ${lazyTypesBlock}`;
848
+ }
849
+
850
+ // src/icu/extract-variables.ts
851
+ function isPluralOrOrdinalNode(node) {
852
+ return node.type === 6 && "pluralType" in node && Boolean(node.pluralType);
853
+ }
854
+ function hasVariableName(node) {
855
+ return "value" in node && typeof node.value === "string";
856
+ }
857
+ function addVariableMeta(variables, name, type, role) {
858
+ const existing = variables[name];
859
+ if (!existing) {
860
+ variables[name] = { type, roles: /* @__PURE__ */ new Set([role]) };
861
+ return;
862
+ }
863
+ existing.roles.add(role);
864
+ existing.type = mergeVariableTypes(existing.type, type);
865
+ }
866
+ function mergeVariableTypes(left, right) {
867
+ if (left === right) {
868
+ return left;
869
+ }
870
+ if (left === "string" && right === "number" || left === "number" && right === "string") {
871
+ return "number";
872
+ }
873
+ return right;
874
+ }
875
+ function extractVariableMeta(nodes) {
876
+ const variables = {};
877
+ const walk = (walkNodes) => {
878
+ for (const node of walkNodes) {
879
+ if (node.type === 1 && hasVariableName(node)) {
880
+ addVariableMeta(variables, node.value, "string", "simple");
881
+ } else if (node.type === 2 && hasVariableName(node)) {
882
+ addVariableMeta(variables, node.value, "number", "number");
883
+ } else if (isPluralOrOrdinalNode(node) && hasVariableName(node)) {
884
+ const role = node.pluralType === "ordinal" ? "selectordinal" : "plural";
885
+ addVariableMeta(variables, node.value, "number", role);
886
+ } else if (node.type === 3 && hasVariableName(node)) {
887
+ addVariableMeta(variables, node.value, "date", "date");
888
+ } else if (node.type === 4 && hasVariableName(node)) {
889
+ addVariableMeta(variables, node.value, "date", "time");
890
+ } else if (node.type === 5 && hasVariableName(node)) {
891
+ addVariableMeta(variables, node.value, "string", "select");
892
+ }
893
+ if ("options" in node && node.options) {
894
+ for (const option of Object.values(node.options)) {
895
+ walk(option.value);
896
+ }
897
+ }
898
+ if ("children" in node && Array.isArray(node.children)) {
899
+ walk(node.children);
900
+ }
901
+ }
902
+ };
903
+ walk(nodes);
904
+ return variables;
905
+ }
906
+ function inferMergedVariableType(roles) {
907
+ const hasPlural = roles.has("plural");
908
+ const hasSelectordinal = roles.has("selectordinal");
909
+ const hasSelect = roles.has("select");
910
+ const hasSimple = roles.has("simple");
911
+ const hasNumber = roles.has("number");
912
+ const hasDate = roles.has("date");
913
+ const hasTime = roles.has("time");
914
+ if (hasPlural && hasSelectordinal) {
915
+ return "CONFLICT";
916
+ }
917
+ if (hasSelect && (hasPlural || hasSelectordinal || hasNumber)) {
918
+ return "CONFLICT";
919
+ }
920
+ if (hasPlural && hasSelect) {
921
+ return "CONFLICT";
922
+ }
923
+ if (hasSelectordinal && hasSelect) {
924
+ return "CONFLICT";
925
+ }
926
+ if (hasDate || hasTime) {
927
+ if (hasPlural || hasSelect || hasSelectordinal || hasSimple || hasNumber) {
928
+ return "CONFLICT";
929
+ }
930
+ return "date";
931
+ }
932
+ if (hasPlural || hasSelectordinal || hasNumber) {
933
+ return "number";
934
+ }
935
+ if (hasSelect || hasSimple) {
936
+ return "string";
937
+ }
938
+ return "string";
939
+ }
940
+ function mergeVariableMetaAcrossLocales(localeMetas) {
941
+ const keySignatures = localeMetas.map((meta) => Object.keys(meta).sort().join(","));
942
+ if (new Set(keySignatures).size > 1) {
943
+ return {
944
+ ok: false,
945
+ message: `Inconsistent ICU variable names across locales (keys: ${keySignatures.join(" vs ")})`
946
+ };
947
+ }
948
+ const merged = {};
949
+ const variableNames = new Set(localeMetas.flatMap((meta) => Object.keys(meta)));
950
+ for (const variableName of variableNames) {
951
+ const combinedRoles = /* @__PURE__ */ new Set();
952
+ for (const meta of localeMetas) {
953
+ for (const role of meta[variableName]?.roles ?? []) {
954
+ combinedRoles.add(role);
955
+ }
956
+ }
957
+ const mergedType = inferMergedVariableType(combinedRoles);
958
+ if (mergedType === "CONFLICT") {
959
+ const rolesByLocale = localeMetas.map((meta) => {
960
+ const entry = meta[variableName];
961
+ if (!entry) {
962
+ return null;
963
+ }
964
+ return [...entry.roles].sort().join("|");
965
+ }).filter((value) => value !== null);
966
+ return {
967
+ ok: false,
968
+ message: `Incompatible ICU variable "${variableName}" across locales (roles: ${rolesByLocale.join(" vs ")})`
969
+ };
970
+ }
971
+ merged[variableName] = mergedType;
972
+ }
973
+ return { ok: true, merged };
974
+ }
975
+
976
+ // src/project-locales.ts
977
+ function classifyAreaLocales(areaLocales, localeFallback) {
978
+ const areaSet = new Set(areaLocales);
979
+ const full = [];
980
+ const preserve = [];
981
+ for (const locale of areaLocales) {
982
+ const fallback = localeFallback?.[locale];
983
+ if (fallback !== null && fallback !== void 0 && areaSet.has(fallback)) {
984
+ preserve.push(locale);
985
+ } else {
986
+ full.push(locale);
987
+ }
988
+ }
989
+ return { full, preserve };
990
+ }
991
+ function projectKeyDictionary(dictionary, locales, localeFallback) {
992
+ const targetLocales = [...new Set(locales)];
993
+ const result = {};
994
+ for (const [key, localesByKey] of Object.entries(dictionary)) {
995
+ const projected = {};
996
+ for (const locale of targetLocales) {
997
+ const resolved = resolveLocaleTemplate(localesByKey, locale, localeFallback);
998
+ if (resolved !== void 0) {
999
+ projected[locale] = resolved.template;
1000
+ }
1001
+ }
1002
+ if (Object.keys(projected).length > 0) {
1003
+ result[key] = projected;
1004
+ }
1005
+ }
1006
+ return result;
1007
+ }
1008
+ function projectNamespaceLocalesCore(dictionary, locales, localeFallback) {
1009
+ return projectKeyDictionary(dictionary, locales, localeFallback);
1010
+ }
1011
+ function projectNamespaceForDeliveryAreaCore(dictionary, areaLocales, localeFallback) {
1012
+ const { full, preserve } = classifyAreaLocales(areaLocales, localeFallback);
1013
+ const fullProjection = full.length > 0 ? projectKeyDictionary(dictionary, full, localeFallback) : void 0;
1014
+ const result = {};
1015
+ for (const [key, localesByKey] of Object.entries(dictionary)) {
1016
+ const projected = {};
1017
+ if (fullProjection !== void 0) {
1018
+ const fullEntry = fullProjection[key];
1019
+ if (fullEntry !== void 0) {
1020
+ Object.assign(projected, fullEntry);
1021
+ }
1022
+ }
1023
+ for (const locale of preserve) {
1024
+ const template = localesByKey[locale];
1025
+ if (template !== void 0) {
1026
+ projected[locale] = template;
1027
+ }
1028
+ }
1029
+ if (Object.keys(projected).length > 0) {
1030
+ result[key] = projected;
1031
+ }
1032
+ }
1033
+ return result;
1034
+ }
1035
+ function writeFileIfChanged(absolutePath, content) {
1036
+ if (fs2.existsSync(absolutePath)) {
1037
+ const currentContent = fs2.readFileSync(absolutePath, "utf8");
1038
+ if (currentContent === content) {
1039
+ return false;
1040
+ }
1041
+ }
1042
+ fs2.mkdirSync(path5.dirname(absolutePath), { recursive: true });
1043
+ fs2.writeFileSync(absolutePath, content);
1044
+ return true;
1045
+ }
1046
+
1047
+ // src/codegen/read-dictionary.ts
1048
+ function getDictionaryFormat(filePath) {
1049
+ const extension = path5.extname(filePath).toLowerCase();
1050
+ if (extension === ".json") {
1051
+ return "json";
1052
+ }
1053
+ if (extension === ".yaml" || extension === ".yml") {
1054
+ return "yaml";
1055
+ }
1056
+ return null;
1057
+ }
1058
+ function resolveSplitJsonPath(sourcePath, locale, generatedDirRelative) {
1059
+ const baseName = path5.basename(sourcePath, path5.extname(sourcePath));
1060
+ return path5.join(generatedDirRelative, "translations", `${baseName}.${locale}.json`).replace(/\\/g, "/");
1061
+ }
1062
+ function resolveAreaJsonPath(sourcePath, area, generatedDirRelative) {
1063
+ const baseName = path5.basename(sourcePath, path5.extname(sourcePath));
1064
+ return path5.join(generatedDirRelative, "translations", `${baseName}.${area}.json`).replace(/\\/g, "/");
1065
+ }
1066
+ function splitDictionaryByLocale(dictionary, locales, localeFallback) {
1067
+ const byLocale = {};
1068
+ for (const locale of locales) {
1069
+ byLocale[locale] = projectNamespaceLocalesCore(dictionary, [locale], localeFallback);
1070
+ }
1071
+ return byLocale;
1072
+ }
1073
+ function splitDictionaryByDeliveryArea(dictionary, deliveryArtifacts, localeFallback) {
1074
+ const byArea = {};
1075
+ for (const [area, areaLocales] of Object.entries(deliveryArtifacts)) {
1076
+ byArea[area] = projectNamespaceForDeliveryAreaCore(dictionary, areaLocales, localeFallback);
1077
+ }
1078
+ return byArea;
1079
+ }
1080
+ function assertDictionaryShape(value, context) {
1081
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
1082
+ throw new Error(`[Codegen Error] ${context} must be a plain object.`);
1083
+ }
1084
+ for (const [key, localesByKey] of Object.entries(value)) {
1085
+ if (!IDENTIFIER_NAME_PATTERN.test(key)) {
1086
+ throw new Error(
1087
+ `[Codegen Error] ${context}: invalid key "${key}" (${IDENTIFIER_NAME_REQUIREMENT}).`
1088
+ );
1089
+ }
1090
+ if (localesByKey === null || typeof localesByKey !== "object" || Array.isArray(localesByKey)) {
1091
+ throw new Error(`[Codegen Error] ${context}: key "${key}" must map locales to strings.`);
1092
+ }
1093
+ for (const [locale, template] of Object.entries(localesByKey)) {
1094
+ if (typeof template !== "string") {
1095
+ throw new Error(
1096
+ `[Codegen Error] ${context}: key "${key}", locale "${locale}" must be a string.`
1097
+ );
1098
+ }
1099
+ }
1100
+ }
1101
+ }
1102
+ function readDictionaryFile(absolutePath) {
1103
+ const format = getDictionaryFormat(absolutePath);
1104
+ if (!format) {
1105
+ throw new Error(
1106
+ `[Codegen Error] Unsupported dictionary format "${path5.extname(absolutePath)}" for ${absolutePath}. Use .json, .yaml, or .yml.`
1107
+ );
1108
+ }
1109
+ const source = fs2.readFileSync(absolutePath, "utf8");
1110
+ let parsed;
1111
+ try {
1112
+ parsed = format === "json" ? JSON.parse(source) : parse$1(source);
1113
+ } catch (error) {
1114
+ const message = error instanceof Error ? error.message : String(error);
1115
+ throw new Error(`[Codegen Error] Failed to parse dictionary ${absolutePath}: ${message}`);
1116
+ }
1117
+ assertDictionaryShape(parsed, `Dictionary file ${absolutePath}`);
1118
+ return parsed;
1119
+ }
1120
+ function writeCompiledJson(absoluteJsonPath, dictionary) {
1121
+ return writeFileIfChanged(absoluteJsonPath, `${JSON.stringify(dictionary, null, 2)}
1122
+ `);
1123
+ }
1124
+ function prepareDictionaryEntries(projectRoot, entries, generatedDirRelative, options) {
1125
+ const delivery = options.delivery;
1126
+ const { dictionariesByNamespace, localeFallback, requestLocales, deliveryArtifacts } = options;
1127
+ const resolvedEntries = [];
1128
+ const splitPathsByNamespace = {};
1129
+ const compiledFiles = [];
1130
+ if (delivery === "split-by-locale" && (!requestLocales || requestLocales.length === 0)) {
1131
+ throw new Error(
1132
+ "[Codegen Error] split-by-locale delivery requires at least one request locale."
1133
+ );
1134
+ }
1135
+ if (delivery === "custom" && (!deliveryArtifacts || Object.keys(deliveryArtifacts).length === 0)) {
1136
+ throw new Error("[Codegen Error] custom delivery requires deliveryArtifacts.");
1137
+ }
1138
+ for (const entry of entries) {
1139
+ const sourceAbsolutePath = path5.resolve(projectRoot, entry.filePath);
1140
+ const format = getDictionaryFormat(entry.filePath);
1141
+ if (!format) {
1142
+ throw new Error(
1143
+ `[Codegen Error] Namespace "${entry.namespace}" uses unsupported dictionary extension in "${entry.filePath}". Use .json, .yaml, or .yml.`
1144
+ );
1145
+ }
1146
+ const dictionary = dictionariesByNamespace[entry.namespace];
1147
+ if (!dictionary) {
1148
+ throw new Error(
1149
+ `[Codegen Error] Missing parsed dictionary for namespace "${entry.namespace}".`
1150
+ );
1151
+ }
1152
+ if (delivery === "split-by-locale") {
1153
+ const splitPaths2 = {};
1154
+ const dictionariesByLocale = splitDictionaryByLocale(
1155
+ dictionary,
1156
+ requestLocales,
1157
+ localeFallback
1158
+ );
1159
+ for (const locale of requestLocales) {
1160
+ const splitRelativePath = resolveSplitJsonPath(
1161
+ entry.filePath,
1162
+ locale,
1163
+ generatedDirRelative
1164
+ );
1165
+ const splitAbsolutePath = path5.resolve(projectRoot, splitRelativePath);
1166
+ const wroteFile = writeCompiledJson(splitAbsolutePath, dictionariesByLocale[locale]);
1167
+ if (wroteFile) {
1168
+ compiledFiles.push(
1169
+ `${relativePosixPath(projectRoot, sourceAbsolutePath)} \u2192 ${splitRelativePath}`
1170
+ );
1171
+ }
1172
+ splitPaths2[locale] = splitRelativePath;
1173
+ }
1174
+ splitPathsByNamespace[entry.namespace] = splitPaths2;
1175
+ resolvedEntries.push(entry);
1176
+ continue;
1177
+ }
1178
+ const splitPaths = {};
1179
+ const dictionariesByArea = splitDictionaryByDeliveryArea(
1180
+ dictionary,
1181
+ deliveryArtifacts,
1182
+ localeFallback
1183
+ );
1184
+ for (const area of Object.keys(deliveryArtifacts).sort()) {
1185
+ const areaRelativePath = resolveAreaJsonPath(entry.filePath, area, generatedDirRelative);
1186
+ const areaAbsolutePath = path5.resolve(projectRoot, areaRelativePath);
1187
+ const wroteFile = writeCompiledJson(areaAbsolutePath, dictionariesByArea[area]);
1188
+ if (wroteFile) {
1189
+ compiledFiles.push(
1190
+ `${relativePosixPath(projectRoot, sourceAbsolutePath)} \u2192 ${areaRelativePath}`
1191
+ );
1192
+ }
1193
+ splitPaths[area] = areaRelativePath;
1194
+ }
1195
+ splitPathsByNamespace[entry.namespace] = splitPaths;
1196
+ resolvedEntries.push(entry);
1197
+ }
1198
+ return { resolvedEntries, splitPathsByNamespace, compiledFiles };
1199
+ }
1200
+
1201
+ // src/codegen/icu-analysis.ts
1202
+ function paramsTypeForVariables(variables) {
1203
+ const keys = Object.keys(variables);
1204
+ if (keys.length === 0) {
1205
+ return "never";
1206
+ }
1207
+ const props = keys.map((key) => {
1208
+ const type = variables[key] === "date" ? "Date | number" : variables[key];
1209
+ return `${key}: ${type}`;
1210
+ });
1211
+ return `{ ${props.join("; ")} }`;
1212
+ }
1213
+ function analyzeDictionaries(projectRoot, entries) {
1214
+ const paramsByNamespace = {};
1215
+ const argsSpecByNamespace = {};
1216
+ const locales = /* @__PURE__ */ new Set();
1217
+ const dictionariesByNamespace = {};
1218
+ let hasErrors = false;
1219
+ for (const entry of entries) {
1220
+ const absolutePath = path5.resolve(projectRoot, entry.filePath);
1221
+ if (!fs2.existsSync(absolutePath)) {
1222
+ console.error(
1223
+ `[Codegen Error] Dictionary file not found for namespace "${entry.namespace}": ${absolutePath}`
1224
+ );
1225
+ hasErrors = true;
1226
+ continue;
1227
+ }
1228
+ const dictionary = readDictionaryFile(absolutePath);
1229
+ dictionariesByNamespace[entry.namespace] = dictionary;
1230
+ paramsByNamespace[entry.namespace] = {};
1231
+ argsSpecByNamespace[entry.namespace] = {};
1232
+ for (const [key, localesByKey] of Object.entries(dictionary)) {
1233
+ const localeMetas = [];
1234
+ for (const locale of Object.keys(localesByKey)) {
1235
+ locales.add(locale);
1236
+ }
1237
+ for (const [locale, template] of Object.entries(localesByKey)) {
1238
+ try {
1239
+ const ast = parse(template);
1240
+ localeMetas.push(extractVariableMeta(ast));
1241
+ } catch (error) {
1242
+ hasErrors = true;
1243
+ const message = error instanceof Error ? error.message : String(error);
1244
+ console.error(
1245
+ `[Codegen Error] ICU syntax error \u2014 namespace "${entry.namespace}", key "${key}", locale "${locale}": ${message}`
1246
+ );
1247
+ }
1248
+ }
1249
+ if (localeMetas.length === 0) {
1250
+ continue;
1251
+ }
1252
+ const mergedVariables = mergeVariableMetaAcrossLocales(localeMetas);
1253
+ if (!mergedVariables.ok) {
1254
+ hasErrors = true;
1255
+ console.error(
1256
+ `[Codegen Error] ${mergedVariables.message} \u2014 namespace "${entry.namespace}", key "${key}"`
1257
+ );
1258
+ continue;
1259
+ }
1260
+ const variables = mergedVariables.merged;
1261
+ paramsByNamespace[entry.namespace][key] = paramsTypeForVariables(variables);
1262
+ argsSpecByNamespace[entry.namespace][key] = variables;
1263
+ }
1264
+ }
1265
+ if (hasErrors) {
1266
+ return { ok: false };
1267
+ }
1268
+ return {
1269
+ ok: true,
1270
+ analysis: { paramsByNamespace, argsSpecByNamespace, locales, dictionariesByNamespace }
1271
+ };
1272
+ }
1273
+
1274
+ // src/codegen/locale-policy.ts
1275
+ function buildRequiredLocales(dictionaryLocales, configFallback) {
1276
+ return [...collectRequestLocales(dictionaryLocales, configFallback)].sort();
1277
+ }
1278
+ function enrichLocaleFallback(dictionaryLocales, configFallback) {
1279
+ const enriched = { ...configFallback };
1280
+ for (const locale of buildRequiredLocales(dictionaryLocales, configFallback)) {
1281
+ if (!(locale in enriched)) {
1282
+ enriched[locale] = null;
1283
+ }
1284
+ }
1285
+ const sortedEntries = Object.entries(enriched).sort(
1286
+ ([left], [right]) => left.localeCompare(right)
1287
+ );
1288
+ return Object.fromEntries(sortedEntries);
1289
+ }
1290
+
1291
+ // src/codegen/run-codegen.ts
1292
+ function resolveRunCodegenInput(input) {
1293
+ if (typeof input === "string" || input === void 0) {
1294
+ const configPath2 = path5.resolve(
1295
+ process.cwd(),
1296
+ typeof input === "string" ? input : "i18n/i18n.codegen.json"
1297
+ );
1298
+ if (!fs2.existsSync(configPath2)) {
1299
+ throw new Error(`[Codegen Error] Config file not found: ${configPath2}`);
1300
+ }
1301
+ return {
1302
+ projectRoot: path5.dirname(configPath2),
1303
+ config: loadConfig(configPath2),
1304
+ log: true
1305
+ };
1306
+ }
1307
+ const log = input.log !== false;
1308
+ if (input.config !== void 0) {
1309
+ const projectRoot = path5.resolve(input.projectRoot ?? process.cwd());
1310
+ return { projectRoot, config: input.config, log };
1311
+ }
1312
+ const configPath = path5.resolve(process.cwd(), input.configPath ?? "i18n/i18n.codegen.json");
1313
+ if (!fs2.existsSync(configPath)) {
1314
+ throw new Error(`[Codegen Error] Config file not found: ${configPath}`);
1315
+ }
1316
+ return {
1317
+ projectRoot: path5.dirname(configPath),
1318
+ config: loadConfig(configPath),
1319
+ log
1320
+ };
1321
+ }
1322
+ function runCodegen(input) {
1323
+ const { projectRoot, config, log } = resolveRunCodegenInput(input);
1324
+ const sourceEntries = resolveNamespaces(config);
1325
+ const artifactsPathRelative = resolveArtifactsPath(config);
1326
+ const delivery = config.delivery ?? "split-by-locale";
1327
+ const loaderStrategy = config.loaderStrategy ?? "import";
1328
+ const analysisResult = analyzeDictionaries(projectRoot, sourceEntries);
1329
+ if (!analysisResult.ok) {
1330
+ throw new Error("[Codegen Error] Dictionary ICU analysis failed. See messages above.");
1331
+ }
1332
+ const { paramsByNamespace, argsSpecByNamespace, locales, dictionariesByNamespace } = analysisResult.analysis;
1333
+ if (config.localeFallback) {
1334
+ const localeFallbackIssues = getCodegenLocaleFallbackIssues(config.localeFallback, locales);
1335
+ if (localeFallbackIssues.length > 0) {
1336
+ reportCodegenIssues(localeFallbackIssues);
1337
+ throw new Error("[Codegen Error] Invalid localeFallback configuration.");
1338
+ }
1339
+ }
1340
+ const paths = resolveCodegenPaths(config);
1341
+ const {
1342
+ paramsTypeName,
1343
+ schemaTypeName,
1344
+ localeTypeName,
1345
+ localeFallbackConstName,
1346
+ factoryName,
1347
+ deliveryAreaTypeName: resolvedDeliveryAreaTypeName
1348
+ } = paths;
1349
+ const localeFallbackTypeName = `${localeTypeName}Fallback`;
1350
+ const importExtension = DEFAULT_IMPORT_EXTENSION;
1351
+ const requestLocales = collectRequestLocales(locales, config.localeFallback);
1352
+ const requestLocalesList = [...requestLocales].sort();
1353
+ if (delivery === "custom" && config.deliveryArtifacts) {
1354
+ const deliveryArtifactsIssues = getDeliveryArtifactsIssues(
1355
+ config.deliveryArtifacts,
1356
+ requestLocales
1357
+ );
1358
+ if (deliveryArtifactsIssues.length > 0) {
1359
+ reportCodegenIssues(deliveryArtifactsIssues);
1360
+ throw new Error("[Codegen Error] Invalid deliveryArtifacts configuration.");
1361
+ }
1362
+ }
1363
+ const {
1364
+ resolvedEntries: entries,
1365
+ splitPathsByNamespace,
1366
+ compiledFiles
1367
+ } = prepareDictionaryEntries(projectRoot, sourceEntries, artifactsPathRelative, {
1368
+ dictionariesByNamespace,
1369
+ delivery,
1370
+ localeFallback: config.localeFallback,
1371
+ requestLocales: delivery === "split-by-locale" ? requestLocalesList : void 0,
1372
+ deliveryArtifacts: delivery === "custom" ? config.deliveryArtifacts : void 0
1373
+ });
1374
+ const typesOutputPath = path5.resolve(projectRoot, paths.typesOutput);
1375
+ const instanceOutputPath = path5.resolve(projectRoot, paths.instanceOutput);
1376
+ const namespaceLoadersOutputPath = path5.resolve(projectRoot, paths.namespaceLoadersOutput);
1377
+ const dictionarySchemaOutputPath = path5.resolve(projectRoot, paths.dictionarySchemaOutput);
1378
+ const typesModule = toModuleBasename(typesOutputPath);
1379
+ const deliveryAreaNames = delivery === "custom" && config.deliveryArtifacts ? Object.keys(config.deliveryArtifacts).sort() : void 0;
1380
+ const deliveryAreaTypeName = delivery === "custom" ? resolvedDeliveryAreaTypeName : void 0;
1381
+ const localeFallbackForEmit = config.localeFallback ? enrichLocaleFallback(locales, config.localeFallback) : void 0;
1382
+ const typesContent = formatTypesFile({
1383
+ entries,
1384
+ paramsTypeName,
1385
+ schemaTypeName,
1386
+ localeTypeName,
1387
+ localeFallbackConstName,
1388
+ localeFallbackTypeName,
1389
+ localeFallback: localeFallbackForEmit,
1390
+ paramsByNamespace,
1391
+ requestLocales: requestLocalesList,
1392
+ ...deliveryAreaTypeName && deliveryAreaNames ? {
1393
+ deliveryAreaTypeName,
1394
+ deliveryAreaNames,
1395
+ ...delivery === "custom" && config.deliveryArtifacts ? { deliveryArtifacts: config.deliveryArtifacts } : {}
1396
+ } : {},
1397
+ lazyEntries: entries
1398
+ });
1399
+ const instanceContent = formatInstanceFile({
1400
+ typesOutputPath,
1401
+ namespaceLoadersOutputPath,
1402
+ paramsTypeName,
1403
+ schemaTypeName,
1404
+ localeTypeName,
1405
+ localeFallbackConstName,
1406
+ factoryName,
1407
+ hasLocaleFallback: Boolean(config.localeFallback),
1408
+ hasLocaleType: requestLocalesList.length > 0,
1409
+ importExtension,
1410
+ delivery,
1411
+ loaderStrategy,
1412
+ ...delivery === "custom" ? { localeDeliveryAreaConstName: "LOCALE_DELIVERY_AREA" } : {}
1413
+ });
1414
+ writeFileIfChanged(typesOutputPath, typesContent);
1415
+ const generatedFiles = [
1416
+ relativePosixPath(projectRoot, typesOutputPath),
1417
+ relativePosixPath(projectRoot, instanceOutputPath)
1418
+ ];
1419
+ writeFileIfChanged(instanceOutputPath, instanceContent);
1420
+ const dictionarySpecBlock = formatDictionarySpecBlock(entries, argsSpecByNamespace);
1421
+ const dictionarySchemaContent = formatDictionarySchemaFile(
1422
+ schemaTypeName,
1423
+ typesModule,
1424
+ dictionarySpecBlock,
1425
+ importExtension
1426
+ );
1427
+ writeFileIfChanged(dictionarySchemaOutputPath, dictionarySchemaContent);
1428
+ generatedFiles.push(relativePosixPath(projectRoot, dictionarySchemaOutputPath));
1429
+ const lazyEntriesWithPaths = entries.map((entry) => ({
1430
+ ...entry,
1431
+ absolutePath: path5.resolve(projectRoot, entry.filePath)
1432
+ }));
1433
+ const namespaceLoadersContent = formatNamespaceLoadersFile({
1434
+ loadersOutputPath: namespaceLoadersOutputPath,
1435
+ lazyEntries: lazyEntriesWithPaths,
1436
+ schemaTypeName,
1437
+ localeTypeName,
1438
+ typesModule,
1439
+ importExtension,
1440
+ projectRoot,
1441
+ delivery,
1442
+ splitPathsByNamespace,
1443
+ loaderStrategy,
1444
+ ...delivery === "split-by-locale" ? { requestLocales: requestLocalesList } : {},
1445
+ ...delivery === "custom" && deliveryAreaTypeName && deliveryAreaNames ? { deliveryAreaTypeName, deliveryAreaNames } : {}
1446
+ });
1447
+ writeFileIfChanged(namespaceLoadersOutputPath, namespaceLoadersContent);
1448
+ generatedFiles.push(relativePosixPath(projectRoot, namespaceLoadersOutputPath));
1449
+ if (log) {
1450
+ console.log(`\u2705 Generated: ${generatedFiles.join(", ")}`);
1451
+ if (compiledFiles.length > 0) {
1452
+ console.log(`\u2705 Compiled: ${compiledFiles.join(", ")}`);
1453
+ }
1454
+ }
1455
+ return { projectRoot, generatedFiles, compiledFiles };
1456
+ }
1457
+
1458
+ // src/codegen/generate-i18n-types.ts
1459
+ function main() {
1460
+ const configArgIndex = process.argv.indexOf("--config");
1461
+ const configPath = configArgIndex >= 0 ? process.argv[configArgIndex + 1] : "i18n/i18n.codegen.json";
1462
+ runCodegen(path5.resolve(process.cwd(), configPath));
1463
+ }
1464
+ try {
1465
+ main();
1466
+ } catch (error) {
1467
+ console.error(error instanceof Error ? error.message : String(error));
1468
+ process.exit(1);
1469
+ }
1470
+ //# sourceMappingURL=codegen.js.map
1471
+ //# sourceMappingURL=codegen.js.map