@xndrjs/i18n 0.8.0 → 0.8.2-alpha.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.
- package/README.md +1 -2
- package/dist/cli/audit.js +739 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/codegen.js +1471 -0
- package/dist/cli/codegen.js.map +1 -0
- package/dist/cli/setup.js +148 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/codegen/index.js +43 -40
- package/dist/codegen/index.js.map +1 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +56 -10
- package/dist/index.js.map +1 -1
- package/package.json +6 -9
- package/bin/audit.mjs +0 -14
- package/bin/codegen.mjs +0 -17
- package/bin/setup.mjs +0 -14
- package/src/IcuTranslationProviderMulti.test.ts +0 -215
- package/src/IcuTranslationProviderMulti.ts +0 -176
- package/src/audit/audit-dictionaries.test.ts +0 -134
- package/src/audit/audit-dictionaries.ts +0 -178
- package/src/audit/run-audit.test.ts +0 -169
- package/src/audit/run-audit.ts +0 -94
- package/src/builder-load-registry.ts +0 -39
- package/src/builder-loaders.ts +0 -15
- package/src/builder-types.test.ts +0 -49
- package/src/builder-types.ts +0 -24
- package/src/builder.test.ts +0 -260
- package/src/builder.ts +0 -10
- package/src/codegen/codegen-config-schema.ts +0 -156
- package/src/codegen/config.test.ts +0 -231
- package/src/codegen/config.ts +0 -42
- package/src/codegen/constants.ts +0 -9
- package/src/codegen/delivery-artifacts.test.ts +0 -167
- package/src/codegen/delivery-artifacts.ts +0 -154
- package/src/codegen/dictionary-spec-contract.test.ts +0 -28
- package/src/codegen/dictionary-spec-contract.ts +0 -138
- package/src/codegen/emit/dictionary-schema-file.ts +0 -113
- package/src/codegen/emit/instance-file.test.ts +0 -71
- package/src/codegen/emit/instance-file.ts +0 -257
- package/src/codegen/emit/namespace-loaders-file.test.ts +0 -251
- package/src/codegen/emit/namespace-loaders-file.ts +0 -229
- package/src/codegen/emit/types-file.test.ts +0 -96
- package/src/codegen/emit/types-file.ts +0 -142
- package/src/codegen/fixtures/billing-namespace.json +0 -6
- package/src/codegen/generate-i18n-types.test.ts +0 -1137
- package/src/codegen/generate-i18n-types.ts +0 -20
- package/src/codegen/icu-analysis.ts +0 -110
- package/src/codegen/locale-fallback.ts +0 -73
- package/src/codegen/locale-policy.test.ts +0 -35
- package/src/codegen/locale-policy.ts +0 -30
- package/src/codegen/paths.ts +0 -44
- package/src/codegen/project-locales-set-namespace.test.ts +0 -134
- package/src/codegen/read-dictionary.test.ts +0 -594
- package/src/codegen/read-dictionary.ts +0 -260
- package/src/codegen/regenerate-namespaces.ts +0 -180
- package/src/codegen/run-codegen.test.ts +0 -230
- package/src/codegen/run-codegen.ts +0 -252
- package/src/codegen/types.ts +0 -10
- package/src/codegen/write-file-if-changed.test.ts +0 -42
- package/src/codegen/write-file-if-changed.ts +0 -20
- package/src/codegen-config/build-config.ts +0 -15
- package/src/codegen-config/codegen-config.test.ts +0 -36
- package/src/codegen-config/index.ts +0 -37
- package/src/codegen-config/type-names.ts +0 -20
- package/src/codegen-config/write-config.ts +0 -8
- package/src/engine.ts +0 -47
- package/src/fetch-artifact.ts +0 -16
- package/src/format-core.ts +0 -91
- package/src/i18n-handle.ts +0 -202
- package/src/icu/extract-variables.test.ts +0 -199
- package/src/icu/extract-variables.ts +0 -227
- package/src/icu/parse-template.ts +0 -31
- package/src/index.ts +0 -58
- package/src/project-locales.test.ts +0 -280
- package/src/project-locales.ts +0 -211
- package/src/resolve-locale.test.ts +0 -91
- package/src/resolve-locale.ts +0 -88
- package/src/scope-multi.ts +0 -105
- package/src/scope-types.ts +0 -35
- package/src/scope.test.ts +0 -172
- package/src/serialized-state.test.ts +0 -114
- package/src/serialized-state.ts +0 -28
- package/src/setup/setup-i18n.test.ts +0 -85
- package/src/setup/setup-i18n.ts +0 -156
- package/src/types.ts +0 -65
- package/src/validation/create-args-schema.ts +0 -76
- package/src/validation/create-normalized-schema.ts +0 -52
- package/src/validation/errors.ts +0 -43
- package/src/validation/index.ts +0 -202
- package/src/validation/normalize.ts +0 -299
- package/src/validation/to-dictionary.ts +0 -67
- package/src/validation/types.ts +0 -82
- package/src/validation/validate-normalized.ts +0 -133
- package/src/validation/validation.test.ts +0 -562
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import type { DeliveryMode, LoaderStrategy } from "../codegen-config-schema.js";
|
|
3
|
-
import { GENERATED_FILE_BANNER, toImportPath, toRelativeModuleImport } from "../paths.js";
|
|
4
|
-
import type { ImportExtension, NamespaceEntry } from "../types.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Emits the generated `namespace-loaders.generated.ts` module: dynamic `import()`
|
|
8
|
-
* or injectable `fetchImpl(resourceId)` loaders, shaped by delivery mode.
|
|
9
|
-
*/
|
|
10
|
-
export interface NamespaceLoadersFileOptions {
|
|
11
|
-
loadersOutputPath: string;
|
|
12
|
-
lazyEntries: (NamespaceEntry & { absolutePath: string })[];
|
|
13
|
-
schemaTypeName: string;
|
|
14
|
-
localeTypeName: string;
|
|
15
|
-
typesModule: string;
|
|
16
|
-
importExtension: ImportExtension;
|
|
17
|
-
projectRoot: string;
|
|
18
|
-
delivery: DeliveryMode;
|
|
19
|
-
splitPathsByNamespace?: Record<string, Record<string, string>>;
|
|
20
|
-
requestLocales?: readonly string[];
|
|
21
|
-
deliveryAreaTypeName?: string;
|
|
22
|
-
deliveryAreaNames?: readonly string[];
|
|
23
|
-
loaderStrategy?: LoaderStrategy;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface PartitionedNamespaceLoadersParams {
|
|
27
|
-
partitionKeys: readonly string[];
|
|
28
|
-
paramName: string;
|
|
29
|
-
paramTypeName: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function formatDefaultNamespacesLiteral(lazyEntries: NamespaceEntry[]): string {
|
|
33
|
-
return [...lazyEntries]
|
|
34
|
-
.map((entry) => entry.namespace)
|
|
35
|
-
.sort()
|
|
36
|
-
.map((namespace) => JSON.stringify(namespace))
|
|
37
|
-
.join(", ");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function formatPartitionedTypesImport(
|
|
41
|
-
options: NamespaceLoadersFileOptions,
|
|
42
|
-
paramTypeName: string
|
|
43
|
-
): string {
|
|
44
|
-
const { schemaTypeName, typesModule, importExtension } = options;
|
|
45
|
-
return `import type { ${schemaTypeName}, LazyNamespace, ${paramTypeName} } from '${toRelativeModuleImport(typesModule, importExtension)}';\n\n`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function formatImportCase(
|
|
49
|
-
loadersOutputPath: string,
|
|
50
|
-
projectRoot: string,
|
|
51
|
-
splitRelativePath: string
|
|
52
|
-
): string {
|
|
53
|
-
const importPath = toImportPath(loadersOutputPath, path.resolve(projectRoot, splitRelativePath));
|
|
54
|
-
return `return import('${importPath}.json').then((m) => m.default);`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function indentBlock(text: string, spaces: number): string {
|
|
58
|
-
const pad = " ".repeat(spaces);
|
|
59
|
-
return text
|
|
60
|
-
.split("\n")
|
|
61
|
-
.map((line) => (line.length === 0 ? line : `${pad}${line}`))
|
|
62
|
-
.join("\n");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function formatImportNamespaceLoaderEntry(options: {
|
|
66
|
-
namespace: string;
|
|
67
|
-
paramName: string;
|
|
68
|
-
partitionKeys: readonly string[];
|
|
69
|
-
splitPathsByNamespace: Record<string, Record<string, string>>;
|
|
70
|
-
loadersOutputPath: string;
|
|
71
|
-
projectRoot: string;
|
|
72
|
-
}): string {
|
|
73
|
-
const {
|
|
74
|
-
namespace,
|
|
75
|
-
paramName,
|
|
76
|
-
partitionKeys,
|
|
77
|
-
splitPathsByNamespace,
|
|
78
|
-
loadersOutputPath,
|
|
79
|
-
projectRoot,
|
|
80
|
-
} = options;
|
|
81
|
-
|
|
82
|
-
const caseBodies = partitionKeys.map((partitionKey) => {
|
|
83
|
-
const splitRelativePath = splitPathsByNamespace[namespace]?.[partitionKey];
|
|
84
|
-
if (!splitRelativePath) {
|
|
85
|
-
throw new Error(
|
|
86
|
-
`[Codegen Error] Missing split path for namespace "${namespace}", ${paramName} "${partitionKey}".`
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
const body = formatImportCase(loadersOutputPath, projectRoot, splitRelativePath);
|
|
90
|
-
return [`case ${JSON.stringify(partitionKey)}:`, ` ${body}`].join("\n");
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const switchBody = [
|
|
94
|
-
...caseBodies,
|
|
95
|
-
`default:`,
|
|
96
|
-
` throw new Error(\`[i18n] No translation artifact for namespace ${JSON.stringify(namespace)} and ${paramName} "\${String(${paramName})}".\`);`,
|
|
97
|
-
].join("\n");
|
|
98
|
-
|
|
99
|
-
const entry = [
|
|
100
|
-
`${namespace}: (${paramName}) => {`,
|
|
101
|
-
` switch (${paramName}) {`,
|
|
102
|
-
indentBlock(switchBody, 4),
|
|
103
|
-
` }`,
|
|
104
|
-
`},`,
|
|
105
|
-
].join("\n");
|
|
106
|
-
|
|
107
|
-
return indentBlock(entry, 2);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Fetch loaders forward a resource id; URL mapping is left to `fetchImpl`. */
|
|
111
|
-
function formatFetchNamespaceLoaderEntry(options: {
|
|
112
|
-
namespace: string;
|
|
113
|
-
paramName: string;
|
|
114
|
-
schemaTypeName: string;
|
|
115
|
-
delivery: DeliveryMode;
|
|
116
|
-
}): string {
|
|
117
|
-
const { namespace, paramName, schemaTypeName, delivery } = options;
|
|
118
|
-
const nsLiteral = JSON.stringify(namespace);
|
|
119
|
-
const resourceId =
|
|
120
|
-
delivery === "custom"
|
|
121
|
-
? `{ locale, namespace: ${nsLiteral}, area }`
|
|
122
|
-
: `{ locale, namespace: ${nsLiteral} }`;
|
|
123
|
-
|
|
124
|
-
// Custom: partition is area; locale comes from load() context.
|
|
125
|
-
// Split-by-locale: partition is locale (second arg unused).
|
|
126
|
-
const signature = delivery === "custom" ? `(${paramName}, { locale })` : `(${paramName})`;
|
|
127
|
-
|
|
128
|
-
return indentBlock(
|
|
129
|
-
`${namespace}: ${signature} =>\n` +
|
|
130
|
-
` fetchImpl(${resourceId}) as Promise<${schemaTypeName}[${nsLiteral}]>,`,
|
|
131
|
-
4
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function formatPartitionedNamespaceLoadersFile(
|
|
136
|
-
options: NamespaceLoadersFileOptions,
|
|
137
|
-
params: PartitionedNamespaceLoadersParams
|
|
138
|
-
): string {
|
|
139
|
-
const { partitionKeys, paramName, paramTypeName } = params;
|
|
140
|
-
const {
|
|
141
|
-
loadersOutputPath,
|
|
142
|
-
lazyEntries,
|
|
143
|
-
schemaTypeName,
|
|
144
|
-
projectRoot,
|
|
145
|
-
delivery,
|
|
146
|
-
splitPathsByNamespace = {},
|
|
147
|
-
} = options;
|
|
148
|
-
const loaderStrategy = options.loaderStrategy ?? "import";
|
|
149
|
-
|
|
150
|
-
const loaderValueType = `${schemaTypeName}[K]`;
|
|
151
|
-
const typesImport = formatPartitionedTypesImport(options, paramTypeName);
|
|
152
|
-
|
|
153
|
-
if (loaderStrategy === "fetch") {
|
|
154
|
-
// Fetch loaders ignore partition-key switches: the resource id is enough for the app.
|
|
155
|
-
const fetchLoadersType = `{\n [K in LazyNamespace]: (${
|
|
156
|
-
delivery === "custom"
|
|
157
|
-
? `${paramName}: ${paramTypeName}, context: { locale: string }`
|
|
158
|
-
: `${paramName}: ${paramTypeName}`
|
|
159
|
-
}) => Promise<${loaderValueType}>;\n}`;
|
|
160
|
-
|
|
161
|
-
const loaderEntries = lazyEntries
|
|
162
|
-
.map((entry) =>
|
|
163
|
-
formatFetchNamespaceLoaderEntry({
|
|
164
|
-
namespace: entry.namespace,
|
|
165
|
-
paramName,
|
|
166
|
-
schemaTypeName,
|
|
167
|
-
delivery,
|
|
168
|
-
})
|
|
169
|
-
)
|
|
170
|
-
.join("\n");
|
|
171
|
-
|
|
172
|
-
return (
|
|
173
|
-
`${GENERATED_FILE_BANNER}` +
|
|
174
|
-
`import type { FetchArtifact } from '@xndrjs/i18n';\n` +
|
|
175
|
-
typesImport +
|
|
176
|
-
`export type NamespaceLoaders = ${fetchLoadersType};\n\n` +
|
|
177
|
-
`/** Build loaders that resolve artifacts via the injected {@link FetchArtifact} (resource id only). */\n` +
|
|
178
|
-
`export function createNamespaceLoaders(fetchImpl: FetchArtifact): NamespaceLoaders {\n` +
|
|
179
|
-
` return {\n${loaderEntries}\n };\n` +
|
|
180
|
-
`}\n\n` +
|
|
181
|
-
`export const defaultLazyNamespaces = [${formatDefaultNamespacesLiteral(lazyEntries)}] as const;\n`
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const loaderEntries = lazyEntries
|
|
186
|
-
.map((entry) =>
|
|
187
|
-
formatImportNamespaceLoaderEntry({
|
|
188
|
-
namespace: entry.namespace,
|
|
189
|
-
paramName,
|
|
190
|
-
partitionKeys,
|
|
191
|
-
splitPathsByNamespace,
|
|
192
|
-
loadersOutputPath,
|
|
193
|
-
projectRoot,
|
|
194
|
-
})
|
|
195
|
-
)
|
|
196
|
-
.join("\n");
|
|
197
|
-
|
|
198
|
-
// Import loaders only use the partition key; context is accepted for a uniform NamespaceLoader shape.
|
|
199
|
-
const importLoadersType = `{\n [K in LazyNamespace]: (${paramName}: ${paramTypeName}) => Promise<${loaderValueType}>;\n}`;
|
|
200
|
-
|
|
201
|
-
return (
|
|
202
|
-
`${GENERATED_FILE_BANNER}` +
|
|
203
|
-
typesImport +
|
|
204
|
-
`export const namespaceLoaders: ${importLoadersType} = {\n${loaderEntries}\n};\n` +
|
|
205
|
-
`\n` +
|
|
206
|
-
`export const defaultLazyNamespaces = [${formatDefaultNamespacesLiteral(lazyEntries)}] as const;\n`
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Builds `namespace-loaders.generated.ts` for split-by-locale or custom delivery areas.
|
|
212
|
-
*/
|
|
213
|
-
export function formatNamespaceLoadersFile(options: NamespaceLoadersFileOptions): string {
|
|
214
|
-
if (options.delivery === "split-by-locale") {
|
|
215
|
-
return formatPartitionedNamespaceLoadersFile(options, {
|
|
216
|
-
partitionKeys: options.requestLocales ?? [],
|
|
217
|
-
paramName: "locale",
|
|
218
|
-
paramTypeName: options.localeTypeName,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
if (!options.deliveryAreaTypeName) {
|
|
222
|
-
throw new Error("[Codegen Error] deliveryAreaTypeName is required for custom delivery.");
|
|
223
|
-
}
|
|
224
|
-
return formatPartitionedNamespaceLoadersFile(options, {
|
|
225
|
-
partitionKeys: options.deliveryAreaNames ?? [],
|
|
226
|
-
paramName: "area",
|
|
227
|
-
paramTypeName: options.deliveryAreaTypeName,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { formatTypesFile } from "./types-file.js";
|
|
4
|
-
|
|
5
|
-
const projectRoot = "/project";
|
|
6
|
-
const typesOutputPath = path.join(projectRoot, "src/i18n/generated/i18n-types.generated.ts");
|
|
7
|
-
|
|
8
|
-
describe("formatTypesFile", () => {
|
|
9
|
-
it("emits explicit Partial schema types and lazy aliases", () => {
|
|
10
|
-
const output = formatTypesFile({
|
|
11
|
-
entries: [
|
|
12
|
-
{ namespace: "default", filePath: "src/i18n/translations/default.json" },
|
|
13
|
-
{ namespace: "billing", filePath: "src/i18n/translations/billing.yaml" },
|
|
14
|
-
],
|
|
15
|
-
projectRoot,
|
|
16
|
-
typesOutputPath,
|
|
17
|
-
paramsTypeName: "AppParams",
|
|
18
|
-
schemaTypeName: "AppSchema",
|
|
19
|
-
localeTypeName: "AppLocale",
|
|
20
|
-
localeFallbackConstName: "LOCALE_FALLBACK",
|
|
21
|
-
localeFallbackTypeName: "AppLocaleFallback",
|
|
22
|
-
paramsByNamespace: {
|
|
23
|
-
default: {
|
|
24
|
-
login_button: "never",
|
|
25
|
-
welcome: "{ name: string }",
|
|
26
|
-
},
|
|
27
|
-
billing: {
|
|
28
|
-
invoice_summary: "{ count: number }",
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
requestLocales: ["en", "it"],
|
|
32
|
-
lazyEntries: [
|
|
33
|
-
{ namespace: "default", filePath: "src/i18n/translations/default.json" },
|
|
34
|
-
{ namespace: "billing", filePath: "src/i18n/translations/billing.yaml" },
|
|
35
|
-
],
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
expect(output).toContain("login_button: Partial<Record<AppLocale, string>>;");
|
|
39
|
-
expect(output).toContain("invoice_summary: Partial<Record<AppLocale, string>>;");
|
|
40
|
-
expect(output).toContain('export const AppLocales = ["en", "it"] as const;');
|
|
41
|
-
expect(output).toContain("export type AppLocale = (typeof AppLocales)[number];");
|
|
42
|
-
expect(output).toContain("export type LazyNamespace = 'default' | 'billing'");
|
|
43
|
-
expect(output).toContain("export type InitialSchema = Record<string, never>");
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("emits delivery area type for custom delivery", () => {
|
|
47
|
-
const output = formatTypesFile({
|
|
48
|
-
entries: [
|
|
49
|
-
{ namespace: "default", filePath: "src/i18n/translations/default.json" },
|
|
50
|
-
{ namespace: "billing", filePath: "src/i18n/translations/billing.yaml" },
|
|
51
|
-
],
|
|
52
|
-
projectRoot,
|
|
53
|
-
typesOutputPath,
|
|
54
|
-
paramsTypeName: "AppParams",
|
|
55
|
-
schemaTypeName: "AppSchema",
|
|
56
|
-
localeTypeName: "AppLocale",
|
|
57
|
-
localeFallbackConstName: "LOCALE_FALLBACK",
|
|
58
|
-
localeFallbackTypeName: "AppLocaleFallback",
|
|
59
|
-
localeFallback: {
|
|
60
|
-
"en-US": null,
|
|
61
|
-
it: "en-US",
|
|
62
|
-
fr: null,
|
|
63
|
-
},
|
|
64
|
-
paramsByNamespace: {
|
|
65
|
-
default: { some_key: "never" },
|
|
66
|
-
billing: { invoice_summary: "{ count: number }" },
|
|
67
|
-
},
|
|
68
|
-
requestLocales: ["en-US", "fr", "it"],
|
|
69
|
-
deliveryAreaTypeName: "AppDeliveryArea",
|
|
70
|
-
deliveryAreaNames: ["eu", "us"],
|
|
71
|
-
deliveryArtifacts: {
|
|
72
|
-
eu: ["it", "fr"],
|
|
73
|
-
us: ["en-US"],
|
|
74
|
-
},
|
|
75
|
-
lazyEntries: [
|
|
76
|
-
{ namespace: "default", filePath: "src/i18n/translations/default.json" },
|
|
77
|
-
{ namespace: "billing", filePath: "src/i18n/translations/billing.yaml" },
|
|
78
|
-
],
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
expect(output).toContain('export const AppLocales = ["en-US", "fr", "it"] as const;');
|
|
82
|
-
expect(output).toContain("export type AppLocale = (typeof AppLocales)[number];");
|
|
83
|
-
expect(output).toContain('export const AppDeliveryAreas = ["eu", "us"] as const;');
|
|
84
|
-
expect(output).toContain("export type AppDeliveryArea = (typeof AppDeliveryAreas)[number];");
|
|
85
|
-
expect(output).toContain("export const DELIVERY_ARTIFACTS = {");
|
|
86
|
-
expect(output).toContain('"eu": ["fr", "it"] as const');
|
|
87
|
-
expect(output).toContain('"us": ["en-US"] as const');
|
|
88
|
-
expect(output).toContain("} as const satisfies Record<AppDeliveryArea, readonly AppLocale[]>;");
|
|
89
|
-
expect(output).toContain("export type AppDeliveryArtifacts = typeof DELIVERY_ARTIFACTS;");
|
|
90
|
-
expect(output).toContain("export const LOCALE_DELIVERY_AREA = {");
|
|
91
|
-
expect(output).toContain('"it": "eu"');
|
|
92
|
-
expect(output).toContain('"en-US": "us"');
|
|
93
|
-
expect(output).toContain("} as const satisfies Record<AppLocale, AppDeliveryArea>;");
|
|
94
|
-
expect(output).toContain("some_key: Partial<Record<AppLocale, string>>;");
|
|
95
|
-
});
|
|
96
|
-
});
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
formatDeliveryArtifactsBlock,
|
|
3
|
-
formatLocaleDeliveryAreaBlock,
|
|
4
|
-
type DeliveryArtifactsMap,
|
|
5
|
-
} from "../delivery-artifacts.js";
|
|
6
|
-
import { formatLocaleFallbackBlock } from "../locale-fallback.js";
|
|
7
|
-
import { GENERATED_FILE_BANNER } from "../paths.js";
|
|
8
|
-
import type { NamespaceEntry } from "../types.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Emits lazy-load type aliases. Every namespace is lazy; `InitialSchema` is empty
|
|
12
|
-
* (cold start / hydrate via `resources` only).
|
|
13
|
-
*/
|
|
14
|
-
export function formatLazyTypesBlock(lazyEntries: NamespaceEntry[]): string {
|
|
15
|
-
const lazyUnion =
|
|
16
|
-
lazyEntries.length > 0
|
|
17
|
-
? lazyEntries.map((entry) => `'${entry.namespace}'`).join(" | ")
|
|
18
|
-
: "never";
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
`export type LazyNamespace = ${lazyUnion};\n` +
|
|
22
|
-
`/** Empty cold-start schema — namespaces arrive via \`namespaceLoaders\`. */\n` +
|
|
23
|
-
`export type InitialSchema = Record<string, never>;\n\n`
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function formatLocaleTemplateType(localeTypeName: string, hasLocaleUnion: boolean): string {
|
|
28
|
-
if (!hasLocaleUnion) {
|
|
29
|
-
return "Partial<Record<string, string>>";
|
|
30
|
-
}
|
|
31
|
-
return `Partial<Record<${localeTypeName}, string>>`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface TypesFileOptions {
|
|
35
|
-
entries: NamespaceEntry[];
|
|
36
|
-
projectRoot: string;
|
|
37
|
-
typesOutputPath: string;
|
|
38
|
-
paramsTypeName: string;
|
|
39
|
-
schemaTypeName: string;
|
|
40
|
-
localeTypeName: string;
|
|
41
|
-
localeFallbackConstName: string;
|
|
42
|
-
localeFallbackTypeName: string;
|
|
43
|
-
localeFallback?: Record<string, string | null> | undefined;
|
|
44
|
-
paramsByNamespace: Record<string, Record<string, string>>;
|
|
45
|
-
/** Sorted request locales — emits `ProjectLocales` const + `ProjectLocale` type. */
|
|
46
|
-
requestLocales: readonly string[];
|
|
47
|
-
deliveryAreaTypeName?: string;
|
|
48
|
-
/** Sorted delivery area names — emits `ProjectDeliveryAreas` const + type. */
|
|
49
|
-
deliveryAreaNames?: readonly string[];
|
|
50
|
-
deliveryArtifacts?: DeliveryArtifactsMap;
|
|
51
|
-
localeDeliveryAreaConstName?: string;
|
|
52
|
-
lazyEntries: NamespaceEntry[];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function formatTypesFile(options: TypesFileOptions): string {
|
|
56
|
-
const {
|
|
57
|
-
entries,
|
|
58
|
-
paramsTypeName,
|
|
59
|
-
schemaTypeName,
|
|
60
|
-
localeTypeName,
|
|
61
|
-
localeFallbackConstName,
|
|
62
|
-
localeFallbackTypeName,
|
|
63
|
-
localeFallback,
|
|
64
|
-
paramsByNamespace,
|
|
65
|
-
requestLocales,
|
|
66
|
-
deliveryAreaTypeName,
|
|
67
|
-
deliveryAreaNames,
|
|
68
|
-
deliveryArtifacts,
|
|
69
|
-
localeDeliveryAreaConstName = "LOCALE_DELIVERY_AREA",
|
|
70
|
-
lazyEntries,
|
|
71
|
-
} = options;
|
|
72
|
-
|
|
73
|
-
const hasLocaleUnion = requestLocales.length > 0;
|
|
74
|
-
const localeTemplateType = formatLocaleTemplateType(localeTypeName, hasLocaleUnion);
|
|
75
|
-
const localesConstName = `${localeTypeName}s`;
|
|
76
|
-
|
|
77
|
-
const localeBlock = hasLocaleUnion
|
|
78
|
-
? `${localeFallback ? formatLocaleFallbackBlock(localeFallback, localeFallbackConstName, localeFallbackTypeName) : ""}` +
|
|
79
|
-
`export const ${localesConstName} = [${requestLocales.map((locale) => JSON.stringify(locale)).join(", ")}] as const;\n` +
|
|
80
|
-
`export type ${localeTypeName} = (typeof ${localesConstName})[number];\n\n`
|
|
81
|
-
: "";
|
|
82
|
-
|
|
83
|
-
const deliveryAreasConstName = deliveryAreaTypeName ? `${deliveryAreaTypeName}s` : undefined;
|
|
84
|
-
const deliveryAreaBlock =
|
|
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` +
|
|
91
|
-
(deliveryArtifacts
|
|
92
|
-
? formatDeliveryArtifactsBlock(
|
|
93
|
-
deliveryArtifacts,
|
|
94
|
-
"DELIVERY_ARTIFACTS",
|
|
95
|
-
localeTypeName,
|
|
96
|
-
deliveryAreaTypeName
|
|
97
|
-
) +
|
|
98
|
-
formatLocaleDeliveryAreaBlock(
|
|
99
|
-
deliveryArtifacts,
|
|
100
|
-
localeDeliveryAreaConstName,
|
|
101
|
-
localeTypeName,
|
|
102
|
-
deliveryAreaTypeName
|
|
103
|
-
)
|
|
104
|
-
: "")
|
|
105
|
-
: "";
|
|
106
|
-
|
|
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");
|
|
116
|
-
|
|
117
|
-
const paramsBlock = `export type ${paramsTypeName} = {\n${namespaceBlocks}\n};`;
|
|
118
|
-
|
|
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");
|
|
128
|
-
|
|
129
|
-
const schemaBlock = `export type ${schemaTypeName} = {\n${schemaLines}\n};`;
|
|
130
|
-
|
|
131
|
-
const lazyTypesBlock = formatLazyTypesBlock(lazyEntries);
|
|
132
|
-
|
|
133
|
-
return (
|
|
134
|
-
`${GENERATED_FILE_BANNER}` +
|
|
135
|
-
`export const I18N_MODE = 'multi' as const;\n\n` +
|
|
136
|
-
`${localeBlock}` +
|
|
137
|
-
`${deliveryAreaBlock}` +
|
|
138
|
-
`${paramsBlock}\n\n` +
|
|
139
|
-
`${schemaBlock}\n` +
|
|
140
|
-
`${lazyTypesBlock}`
|
|
141
|
-
);
|
|
142
|
-
}
|