@xndrjs/i18n 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +67 -872
  2. package/dist/codegen/index.d.ts +117 -51
  3. package/dist/codegen/index.js +1585 -93
  4. package/dist/codegen/index.js.map +1 -1
  5. package/dist/index.d.ts +100 -234
  6. package/dist/index.js +74 -638
  7. package/dist/index.js.map +1 -1
  8. package/dist/validation/index.d.ts +5 -0
  9. package/dist/validation/index.js.map +1 -1
  10. package/package.json +2 -2
  11. package/src/IcuTranslationProviderMulti.test.ts +18 -40
  12. package/src/IcuTranslationProviderMulti.ts +27 -37
  13. package/src/audit/audit-dictionaries.ts +1 -1
  14. package/src/audit/run-audit.test.ts +6 -7
  15. package/src/builder-load-registry.ts +26 -5
  16. package/src/builder-loaders.ts +8 -11
  17. package/src/builder-types.test.ts +11 -42
  18. package/src/builder.test.ts +82 -243
  19. package/src/builder.ts +8 -110
  20. package/src/codegen/codegen-config-schema.ts +82 -77
  21. package/src/codegen/config.test.ts +55 -143
  22. package/src/codegen/config.ts +10 -67
  23. package/src/codegen/dictionary-spec-contract.test.ts +28 -0
  24. package/src/codegen/dictionary-spec-contract.ts +138 -0
  25. package/src/codegen/emit/dictionary-schema-file.ts +7 -43
  26. package/src/codegen/emit/instance-file.test.ts +35 -52
  27. package/src/codegen/emit/instance-file.ts +170 -268
  28. package/src/codegen/emit/namespace-loaders-file.test.ts +72 -74
  29. package/src/codegen/emit/namespace-loaders-file.ts +147 -90
  30. package/src/codegen/emit/types-file.test.ts +20 -41
  31. package/src/codegen/emit/types-file.ts +48 -74
  32. package/src/codegen/generate-i18n-types.test.ts +163 -492
  33. package/src/codegen/generate-i18n-types.ts +7 -269
  34. package/src/codegen/paths.ts +0 -14
  35. package/src/codegen/project-locales-set-namespace.test.ts +44 -86
  36. package/src/codegen/read-dictionary.test.ts +27 -9
  37. package/src/codegen/read-dictionary.ts +18 -40
  38. package/src/codegen/regenerate-namespaces.ts +180 -0
  39. package/src/codegen/run-codegen.test.ts +230 -0
  40. package/src/codegen/run-codegen.ts +252 -0
  41. package/src/codegen/types.ts +0 -6
  42. package/src/codegen-config/build-config.ts +4 -23
  43. package/src/codegen-config/codegen-config.test.ts +10 -6
  44. package/src/codegen-config/index.ts +18 -3
  45. package/src/engine.ts +3 -27
  46. package/src/fetch-artifact.ts +16 -0
  47. package/src/i18n-handle.ts +202 -0
  48. package/src/index.ts +15 -34
  49. package/src/project-locales.ts +2 -2
  50. package/src/scope-multi.ts +1 -20
  51. package/src/scope-types.ts +14 -6
  52. package/src/scope.test.ts +3 -312
  53. package/src/serialized-state.test.ts +114 -0
  54. package/src/serialized-state.ts +28 -0
  55. package/src/setup/setup-i18n.test.ts +22 -33
  56. package/src/setup/setup-i18n.ts +22 -27
  57. package/src/types.ts +3 -3
  58. package/src/validation/index.ts +4 -0
  59. package/src/IcuTranslationProviderSingle.test.ts +0 -177
  60. package/src/IcuTranslationProviderSingle.ts +0 -114
  61. package/src/builder-multi.ts +0 -481
  62. package/src/codegen/emit/dictionary-file.test.ts +0 -215
  63. package/src/codegen/emit/dictionary-file.ts +0 -244
  64. package/src/deep-freeze.test.ts +0 -40
  65. package/src/deep-freeze.ts +0 -22
  66. package/src/patch-key.test.ts +0 -186
  67. package/src/patch-key.ts +0 -140
  68. package/src/scope-single.ts +0 -85
  69. package/src/single-builder.ts +0 -153
@@ -8,27 +8,19 @@ import { GENERATED_FILE_BANNER } from "../paths.js";
8
8
  import type { NamespaceEntry } from "../types.js";
9
9
 
10
10
  /**
11
- * Emits the generated `i18n-types.generated.ts` module: schema, params, locale/area
12
- * unions, fallback constants, and lazy-load type aliases.
11
+ * Emits lazy-load type aliases. Every namespace is lazy; `InitialSchema` is empty
12
+ * (cold start / hydrate via `resources` only).
13
13
  */
14
- export function formatLazyTypesBlock(
15
- loadOnInitSet: Set<string>,
16
- lazyEntries: NamespaceEntry[],
17
- schemaTypeName: string
18
- ): string {
19
- const loadOnInitUnion =
20
- loadOnInitSet.size > 0
21
- ? [...loadOnInitSet]
22
- .sort()
23
- .map((namespace) => `'${namespace}'`)
24
- .join(" | ")
14
+ export function formatLazyTypesBlock(lazyEntries: NamespaceEntry[]): string {
15
+ const lazyUnion =
16
+ lazyEntries.length > 0
17
+ ? lazyEntries.map((entry) => `'${entry.namespace}'`).join(" | ")
25
18
  : "never";
26
- const lazyUnion = lazyEntries.map((entry) => `'${entry.namespace}'`).join(" | ");
27
19
 
28
20
  return (
29
- `export type LoadOnInitNamespace = ${loadOnInitUnion};\n` +
30
21
  `export type LazyNamespace = ${lazyUnion};\n` +
31
- `export type InitialSchema = Pick<${schemaTypeName}, LoadOnInitNamespace>;\n\n`
22
+ `/** Empty cold-start schema namespaces arrive via \`namespaceLoaders\`. */\n` +
23
+ `export type InitialSchema = Record<string, never>;\n\n`
32
24
  );
33
25
  }
34
26
 
@@ -40,7 +32,6 @@ export function formatLocaleTemplateType(localeTypeName: string, hasLocaleUnion:
40
32
  }
41
33
 
42
34
  export interface TypesFileOptions {
43
- isSingle: boolean;
44
35
  entries: NamespaceEntry[];
45
36
  projectRoot: string;
46
37
  typesOutputPath: string;
@@ -51,19 +42,18 @@ export interface TypesFileOptions {
51
42
  localeFallbackTypeName: string;
52
43
  localeFallback?: Record<string, string | null> | undefined;
53
44
  paramsByNamespace: Record<string, Record<string, string>>;
54
- requestLocaleUnion: string;
45
+ /** Sorted request locales — emits `ProjectLocales` const + `ProjectLocale` type. */
46
+ requestLocales: readonly string[];
55
47
  deliveryAreaTypeName?: string;
56
- deliveryAreaUnion?: string;
48
+ /** Sorted delivery area names — emits `ProjectDeliveryAreas` const + type. */
49
+ deliveryAreaNames?: readonly string[];
57
50
  deliveryArtifacts?: DeliveryArtifactsMap;
58
51
  localeDeliveryAreaConstName?: string;
59
- hasLazy: boolean;
60
- loadOnInitSet: Set<string>;
61
52
  lazyEntries: NamespaceEntry[];
62
53
  }
63
54
 
64
55
  export function formatTypesFile(options: TypesFileOptions): string {
65
56
  const {
66
- isSingle,
67
57
  entries,
68
58
  paramsTypeName,
69
59
  schemaTypeName,
@@ -72,27 +62,32 @@ export function formatTypesFile(options: TypesFileOptions): string {
72
62
  localeFallbackTypeName,
73
63
  localeFallback,
74
64
  paramsByNamespace,
75
- requestLocaleUnion,
65
+ requestLocales,
76
66
  deliveryAreaTypeName,
77
- deliveryAreaUnion,
67
+ deliveryAreaNames,
78
68
  deliveryArtifacts,
79
69
  localeDeliveryAreaConstName = "LOCALE_DELIVERY_AREA",
80
- hasLazy,
81
- loadOnInitSet,
82
70
  lazyEntries,
83
71
  } = options;
84
72
 
85
- const hasLocaleUnion = Boolean(requestLocaleUnion);
73
+ const hasLocaleUnion = requestLocales.length > 0;
86
74
  const localeTemplateType = formatLocaleTemplateType(localeTypeName, hasLocaleUnion);
75
+ const localesConstName = `${localeTypeName}s`;
87
76
 
88
- const localeBlock = requestLocaleUnion
77
+ const localeBlock = hasLocaleUnion
89
78
  ? `${localeFallback ? formatLocaleFallbackBlock(localeFallback, localeFallbackConstName, localeFallbackTypeName) : ""}` +
90
- `export type ${localeTypeName} = ${requestLocaleUnion};\n\n`
79
+ `export const ${localesConstName} = [${requestLocales.map((locale) => JSON.stringify(locale)).join(", ")}] as const;\n` +
80
+ `export type ${localeTypeName} = (typeof ${localesConstName})[number];\n\n`
91
81
  : "";
92
82
 
83
+ const deliveryAreasConstName = deliveryAreaTypeName ? `${deliveryAreaTypeName}s` : undefined;
93
84
  const deliveryAreaBlock =
94
- deliveryAreaTypeName && deliveryAreaUnion
95
- ? `export type ${deliveryAreaTypeName} = ${deliveryAreaUnion};\n\n` +
85
+ deliveryAreaTypeName &&
86
+ deliveryAreasConstName &&
87
+ deliveryAreaNames &&
88
+ deliveryAreaNames.length > 0
89
+ ? `export const ${deliveryAreasConstName} = [${deliveryAreaNames.map((area) => JSON.stringify(area)).join(", ")}] as const;\n` +
90
+ `export type ${deliveryAreaTypeName} = (typeof ${deliveryAreasConstName})[number];\n\n` +
96
91
  (deliveryArtifacts
97
92
  ? formatDeliveryArtifactsBlock(
98
93
  deliveryArtifacts,
@@ -109,56 +104,35 @@ export function formatTypesFile(options: TypesFileOptions): string {
109
104
  : "")
110
105
  : "";
111
106
 
112
- let paramsBlock: string;
113
- let schemaBlock: string;
107
+ const namespaceBlocks = entries
108
+ .map((entry) => {
109
+ const keyTypes = paramsByNamespace[entry.namespace] ?? {};
110
+ const lines = Object.entries(keyTypes)
111
+ .map(([key, type]) => ` ${key}: ${type};`)
112
+ .join("\n");
113
+ return ` ${entry.namespace}: {\n${lines}\n };`;
114
+ })
115
+ .join("\n");
114
116
 
115
- if (isSingle) {
116
- const onlyNamespace = entries[0]!.namespace;
117
- const keyTypes = paramsByNamespace[onlyNamespace] ?? {};
118
- const paramsLines = Object.entries(keyTypes)
119
- .map(([key, type]) => ` ${key}: ${type};`)
120
- .join("\n");
117
+ const paramsBlock = `export type ${paramsTypeName} = {\n${namespaceBlocks}\n};`;
121
118
 
122
- paramsBlock = `export type ${paramsTypeName} = {\n${paramsLines}\n};`;
119
+ const schemaLines = entries
120
+ .map((entry) => {
121
+ const keyTypes = paramsByNamespace[entry.namespace] ?? {};
122
+ const lines = Object.keys(keyTypes)
123
+ .map((key) => ` ${key}: ${localeTemplateType};`)
124
+ .join("\n");
125
+ return ` ${entry.namespace}: {\n${lines}\n };`;
126
+ })
127
+ .join("\n");
123
128
 
124
- const schemaLines = Object.keys(keyTypes)
125
- .map((key) => ` ${key}: ${localeTemplateType};`)
126
- .join("\n");
129
+ const schemaBlock = `export type ${schemaTypeName} = {\n${schemaLines}\n};`;
127
130
 
128
- schemaBlock = `export type ${schemaTypeName} = {\n${schemaLines}\n};`;
129
- } else {
130
- const namespaceBlocks = entries
131
- .map((entry) => {
132
- const keyTypes = paramsByNamespace[entry.namespace] ?? {};
133
- const lines = Object.entries(keyTypes)
134
- .map(([key, type]) => ` ${key}: ${type};`)
135
- .join("\n");
136
- return ` ${entry.namespace}: {\n${lines}\n };`;
137
- })
138
- .join("\n");
139
-
140
- paramsBlock = `export type ${paramsTypeName} = {\n${namespaceBlocks}\n};`;
141
-
142
- const schemaLines = entries
143
- .map((entry) => {
144
- const keyTypes = paramsByNamespace[entry.namespace] ?? {};
145
- const lines = Object.keys(keyTypes)
146
- .map((key) => ` ${key}: ${localeTemplateType};`)
147
- .join("\n");
148
- return ` ${entry.namespace}: {\n${lines}\n };`;
149
- })
150
- .join("\n");
151
-
152
- schemaBlock = `export type ${schemaTypeName} = {\n${schemaLines}\n};`;
153
- }
154
-
155
- const lazyTypesBlock = hasLazy
156
- ? formatLazyTypesBlock(loadOnInitSet, lazyEntries, schemaTypeName)
157
- : "";
131
+ const lazyTypesBlock = formatLazyTypesBlock(lazyEntries);
158
132
 
159
133
  return (
160
134
  `${GENERATED_FILE_BANNER}` +
161
- `export const I18N_MODE = '${isSingle ? "single" : "multi"}' as const;\n\n` +
135
+ `export const I18N_MODE = 'multi' as const;\n\n` +
162
136
  `${localeBlock}` +
163
137
  `${deliveryAreaBlock}` +
164
138
  `${paramsBlock}\n\n` +