@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,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codegen CLI entry point. Thin wrapper around {@link runCodegen}.
|
|
3
|
-
*/
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { runCodegen } from "./run-codegen.js";
|
|
6
|
-
|
|
7
|
-
function main() {
|
|
8
|
-
const configArgIndex = process.argv.indexOf("--config");
|
|
9
|
-
const configPath =
|
|
10
|
-
configArgIndex >= 0 ? process.argv[configArgIndex + 1]! : "i18n/i18n.codegen.json";
|
|
11
|
-
|
|
12
|
-
runCodegen(path.resolve(process.cwd(), configPath));
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
main();
|
|
17
|
-
} catch (error) {
|
|
18
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { parse } from "@formatjs/icu-messageformat-parser";
|
|
4
|
-
import {
|
|
5
|
-
extractVariableMeta,
|
|
6
|
-
mergeVariableMetaAcrossLocales,
|
|
7
|
-
type VariableSpec,
|
|
8
|
-
} from "../icu/extract-variables.js";
|
|
9
|
-
import type { DictionaryJson, NamespaceEntry } from "./types.js";
|
|
10
|
-
import { readDictionaryFile } from "./read-dictionary.js";
|
|
11
|
-
|
|
12
|
-
export function paramsTypeForVariables(variables: VariableSpec): string {
|
|
13
|
-
const keys = Object.keys(variables);
|
|
14
|
-
if (keys.length === 0) {
|
|
15
|
-
return "never";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const props = keys.map((key) => {
|
|
19
|
-
const type = variables[key] === "date" ? "Date | number" : variables[key];
|
|
20
|
-
return `${key}: ${type}`;
|
|
21
|
-
});
|
|
22
|
-
return `{ ${props.join("; ")} }`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface DictionaryAnalysis {
|
|
26
|
-
paramsByNamespace: Record<string, Record<string, string>>;
|
|
27
|
-
argsSpecByNamespace: Record<string, Record<string, VariableSpec>>;
|
|
28
|
-
locales: Set<string>;
|
|
29
|
-
dictionariesByNamespace: Record<string, DictionaryJson>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Phase 1 of codegen: read source dictionaries, validate ICU syntax, infer param types.
|
|
34
|
-
* Output feeds type emission, optional `DICTIONARY_SPEC`, and `prepareDictionaryEntries`.
|
|
35
|
-
*/
|
|
36
|
-
export function analyzeDictionaries(
|
|
37
|
-
projectRoot: string,
|
|
38
|
-
entries: NamespaceEntry[]
|
|
39
|
-
): { ok: true; analysis: DictionaryAnalysis } | { ok: false } {
|
|
40
|
-
const paramsByNamespace: Record<string, Record<string, string>> = {};
|
|
41
|
-
const argsSpecByNamespace: Record<string, Record<string, VariableSpec>> = {};
|
|
42
|
-
const locales = new Set<string>();
|
|
43
|
-
const dictionariesByNamespace: Record<string, DictionaryJson> = {};
|
|
44
|
-
let hasErrors = false;
|
|
45
|
-
|
|
46
|
-
for (const entry of entries) {
|
|
47
|
-
const absolutePath = path.resolve(projectRoot, entry.filePath);
|
|
48
|
-
|
|
49
|
-
if (!fs.existsSync(absolutePath)) {
|
|
50
|
-
console.error(
|
|
51
|
-
`[Codegen Error] Dictionary file not found for namespace "${entry.namespace}": ${absolutePath}`
|
|
52
|
-
);
|
|
53
|
-
hasErrors = true;
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const dictionary = readDictionaryFile(absolutePath);
|
|
58
|
-
dictionariesByNamespace[entry.namespace] = dictionary;
|
|
59
|
-
paramsByNamespace[entry.namespace] = {};
|
|
60
|
-
argsSpecByNamespace[entry.namespace] = {};
|
|
61
|
-
|
|
62
|
-
for (const [key, localesByKey] of Object.entries(dictionary)) {
|
|
63
|
-
const localeMetas: ReturnType<typeof extractVariableMeta>[] = [];
|
|
64
|
-
|
|
65
|
-
for (const locale of Object.keys(localesByKey)) {
|
|
66
|
-
locales.add(locale);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
for (const [locale, template] of Object.entries(localesByKey)) {
|
|
70
|
-
try {
|
|
71
|
-
const ast = parse(template);
|
|
72
|
-
localeMetas.push(extractVariableMeta(ast));
|
|
73
|
-
} catch (error) {
|
|
74
|
-
hasErrors = true;
|
|
75
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
76
|
-
console.error(
|
|
77
|
-
`[Codegen Error] ICU syntax error — namespace "${entry.namespace}", key "${key}", locale "${locale}": ${message}`
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (localeMetas.length === 0) {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const mergedVariables = mergeVariableMetaAcrossLocales(localeMetas);
|
|
87
|
-
if (!mergedVariables.ok) {
|
|
88
|
-
hasErrors = true;
|
|
89
|
-
console.error(
|
|
90
|
-
`[Codegen Error] ${mergedVariables.message} — namespace "${entry.namespace}", key "${key}"`
|
|
91
|
-
);
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const variables = mergedVariables.merged;
|
|
96
|
-
|
|
97
|
-
paramsByNamespace[entry.namespace]![key] = paramsTypeForVariables(variables);
|
|
98
|
-
argsSpecByNamespace[entry.namespace]![key] = variables;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (hasErrors) {
|
|
103
|
-
return { ok: false };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
ok: true,
|
|
108
|
-
analysis: { paramsByNamespace, argsSpecByNamespace, locales, dictionariesByNamespace },
|
|
109
|
-
};
|
|
110
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { validateLocaleFallback } from "../resolve-locale.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Union of locales found in dictionaries plus every locale referenced by `localeFallback`.
|
|
5
|
-
* Drives split-by-locale partitioning and the generated locale union type.
|
|
6
|
-
*/
|
|
7
|
-
export function collectRequestLocales(
|
|
8
|
-
dictionaryLocales: Set<string>,
|
|
9
|
-
fallback?: Record<string, string | null>
|
|
10
|
-
): Set<string> {
|
|
11
|
-
const all = new Set(dictionaryLocales);
|
|
12
|
-
if (!fallback) {
|
|
13
|
-
return all;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
for (const [locale, target] of Object.entries(fallback)) {
|
|
17
|
-
all.add(locale);
|
|
18
|
-
if (target !== null) {
|
|
19
|
-
all.add(target);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return all;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export type LocaleFallbackIssue = {
|
|
27
|
-
path: (string | number)[];
|
|
28
|
-
message: string;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/** Pre-emit validation gate for `localeFallback` config against discovered dictionary locales. */
|
|
32
|
-
export function getCodegenLocaleFallbackIssues(
|
|
33
|
-
fallback: Record<string, string | null>,
|
|
34
|
-
dictionaryLocales: Set<string>
|
|
35
|
-
): LocaleFallbackIssue[] {
|
|
36
|
-
const issues: LocaleFallbackIssue[] = [];
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
validateLocaleFallback(fallback);
|
|
40
|
-
} catch (error) {
|
|
41
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
42
|
-
issues.push({ path: ["localeFallback"], message });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
for (const [locale, target] of Object.entries(fallback)) {
|
|
46
|
-
if (target !== null && !(target in fallback) && !dictionaryLocales.has(target)) {
|
|
47
|
-
issues.push({
|
|
48
|
-
path: ["localeFallback", locale],
|
|
49
|
-
message: `localeFallback: "${locale}" points to "${target}" which is not defined in the fallback map or dictionary locales`,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return issues;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function formatLocaleFallbackBlock(
|
|
58
|
-
fallback: Record<string, string | null>,
|
|
59
|
-
constName: string,
|
|
60
|
-
typeName: string
|
|
61
|
-
): string {
|
|
62
|
-
const lines = Object.entries(fallback)
|
|
63
|
-
.map(
|
|
64
|
-
([locale, target]) =>
|
|
65
|
-
` ${JSON.stringify(locale)}: ${target === null ? "null" : JSON.stringify(target)},`
|
|
66
|
-
)
|
|
67
|
-
.join("\n");
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
`export const ${constName} = {\n${lines}\n} as const satisfies Record<string, string | null>;\n\n` +
|
|
71
|
-
`export type ${typeName} = typeof ${constName};\n\n`
|
|
72
|
-
);
|
|
73
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { buildRequiredLocales, enrichLocaleFallback } from "./locale-policy.js";
|
|
3
|
-
|
|
4
|
-
describe("locale-policy", () => {
|
|
5
|
-
it("buildRequiredLocales unions dictionary locales with fallback keys and targets", () => {
|
|
6
|
-
const locales = new Set(["en", "it"]);
|
|
7
|
-
const fallback = { en: null, "de-CH": "de-DE", "de-DE": "en", it: "en" };
|
|
8
|
-
|
|
9
|
-
expect(buildRequiredLocales(locales, fallback)).toEqual(["de-CH", "de-DE", "en", "it"]);
|
|
10
|
-
expect(buildRequiredLocales(locales)).toEqual(["en", "it"]);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("enrichLocaleFallback adds null for required locales missing from config", () => {
|
|
14
|
-
const locales = new Set(["en", "fr"]);
|
|
15
|
-
const fallback = { en: null, it: "en" };
|
|
16
|
-
|
|
17
|
-
expect(enrichLocaleFallback(locales, fallback)).toEqual({
|
|
18
|
-
en: null,
|
|
19
|
-
fr: null,
|
|
20
|
-
it: "en",
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("enrichLocaleFallback is a no-op when all required locales are already configured", () => {
|
|
25
|
-
const locales = new Set(["en", "it"]);
|
|
26
|
-
const fallback = {
|
|
27
|
-
en: null,
|
|
28
|
-
"de-DE": "en",
|
|
29
|
-
"de-CH": "de-DE",
|
|
30
|
-
it: "en",
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
expect(enrichLocaleFallback(locales, fallback)).toEqual(fallback);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { collectRequestLocales } from "./locale-fallback.js";
|
|
2
|
-
|
|
3
|
-
export function buildRequiredLocales(
|
|
4
|
-
dictionaryLocales: Set<string>,
|
|
5
|
-
configFallback?: Record<string, string | null>
|
|
6
|
-
): string[] {
|
|
7
|
-
return [...collectRequestLocales(dictionaryLocales, configFallback)].sort();
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Completes `localeFallback` with explicit `null` entries for every request locale,
|
|
12
|
-
* producing a stable map emitted into `i18n-types.generated.ts`.
|
|
13
|
-
*/
|
|
14
|
-
export function enrichLocaleFallback(
|
|
15
|
-
dictionaryLocales: Set<string>,
|
|
16
|
-
configFallback: Record<string, string | null>
|
|
17
|
-
): Record<string, string | null> {
|
|
18
|
-
const enriched: Record<string, string | null> = { ...configFallback };
|
|
19
|
-
|
|
20
|
-
for (const locale of buildRequiredLocales(dictionaryLocales, configFallback)) {
|
|
21
|
-
if (!(locale in enriched)) {
|
|
22
|
-
enriched[locale] = null;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const sortedEntries = Object.entries(enriched).sort(([left], [right]) =>
|
|
27
|
-
left.localeCompare(right)
|
|
28
|
-
);
|
|
29
|
-
return Object.fromEntries(sortedEntries);
|
|
30
|
-
}
|
package/src/codegen/paths.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import type { ImportExtension } from "./types.js";
|
|
3
|
-
|
|
4
|
-
export const GENERATED_FILE_BANNER = "// Automatically generated code. Do not edit manually.\n";
|
|
5
|
-
export const DEFAULT_IMPORT_EXTENSION: ImportExtension = "none";
|
|
6
|
-
|
|
7
|
-
export function reportCodegenIssues(issues: readonly { message: string }[]): void {
|
|
8
|
-
for (const issue of issues) {
|
|
9
|
-
console.error(`[Codegen Error] ${issue.message}`);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function toImportPath(fromFile: string, toFile: string): string {
|
|
14
|
-
const relative = path.relative(path.dirname(fromFile), toFile).replace(/\\/g, "/");
|
|
15
|
-
const withoutExt = relative.replace(/\.(json|ya?ml)$/i, "");
|
|
16
|
-
return withoutExt.startsWith(".") ? withoutExt : `./${withoutExt}`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function toModuleBasename(filePath: string): string {
|
|
20
|
-
return path.basename(filePath).replace(/\.ts$/, "");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function importExtensionSuffix(importExtension: ImportExtension): string {
|
|
24
|
-
return importExtension === "none" ? "" : importExtension;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function toRelativeModuleImport(
|
|
28
|
-
moduleBasename: string,
|
|
29
|
-
importExtension: ImportExtension
|
|
30
|
-
): string {
|
|
31
|
-
return `./${moduleBasename}${importExtensionSuffix(importExtension)}`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function toImportIdentifier(namespace: string): string {
|
|
35
|
-
const safe = namespace.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
36
|
-
if (/^[0-9]/.test(safe)) {
|
|
37
|
-
return `ns_${safe}`;
|
|
38
|
-
}
|
|
39
|
-
return `${safe}Ns`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function toLocaleObjectKey(locale: string): string {
|
|
43
|
-
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(locale) ? locale : JSON.stringify(locale);
|
|
44
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { spawnSync } from "node:child_process";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
7
|
-
|
|
8
|
-
const codegenScript = fileURLToPath(new URL("./generate-i18n-types.ts", import.meta.url));
|
|
9
|
-
const packageRoot = fileURLToPath(new URL("../../", import.meta.url));
|
|
10
|
-
|
|
11
|
-
function runCodegen(cwd: string) {
|
|
12
|
-
return spawnSync("tsx", [codegenScript, "--config", "i18n.codegen.json"], {
|
|
13
|
-
cwd,
|
|
14
|
-
encoding: "utf8",
|
|
15
|
-
env: process.env,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function runTsc(projectPath: string) {
|
|
20
|
-
return spawnSync("pnpm", ["exec", "tsc", "--noEmit", "-p", projectPath], {
|
|
21
|
-
cwd: packageRoot,
|
|
22
|
-
encoding: "utf8",
|
|
23
|
-
env: process.env,
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function setupMultiCodegenFixture(tempDir: string) {
|
|
28
|
-
const i18nDir = join(tempDir, "src/i18n");
|
|
29
|
-
const translationsDir = join(i18nDir, "translations");
|
|
30
|
-
mkdirSync(translationsDir, { recursive: true });
|
|
31
|
-
|
|
32
|
-
writeFileSync(
|
|
33
|
-
join(translationsDir, "default.json"),
|
|
34
|
-
JSON.stringify({
|
|
35
|
-
login_button: { en: "Login" },
|
|
36
|
-
})
|
|
37
|
-
);
|
|
38
|
-
writeFileSync(
|
|
39
|
-
join(translationsDir, "billing.json"),
|
|
40
|
-
readFileSync(
|
|
41
|
-
fileURLToPath(new URL("./fixtures/billing-namespace.json", import.meta.url)),
|
|
42
|
-
"utf8"
|
|
43
|
-
)
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
writeFileSync(
|
|
47
|
-
join(tempDir, "i18n.codegen.json"),
|
|
48
|
-
JSON.stringify({
|
|
49
|
-
projectName: "App",
|
|
50
|
-
namespaces: {
|
|
51
|
-
default: "src/i18n/translations/default.json",
|
|
52
|
-
billing: "src/i18n/translations/billing.json",
|
|
53
|
-
},
|
|
54
|
-
codegenPath: "src/i18n",
|
|
55
|
-
localeFallback: {
|
|
56
|
-
en: null,
|
|
57
|
-
it: "en",
|
|
58
|
-
},
|
|
59
|
-
})
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const codegen = runCodegen(tempDir);
|
|
63
|
-
expect(codegen.status, codegen.stderr || codegen.stdout).toBe(0);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function writeTscProject(tempDir: string) {
|
|
67
|
-
writeFileSync(
|
|
68
|
-
join(tempDir, "tsconfig.json"),
|
|
69
|
-
JSON.stringify(
|
|
70
|
-
{
|
|
71
|
-
compilerOptions: {
|
|
72
|
-
target: "ES2022",
|
|
73
|
-
module: "ESNext",
|
|
74
|
-
moduleResolution: "bundler",
|
|
75
|
-
strict: true,
|
|
76
|
-
noEmit: true,
|
|
77
|
-
resolveJsonModule: true,
|
|
78
|
-
esModuleInterop: true,
|
|
79
|
-
skipLibCheck: true,
|
|
80
|
-
paths: {
|
|
81
|
-
"@xndrjs/i18n": [join(packageRoot, "src/index.ts")],
|
|
82
|
-
"@xndrjs/i18n/validation": [join(packageRoot, "src/validation/index.ts")],
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
include: ["src/**/*.ts"],
|
|
86
|
-
},
|
|
87
|
-
null,
|
|
88
|
-
2
|
|
89
|
-
)
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
describe("codegen instance + load", () => {
|
|
94
|
-
let tempDir: string;
|
|
95
|
-
|
|
96
|
-
afterEach(() => {
|
|
97
|
-
if (tempDir) {
|
|
98
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("emits createI18n wired to namespaceLoaders", () => {
|
|
103
|
-
tempDir = mkdtempSync(join(tmpdir(), "xndrjs-i18n-project-locales-"));
|
|
104
|
-
setupMultiCodegenFixture(tempDir);
|
|
105
|
-
|
|
106
|
-
const factory = readFileSync(join(tempDir, "src/i18n/instance.generated.ts"), "utf8");
|
|
107
|
-
expect(factory).toContain("export function createI18n(");
|
|
108
|
-
expect(factory).toContain("namespaceLoaders");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("passes tsc when loading via generated namespaceLoaders", () => {
|
|
112
|
-
tempDir = mkdtempSync(join(tmpdir(), "xndrjs-i18n-project-locales-"));
|
|
113
|
-
setupMultiCodegenFixture(tempDir);
|
|
114
|
-
|
|
115
|
-
writeFileSync(
|
|
116
|
-
join(tempDir, "src/hydrate-billing.ts"),
|
|
117
|
-
[
|
|
118
|
-
`import { createI18n } from "./i18n/instance.generated.js";`,
|
|
119
|
-
``,
|
|
120
|
-
`async function hydrateBilling() {`,
|
|
121
|
-
` const { t } = await createI18n().load({ namespaces: ["billing"], locale: "en" });`,
|
|
122
|
-
` return t("billing", "invoice_summary", { count: 1 });`,
|
|
123
|
-
`}`,
|
|
124
|
-
``,
|
|
125
|
-
`export { hydrateBilling };`,
|
|
126
|
-
].join("\n")
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
writeTscProject(tempDir);
|
|
130
|
-
|
|
131
|
-
const tsc = runTsc(join(tempDir, "tsconfig.json"));
|
|
132
|
-
expect(tsc.status, `${tsc.stdout}\n${tsc.stderr}`).toBe(0);
|
|
133
|
-
});
|
|
134
|
-
});
|