@dialexjs/cli 0.9.1 → 0.9.2
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/dist/api.d.mts +42 -39
- package/dist/api.d.mts.map +1 -1
- package/dist/api.mjs +1 -1
- package/dist/index.mjs +329 -395
- package/dist/index.mjs.map +1 -1
- package/dist/{project-B27699T2.mjs → project-BTpZGNjh.mjs} +232 -208
- package/dist/project-BTpZGNjh.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/project-B27699T2.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { A as scanFiles, B as listLeaves, C as lockPath, E as writeLock, G as saveDictionaryFile, H as loadDictionaryFile, I as copyLeaf, K as TODO_PREFIX, L as setString, R as getString, S as lockFor, T as record, V as listLocales, a as generateDictionaries, d as resolveDialexConfig, f as logger, i as runGenerate, p as analyzeProject, r as toKey, s as renderGenerated, t as loadProject, w as readLock, x as isStale, y as LOCK_FILE, z as hasPath } from "./project-BTpZGNjh.mjs";
|
|
3
3
|
import { Command } from "commander";
|
|
4
|
-
import fs from "node:fs";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
import pc from "picocolors";
|
|
7
|
-
import
|
|
6
|
+
import fs from "node:fs";
|
|
8
7
|
import { loadFile, parseModule, writeFile } from "magicast";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { argumentSignature, getArguments, isIcuStructured, parseMessage } from "dialexjs/icu";
|
|
10
10
|
import * as p from "@clack/prompts";
|
|
11
11
|
import { addNuxtModule } from "magicast/helpers";
|
|
12
12
|
//#region package.json
|
|
13
|
-
var version = "0.9.
|
|
13
|
+
var version = "0.9.2";
|
|
14
14
|
//#endregion
|
|
15
|
-
//#region src/commands/check.ts
|
|
16
|
-
function addIssue(diag, issue) {
|
|
17
|
-
const level = issue.level === "error" ? "error" : "warning";
|
|
18
|
-
(level === "error" ? diag.errors : diag.warnings).push(issue.message);
|
|
19
|
-
diag.annotations.push({
|
|
20
|
-
level,
|
|
21
|
-
message: issue.message,
|
|
22
|
-
line: issue.range ? issue.range.start.line + 1 : void 0
|
|
23
|
-
});
|
|
24
|
-
}
|
|
15
|
+
//#region src/commands/check/fix.ts
|
|
25
16
|
/**
|
|
26
17
|
* Inserts keys that exist in one locale but not in another, copying the default-locale value
|
|
27
18
|
* (strings get a `[TODO]` prefix). Returns how many keys were inserted.
|
|
@@ -49,9 +40,22 @@ async function applyFixes(files, configuredLocales, defaultLocale) {
|
|
|
49
40
|
}
|
|
50
41
|
return fixed;
|
|
51
42
|
}
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/commands/check/report.ts
|
|
45
|
+
function addIssue(diag, issue) {
|
|
46
|
+
const level = issue.level === "error" ? "error" : "warning";
|
|
47
|
+
(level === "error" ? diag.errors : diag.warnings).push(issue.message);
|
|
48
|
+
diag.annotations.push({
|
|
49
|
+
level,
|
|
50
|
+
message: issue.message,
|
|
51
|
+
line: issue.range ? issue.range.start.line + 1 : void 0
|
|
52
|
+
});
|
|
53
|
+
}
|
|
52
54
|
function escapeAnnotation(value) {
|
|
53
55
|
return value.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
|
|
54
56
|
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/commands/check/stale-generated.ts
|
|
55
59
|
/**
|
|
56
60
|
* What a formatter (Prettier, Biome, oxfmt) can change without changing the code: whitespace,
|
|
57
61
|
* quote style, quoted object keys and trailing commas. Comparing this form keeps `check` quiet
|
|
@@ -108,6 +112,8 @@ function staleGeneratedFiles(root, options) {
|
|
|
108
112
|
rewritten
|
|
109
113
|
};
|
|
110
114
|
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/commands/check/index.ts
|
|
111
117
|
async function runCheck(options = {}) {
|
|
112
118
|
const root = options.cwd || process.cwd();
|
|
113
119
|
const quiet = options.json === true;
|
|
@@ -300,7 +306,7 @@ ${units.join("\n")}
|
|
|
300
306
|
return result;
|
|
301
307
|
}
|
|
302
308
|
//#endregion
|
|
303
|
-
//#region src/commands/import.ts
|
|
309
|
+
//#region src/commands/import/formats.ts
|
|
304
310
|
const unescapeXml = (value) => value.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/'/g, "'").replace(/&/g, "&");
|
|
305
311
|
function parseCsv(text) {
|
|
306
312
|
const rows = [];
|
|
@@ -383,6 +389,8 @@ function inferFormat(file) {
|
|
|
383
389
|
if (ext === ".xlf" || ext === ".xliff") return "xliff";
|
|
384
390
|
throw new Error(`Cannot infer format from "${ext}". Pass --format json|csv|xliff.`);
|
|
385
391
|
}
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region src/commands/import/icu-guard.ts
|
|
386
394
|
/**
|
|
387
395
|
* Why `value` must not replace the current text, if it is an ICU message that is invalid or uses
|
|
388
396
|
* different arguments than the same key in another locale.
|
|
@@ -408,6 +416,8 @@ function icuProblem(df, defaultLocale, locale, keyPath, value) {
|
|
|
408
416
|
}
|
|
409
417
|
}
|
|
410
418
|
}
|
|
419
|
+
//#endregion
|
|
420
|
+
//#region src/commands/import/index.ts
|
|
411
421
|
async function runImport(options) {
|
|
412
422
|
const root = options.cwd || process.cwd();
|
|
413
423
|
const file = path.resolve(root, options.file);
|
|
@@ -627,6 +637,125 @@ function detectFramework(root) {
|
|
|
627
637
|
};
|
|
628
638
|
}
|
|
629
639
|
//#endregion
|
|
640
|
+
//#region src/commands/init/ai-setup.ts
|
|
641
|
+
/** Writes `.agents/skills/dialex/SKILL.md`, merges `.mcp.json` and adds a note to AGENTS.md. */
|
|
642
|
+
function setupAiSupport(root) {
|
|
643
|
+
try {
|
|
644
|
+
const skillDir = path.join(root, ".agents/skills/dialex");
|
|
645
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
646
|
+
fs.writeFileSync(path.join(skillDir, "SKILL.md"), `---
|
|
647
|
+
name: dialex
|
|
648
|
+
description: Guide for internationalizing JavaScript and TypeScript apps with Dialex. Use when adding translations, creating content dictionaries, configuring locales, or using Dialex with frameworks like Next.js, React, Express, Hono, Fastify, SvelteKit, and Nuxt.
|
|
649
|
+
---
|
|
650
|
+
|
|
651
|
+
# Dialex Internationalization
|
|
652
|
+
|
|
653
|
+
Dialex is a high-performance, type-safe internationalization toolchain for JavaScript and TypeScript.
|
|
654
|
+
|
|
655
|
+
## Core Concepts
|
|
656
|
+
- Dictionaries are defined with \`defineDictionary("name", { ... })\` in \`*.content.ts\` files.
|
|
657
|
+
- Keys must match across all locales.
|
|
658
|
+
- ICU message syntax is supported for plurals and selects.
|
|
659
|
+
- Parity is validated at compile time with \`dialex check\`.
|
|
660
|
+
|
|
661
|
+
## Useful Commands
|
|
662
|
+
- \`dialex check --json\`: Check dictionary parity and references.
|
|
663
|
+
- \`dialex check --fix\`: Automatically insert missing keys marked with [TODO].
|
|
664
|
+
- \`dialex generate\`: Rebuild standalone dictionary bundle and types.
|
|
665
|
+
- \`dialex translate --locale <locale>\`: Automatically translate missing keys.
|
|
666
|
+
`, "utf-8");
|
|
667
|
+
const mcpConfigPath = path.join(root, ".mcp.json");
|
|
668
|
+
let mcpConfig = {};
|
|
669
|
+
if (fs.existsSync(mcpConfigPath)) try {
|
|
670
|
+
mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, "utf-8"));
|
|
671
|
+
} catch {
|
|
672
|
+
mcpConfig = {};
|
|
673
|
+
}
|
|
674
|
+
mcpConfig.mcpServers = mcpConfig.mcpServers || {};
|
|
675
|
+
mcpConfig.mcpServers.dialex = mcpConfig.mcpServers.dialex || {
|
|
676
|
+
command: "npx",
|
|
677
|
+
args: ["@dialexjs/mcp"]
|
|
678
|
+
};
|
|
679
|
+
fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
|
|
680
|
+
const agentsMdPath = path.join(root, "AGENTS.md");
|
|
681
|
+
const dialexAgentSnippet = `
|
|
682
|
+
## Internationalization (Dialex)
|
|
683
|
+
|
|
684
|
+
This project uses [Dialex](https://github.com/awaiden/dialex) for type-safe internationalization.
|
|
685
|
+
- Dictionaries are in \`*.content.ts\` files using \`defineDictionary\`.
|
|
686
|
+
- Run \`dialex check --json\` to verify parity.
|
|
687
|
+
- Run \`dialex check --fix\` to insert missing keys marked with \`[TODO]\`.
|
|
688
|
+
- Run \`dx generate\` (alias of \`dialex\`) or \`npm run dx:generate\` to compile dictionary bundles and update types.
|
|
689
|
+
`;
|
|
690
|
+
if (!fs.existsSync(agentsMdPath)) fs.writeFileSync(agentsMdPath, dialexAgentSnippet.trimStart(), "utf-8");
|
|
691
|
+
else {
|
|
692
|
+
const existing = fs.readFileSync(agentsMdPath, "utf-8");
|
|
693
|
+
if (!existing.includes("Dialex")) fs.writeFileSync(agentsMdPath, existing.trimEnd() + "\n" + dialexAgentSnippet, "utf-8");
|
|
694
|
+
}
|
|
695
|
+
logger.success("Configured AI assistant support (.agents/skills, .mcp.json, AGENTS.md)");
|
|
696
|
+
} catch (err) {
|
|
697
|
+
logger.warn(`Could not finish AI configuration: ${err.message || String(err)}`);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
//#endregion
|
|
701
|
+
//#region src/commands/init/config-file.ts
|
|
702
|
+
/** Creates `dialex.config.ts`, or updates the locales of the one that exists. */
|
|
703
|
+
async function writeDialexConfig(root, framework, defaultLocale, localeList) {
|
|
704
|
+
const configPath = fs.existsSync(path.join(root, "dialex.config.ts")) ? path.join(root, "dialex.config.ts") : fs.existsSync(path.join(root, "i18n.config.ts")) ? path.join(root, "i18n.config.ts") : path.join(root, "dialex.config.ts");
|
|
705
|
+
const configFileName = path.basename(configPath);
|
|
706
|
+
const generatedOutput = framework === "react-router" ? "app/dialex.generated.ts" : framework === "adonis" ? "start/dialex.generated.ts" : void 0;
|
|
707
|
+
try {
|
|
708
|
+
if (fs.existsSync(configPath)) {
|
|
709
|
+
const mod = await loadFile(configPath);
|
|
710
|
+
if (mod && mod.exports.default) {
|
|
711
|
+
if (mod.exports.default.$args && mod.exports.default.$args[0]) {
|
|
712
|
+
mod.exports.default.$args[0].defaultLocale = defaultLocale;
|
|
713
|
+
mod.exports.default.$args[0].locales = localeList;
|
|
714
|
+
if (generatedOutput) mod.exports.default.$args[0].output = generatedOutput;
|
|
715
|
+
}
|
|
716
|
+
await writeFile(mod, configPath);
|
|
717
|
+
logger.success(`Updated ${pc.bold(configFileName)} via Magicast`);
|
|
718
|
+
}
|
|
719
|
+
} else {
|
|
720
|
+
const mod = parseModule(`import { defineConfig } from "dialexjs";
|
|
721
|
+
|
|
722
|
+
export default defineConfig({});
|
|
723
|
+
`);
|
|
724
|
+
mod.exports.default.$args[0].defaultLocale = defaultLocale;
|
|
725
|
+
mod.exports.default.$args[0].locales = localeList;
|
|
726
|
+
if (generatedOutput) mod.exports.default.$args[0].output = generatedOutput;
|
|
727
|
+
await writeFile(mod, configPath);
|
|
728
|
+
logger.success(`Created ${pc.bold(configFileName)} via Magicast`);
|
|
729
|
+
}
|
|
730
|
+
} catch (err) {
|
|
731
|
+
logger.warn(`Magicast config update notice: ${err.message || String(err)}`);
|
|
732
|
+
const fallback = `import { defineConfig } from "dialexjs";
|
|
733
|
+
|
|
734
|
+
export default defineConfig({
|
|
735
|
+
defaultLocale: "${defaultLocale}",
|
|
736
|
+
locales: [${localeList.map((l) => `"${l}"`).join(", ")}],${generatedOutput ? `\n output: "${generatedOutput}",` : ""}
|
|
737
|
+
});
|
|
738
|
+
`;
|
|
739
|
+
fs.writeFileSync(configPath, fallback, "utf-8");
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
/** Nuxt reads the generated file through its module; it has to be registered in the Nuxt config. */
|
|
743
|
+
async function registerNuxtModule(root) {
|
|
744
|
+
const nuxtConfig = [
|
|
745
|
+
"nuxt.config.ts",
|
|
746
|
+
"nuxt.config.js",
|
|
747
|
+
"nuxt.config.mjs"
|
|
748
|
+
].map((f) => path.join(root, f)).find((f) => fs.existsSync(f));
|
|
749
|
+
if (nuxtConfig) try {
|
|
750
|
+
const nMod = await loadFile(nuxtConfig);
|
|
751
|
+
addNuxtModule(nMod, "dialexjs/nuxt", "dialex", {});
|
|
752
|
+
await writeFile(nMod, nuxtConfig);
|
|
753
|
+
logger.success(`Registered dialexjs/nuxt in ${pc.bold(path.basename(nuxtConfig))} via Magicast`);
|
|
754
|
+
} catch {
|
|
755
|
+
logger.warn("Could not update the Nuxt config; add the dialexjs/nuxt module manually.");
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
//#endregion
|
|
630
759
|
//#region src/utils/package-json.ts
|
|
631
760
|
/** Version of this CLI, which the other Dialex packages share. Read from the nearest package.json. */
|
|
632
761
|
function cliVersion() {
|
|
@@ -678,237 +807,9 @@ function addRequiredPackages(pkg, version = cliVersion()) {
|
|
|
678
807
|
return added;
|
|
679
808
|
}
|
|
680
809
|
//#endregion
|
|
681
|
-
//#region src/commands/init.ts
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
const detected = detectFramework(root);
|
|
685
|
-
const isInteractive = !options.yes && process.stdin.isTTY;
|
|
686
|
-
let framework = options.framework || detected.framework;
|
|
687
|
-
let defaultLocale = options.defaultLocale || "en";
|
|
688
|
-
let localesInput = options.locales || "en,tr";
|
|
689
|
-
if (isInteractive) {
|
|
690
|
-
p.intro(pc.bgCyan(pc.black(" dialex init ")));
|
|
691
|
-
const frameworkOptions = [
|
|
692
|
-
{
|
|
693
|
-
value: "hono",
|
|
694
|
-
label: "Hono",
|
|
695
|
-
hint: detected.framework === "hono" ? "detected" : void 0
|
|
696
|
-
},
|
|
697
|
-
{
|
|
698
|
-
value: "express",
|
|
699
|
-
label: "Express",
|
|
700
|
-
hint: detected.framework === "express" ? "detected" : void 0
|
|
701
|
-
},
|
|
702
|
-
{
|
|
703
|
-
value: "fastify",
|
|
704
|
-
label: "Fastify",
|
|
705
|
-
hint: detected.framework === "fastify" ? "detected" : void 0
|
|
706
|
-
},
|
|
707
|
-
{
|
|
708
|
-
value: "koa",
|
|
709
|
-
label: "Koa",
|
|
710
|
-
hint: detected.framework === "koa" ? "detected" : void 0
|
|
711
|
-
},
|
|
712
|
-
{
|
|
713
|
-
value: "nestjs",
|
|
714
|
-
label: "NestJS",
|
|
715
|
-
hint: detected.framework === "nestjs" ? "detected" : void 0
|
|
716
|
-
},
|
|
717
|
-
{
|
|
718
|
-
value: "next",
|
|
719
|
-
label: "Next.js",
|
|
720
|
-
hint: detected.framework === "next" ? "detected" : void 0
|
|
721
|
-
},
|
|
722
|
-
{
|
|
723
|
-
value: "react",
|
|
724
|
-
label: "React / Vite",
|
|
725
|
-
hint: detected.framework === "react" ? "detected" : void 0
|
|
726
|
-
},
|
|
727
|
-
{
|
|
728
|
-
value: "elysia",
|
|
729
|
-
label: "Elysia",
|
|
730
|
-
hint: detected.framework === "elysia" ? "detected" : void 0
|
|
731
|
-
},
|
|
732
|
-
{
|
|
733
|
-
value: "sveltekit",
|
|
734
|
-
label: "SvelteKit",
|
|
735
|
-
hint: detected.framework === "sveltekit" ? "detected" : void 0
|
|
736
|
-
},
|
|
737
|
-
{
|
|
738
|
-
value: "astro",
|
|
739
|
-
label: "Astro",
|
|
740
|
-
hint: detected.framework === "astro" ? "detected" : void 0
|
|
741
|
-
},
|
|
742
|
-
{
|
|
743
|
-
value: "vue",
|
|
744
|
-
label: "Vue / Vite",
|
|
745
|
-
hint: detected.framework === "vue" ? "detected" : void 0
|
|
746
|
-
},
|
|
747
|
-
{
|
|
748
|
-
value: "nuxt",
|
|
749
|
-
label: "Nuxt",
|
|
750
|
-
hint: detected.framework === "nuxt" ? "detected" : void 0
|
|
751
|
-
},
|
|
752
|
-
{
|
|
753
|
-
value: "svelte",
|
|
754
|
-
label: "Svelte / Vite",
|
|
755
|
-
hint: detected.framework === "svelte" ? "detected" : void 0
|
|
756
|
-
},
|
|
757
|
-
{
|
|
758
|
-
value: "solid",
|
|
759
|
-
label: "Solid / Vite",
|
|
760
|
-
hint: detected.framework === "solid" ? "detected" : void 0
|
|
761
|
-
},
|
|
762
|
-
{
|
|
763
|
-
value: "react-router",
|
|
764
|
-
label: "React Router",
|
|
765
|
-
hint: detected.framework === "react-router" ? "detected" : void 0
|
|
766
|
-
},
|
|
767
|
-
{
|
|
768
|
-
value: "react-native",
|
|
769
|
-
label: "React Native / Expo",
|
|
770
|
-
hint: detected.framework === "react-native" ? "detected" : void 0
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
value: "preact",
|
|
774
|
-
label: "Preact / Vite",
|
|
775
|
-
hint: detected.framework === "preact" ? "detected" : void 0
|
|
776
|
-
},
|
|
777
|
-
{
|
|
778
|
-
value: "qwik",
|
|
779
|
-
label: "Qwik",
|
|
780
|
-
hint: detected.framework === "qwik" ? "detected" : void 0
|
|
781
|
-
},
|
|
782
|
-
{
|
|
783
|
-
value: "lit",
|
|
784
|
-
label: "Lit / Web Components",
|
|
785
|
-
hint: detected.framework === "lit" ? "detected" : void 0
|
|
786
|
-
},
|
|
787
|
-
{
|
|
788
|
-
value: "hapi",
|
|
789
|
-
label: "Hapi",
|
|
790
|
-
hint: detected.framework === "hapi" ? "detected" : void 0
|
|
791
|
-
},
|
|
792
|
-
{
|
|
793
|
-
value: "adonis",
|
|
794
|
-
label: "AdonisJS",
|
|
795
|
-
hint: detected.framework === "adonis" ? "detected" : void 0
|
|
796
|
-
},
|
|
797
|
-
{
|
|
798
|
-
value: "trpc",
|
|
799
|
-
label: "tRPC",
|
|
800
|
-
hint: detected.framework === "trpc" ? "detected" : void 0
|
|
801
|
-
}
|
|
802
|
-
];
|
|
803
|
-
const selectedFramework = await p.select({
|
|
804
|
-
message: `Framework detected as ${pc.bold(detected.framework)} (${detected.matchedRule}). Select framework:`,
|
|
805
|
-
options: frameworkOptions,
|
|
806
|
-
initialValue: detected.framework
|
|
807
|
-
});
|
|
808
|
-
if (p.isCancel(selectedFramework)) {
|
|
809
|
-
p.cancel("Operation cancelled.");
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
framework = selectedFramework;
|
|
813
|
-
const inputDefault = await p.text({
|
|
814
|
-
message: "What is your default locale?",
|
|
815
|
-
initialValue: "en",
|
|
816
|
-
validate: (v) => !v || !v.trim() ? "Default locale cannot be empty" : void 0
|
|
817
|
-
});
|
|
818
|
-
if (p.isCancel(inputDefault)) {
|
|
819
|
-
p.cancel("Operation cancelled.");
|
|
820
|
-
return;
|
|
821
|
-
}
|
|
822
|
-
defaultLocale = inputDefault.trim();
|
|
823
|
-
const inputLocales = await p.text({
|
|
824
|
-
message: "Comma-separated list of supported locales:",
|
|
825
|
-
initialValue: `${defaultLocale},tr`,
|
|
826
|
-
validate: (v) => !v || !v.trim() ? "Locales list cannot be empty" : void 0
|
|
827
|
-
});
|
|
828
|
-
if (p.isCancel(inputLocales)) {
|
|
829
|
-
p.cancel("Operation cancelled.");
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
localesInput = inputLocales.trim();
|
|
833
|
-
if (options.ai === void 0 && options.noAi === void 0) {
|
|
834
|
-
const confirmAi = await p.confirm({
|
|
835
|
-
message: "Configure AI agent support (Claude/Cursor skills, .mcp.json, AGENTS.md)?",
|
|
836
|
-
initialValue: true
|
|
837
|
-
});
|
|
838
|
-
if (!p.isCancel(confirmAi)) options.ai = confirmAi;
|
|
839
|
-
}
|
|
840
|
-
} else logger.info(`Auto-detected framework: ${pc.bold(framework)} (${detected.matchedRule})`);
|
|
841
|
-
const localeList = Array.from(/* @__PURE__ */ new Set([defaultLocale, ...localesInput.split(",").map((s) => s.trim()).filter(Boolean)]));
|
|
842
|
-
const configPath = fs.existsSync(path.join(root, "dialex.config.ts")) ? path.join(root, "dialex.config.ts") : fs.existsSync(path.join(root, "i18n.config.ts")) ? path.join(root, "i18n.config.ts") : path.join(root, "dialex.config.ts");
|
|
843
|
-
const configFileName = path.basename(configPath);
|
|
844
|
-
const generatedOutput = framework === "react-router" ? "app/dialex.generated.ts" : framework === "adonis" ? "start/dialex.generated.ts" : void 0;
|
|
845
|
-
try {
|
|
846
|
-
if (fs.existsSync(configPath)) {
|
|
847
|
-
const mod = await loadFile(configPath);
|
|
848
|
-
if (mod && mod.exports.default) {
|
|
849
|
-
if (mod.exports.default.$args && mod.exports.default.$args[0]) {
|
|
850
|
-
mod.exports.default.$args[0].defaultLocale = defaultLocale;
|
|
851
|
-
mod.exports.default.$args[0].locales = localeList;
|
|
852
|
-
if (generatedOutput) mod.exports.default.$args[0].output = generatedOutput;
|
|
853
|
-
}
|
|
854
|
-
await writeFile(mod, configPath);
|
|
855
|
-
logger.success(`Updated ${pc.bold(configFileName)} via Magicast`);
|
|
856
|
-
}
|
|
857
|
-
} else {
|
|
858
|
-
const mod = parseModule(`import { defineConfig } from "dialexjs";
|
|
859
|
-
|
|
860
|
-
export default defineConfig({});
|
|
861
|
-
`);
|
|
862
|
-
mod.exports.default.$args[0].defaultLocale = defaultLocale;
|
|
863
|
-
mod.exports.default.$args[0].locales = localeList;
|
|
864
|
-
if (generatedOutput) mod.exports.default.$args[0].output = generatedOutput;
|
|
865
|
-
await writeFile(mod, configPath);
|
|
866
|
-
logger.success(`Created ${pc.bold(configFileName)} via Magicast`);
|
|
867
|
-
}
|
|
868
|
-
} catch (err) {
|
|
869
|
-
logger.warn(`Magicast config update notice: ${err.message || String(err)}`);
|
|
870
|
-
const fallback = `import { defineConfig } from "dialexjs";
|
|
871
|
-
|
|
872
|
-
export default defineConfig({
|
|
873
|
-
defaultLocale: "${defaultLocale}",
|
|
874
|
-
locales: [${localeList.map((l) => `"${l}"`).join(", ")}],${generatedOutput ? `\n output: "${generatedOutput}",` : ""}
|
|
875
|
-
});
|
|
876
|
-
`;
|
|
877
|
-
fs.writeFileSync(configPath, fallback, "utf-8");
|
|
878
|
-
}
|
|
879
|
-
if (framework === "nuxt") {
|
|
880
|
-
const nuxtConfig = [
|
|
881
|
-
"nuxt.config.ts",
|
|
882
|
-
"nuxt.config.js",
|
|
883
|
-
"nuxt.config.mjs"
|
|
884
|
-
].map((f) => path.join(root, f)).find((f) => fs.existsSync(f));
|
|
885
|
-
if (nuxtConfig) try {
|
|
886
|
-
const nMod = await loadFile(nuxtConfig);
|
|
887
|
-
addNuxtModule(nMod, "dialexjs/nuxt", "dialex", {});
|
|
888
|
-
await writeFile(nMod, nuxtConfig);
|
|
889
|
-
logger.success(`Registered dialexjs/nuxt in ${pc.bold(path.basename(nuxtConfig))} via Magicast`);
|
|
890
|
-
} catch {
|
|
891
|
-
logger.warn("Could not update the Nuxt config; add the dialexjs/nuxt module manually.");
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
const targetDir = (framework === "react-router" || framework === "adonis") && fs.existsSync(path.join(root, "app")) ? path.join(root, "app") : fs.existsSync(path.join(root, "src")) ? path.join(root, "src") : root;
|
|
895
|
-
const dictPath = path.join(targetDir, "home.content.ts");
|
|
896
|
-
if (!fs.existsSync(dictPath)) {
|
|
897
|
-
const dictContent = `import { defineDictionary } from "dialexjs";
|
|
898
|
-
|
|
899
|
-
export default defineDictionary("home", {
|
|
900
|
-
${localeList.map((loc) => ` ${loc}: {
|
|
901
|
-
title: "Welcome to Dialex (${loc})!",
|
|
902
|
-
description: "Type-safe internationalization.",
|
|
903
|
-
greeting: (name: string) => "Hello, " + name + "!",
|
|
904
|
-
},`).join("\n")}
|
|
905
|
-
});
|
|
906
|
-
`;
|
|
907
|
-
fs.writeFileSync(dictPath, dictContent, "utf-8");
|
|
908
|
-
logger.success(`Created starter dictionary at ${pc.bold(path.relative(root, dictPath))}`);
|
|
909
|
-
} else logger.info("home.content.ts already exists, skipping creation.");
|
|
910
|
-
generateDictionaries(root, { static: true });
|
|
911
|
-
logger.success("Generated types and standalone dictionary registry!");
|
|
810
|
+
//#region src/commands/init/dependencies.ts
|
|
811
|
+
/** Adds `dialexjs` and `@dialexjs/cli` to package.json, and the `dx:generate` script. */
|
|
812
|
+
function updatePackageJson(root) {
|
|
912
813
|
const pkgPath = path.join(root, "package.json");
|
|
913
814
|
if (fs.existsSync(pkgPath)) try {
|
|
914
815
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
@@ -926,67 +827,93 @@ ${localeList.map((loc) => ` ${loc}: {
|
|
|
926
827
|
logger.warn("Could not update package.json; add dialexjs and @dialexjs/cli manually.");
|
|
927
828
|
}
|
|
928
829
|
else logger.warn("No package.json found; install dialexjs and @dialexjs/cli in your project.");
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
-
|
|
949
|
-
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
if (!fs.existsSync(agentsMdPath)) fs.writeFileSync(agentsMdPath, dialexAgentSnippet.trimStart(), "utf-8");
|
|
977
|
-
else {
|
|
978
|
-
const existing = fs.readFileSync(agentsMdPath, "utf-8");
|
|
979
|
-
if (!existing.includes("Dialex")) fs.writeFileSync(agentsMdPath, existing.trimEnd() + "\n" + dialexAgentSnippet, "utf-8");
|
|
980
|
-
}
|
|
981
|
-
logger.success("Configured AI assistant support (.agents/skills, .mcp.json, AGENTS.md)");
|
|
982
|
-
} catch (err) {
|
|
983
|
-
logger.warn(`Could not finish AI configuration: ${err.message || String(err)}`);
|
|
830
|
+
}
|
|
831
|
+
//#endregion
|
|
832
|
+
//#region src/commands/init/prompts.ts
|
|
833
|
+
/** The frameworks `dialex init` offers, in the order of the prompt: value and label. */
|
|
834
|
+
const FRAMEWORKS = [
|
|
835
|
+
["hono", "Hono"],
|
|
836
|
+
["express", "Express"],
|
|
837
|
+
["fastify", "Fastify"],
|
|
838
|
+
["koa", "Koa"],
|
|
839
|
+
["nestjs", "NestJS"],
|
|
840
|
+
["next", "Next.js"],
|
|
841
|
+
["react", "React / Vite"],
|
|
842
|
+
["elysia", "Elysia"],
|
|
843
|
+
["sveltekit", "SvelteKit"],
|
|
844
|
+
["astro", "Astro"],
|
|
845
|
+
["vue", "Vue / Vite"],
|
|
846
|
+
["nuxt", "Nuxt"],
|
|
847
|
+
["svelte", "Svelte / Vite"],
|
|
848
|
+
["solid", "Solid / Vite"],
|
|
849
|
+
["react-router", "React Router"],
|
|
850
|
+
["react-native", "React Native / Expo"],
|
|
851
|
+
["preact", "Preact / Vite"],
|
|
852
|
+
["qwik", "Qwik"],
|
|
853
|
+
["lit", "Lit / Web Components"],
|
|
854
|
+
["hapi", "Hapi"],
|
|
855
|
+
["adonis", "AdonisJS"],
|
|
856
|
+
["trpc", "tRPC"]
|
|
857
|
+
];
|
|
858
|
+
/**
|
|
859
|
+
* Asks for the framework, the locales and (when not decided by a flag) AI support. Returns
|
|
860
|
+
* `undefined` when the user cancels. May set `options.ai`.
|
|
861
|
+
*/
|
|
862
|
+
async function askInit(options, detected) {
|
|
863
|
+
p.intro(pc.bgCyan(pc.black(" dialex init ")));
|
|
864
|
+
const frameworkOptions = FRAMEWORKS.map(([value, label]) => ({
|
|
865
|
+
value,
|
|
866
|
+
label,
|
|
867
|
+
hint: detected.framework === value ? "detected" : void 0
|
|
868
|
+
}));
|
|
869
|
+
const selectedFramework = await p.select({
|
|
870
|
+
message: `Framework detected as ${pc.bold(detected.framework)} (${detected.matchedRule}). Select framework:`,
|
|
871
|
+
options: frameworkOptions,
|
|
872
|
+
initialValue: detected.framework
|
|
873
|
+
});
|
|
874
|
+
if (p.isCancel(selectedFramework)) {
|
|
875
|
+
p.cancel("Operation cancelled.");
|
|
876
|
+
return;
|
|
984
877
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
878
|
+
const framework = selectedFramework;
|
|
879
|
+
const inputDefault = await p.text({
|
|
880
|
+
message: "What is your default locale?",
|
|
881
|
+
initialValue: "en",
|
|
882
|
+
validate: (v) => !v || !v.trim() ? "Default locale cannot be empty" : void 0
|
|
883
|
+
});
|
|
884
|
+
if (p.isCancel(inputDefault)) {
|
|
885
|
+
p.cancel("Operation cancelled.");
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const defaultLocale = inputDefault.trim();
|
|
889
|
+
const inputLocales = await p.text({
|
|
890
|
+
message: "Comma-separated list of supported locales:",
|
|
891
|
+
initialValue: `${defaultLocale},tr`,
|
|
892
|
+
validate: (v) => !v || !v.trim() ? "Locales list cannot be empty" : void 0
|
|
893
|
+
});
|
|
894
|
+
if (p.isCancel(inputLocales)) {
|
|
895
|
+
p.cancel("Operation cancelled.");
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
const localesInput = inputLocales.trim();
|
|
899
|
+
if (options.ai === void 0 && options.noAi === void 0) {
|
|
900
|
+
const confirmAi = await p.confirm({
|
|
901
|
+
message: "Configure AI agent support (Claude/Cursor skills, .mcp.json, AGENTS.md)?",
|
|
902
|
+
initialValue: true
|
|
903
|
+
});
|
|
904
|
+
if (!p.isCancel(confirmAi)) options.ai = confirmAi;
|
|
905
|
+
}
|
|
906
|
+
return {
|
|
907
|
+
framework,
|
|
908
|
+
defaultLocale,
|
|
909
|
+
localesInput
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
//#endregion
|
|
913
|
+
//#region src/commands/init/quickstart.ts
|
|
914
|
+
/** What `dialex init` prints for each framework, after the files are written. */
|
|
915
|
+
const SNIPPETS = {
|
|
916
|
+
hono: `
|
|
990
917
|
import { Hono } from "hono";
|
|
991
918
|
import { dialexHono } from "dialexjs/hono";
|
|
992
919
|
import { dialex } from "./src/dialex.generated.js";
|
|
@@ -998,10 +925,8 @@ app.get("/", (c) => {
|
|
|
998
925
|
const dict = c.var.getDictionary("home");
|
|
999
926
|
return c.json({ title: dict.title });
|
|
1000
927
|
});
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
case "fastify":
|
|
1004
|
-
logger.log(`
|
|
928
|
+
`,
|
|
929
|
+
fastify: `
|
|
1005
930
|
import Fastify from "fastify";
|
|
1006
931
|
import { dialexPlugin } from "dialexjs/fastify";
|
|
1007
932
|
import { dialex } from "./src/dialex.generated.js";
|
|
@@ -1013,10 +938,8 @@ app.get("/", (req) => {
|
|
|
1013
938
|
const dict = req.getDictionary("home");
|
|
1014
939
|
return { title: dict.title };
|
|
1015
940
|
});
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
case "express":
|
|
1019
|
-
logger.log(`
|
|
941
|
+
`,
|
|
942
|
+
express: `
|
|
1020
943
|
import express from "express";
|
|
1021
944
|
import { dialexExpress } from "dialexjs/express";
|
|
1022
945
|
import { dialex } from "./src/dialex.generated.js";
|
|
@@ -1028,10 +951,8 @@ app.get("/", (req, res) => {
|
|
|
1028
951
|
const dict = req.getDictionary("home");
|
|
1029
952
|
res.json({ title: dict.title });
|
|
1030
953
|
});
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
case "koa":
|
|
1034
|
-
logger.log(`
|
|
954
|
+
`,
|
|
955
|
+
koa: `
|
|
1035
956
|
import Koa from "koa";
|
|
1036
957
|
import { dialexKoa } from "dialexjs/koa";
|
|
1037
958
|
import { dialex } from "./src/dialex.generated.js";
|
|
@@ -1043,10 +964,8 @@ app.use((ctx) => {
|
|
|
1043
964
|
const dict = ctx.getDictionary("home");
|
|
1044
965
|
ctx.body = { title: dict.title };
|
|
1045
966
|
});
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
case "nestjs":
|
|
1049
|
-
logger.log(`
|
|
967
|
+
`,
|
|
968
|
+
nestjs: `
|
|
1050
969
|
import { Module } from "@nestjs/common";
|
|
1051
970
|
import { DialexModule } from "dialexjs/nestjs";
|
|
1052
971
|
import { dialex } from "./src/dialex.generated.js";
|
|
@@ -1055,10 +974,8 @@ import { dialex } from "./src/dialex.generated.js";
|
|
|
1055
974
|
imports: [DialexModule.forRoot({ ...dialex })],
|
|
1056
975
|
})
|
|
1057
976
|
export class AppModule {}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
case "next":
|
|
1061
|
-
logger.log(`
|
|
977
|
+
`,
|
|
978
|
+
next: `
|
|
1062
979
|
// src/dialex.ts
|
|
1063
980
|
import { createDialexServer } from "dialexjs/server";
|
|
1064
981
|
import { dialex } from "./dialex.generated";
|
|
@@ -1079,10 +996,8 @@ export default async function Page({ params }: { params: Promise<{ locale: strin
|
|
|
1079
996
|
// import { DialexProvider } from "dialexjs/react";
|
|
1080
997
|
// import { dialex } from "./dialex.generated";
|
|
1081
998
|
// export const Providers = ({ children }) => <DialexProvider {...dialex}>{children}</DialexProvider>;
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
case "react":
|
|
1085
|
-
logger.log(`
|
|
999
|
+
`,
|
|
1000
|
+
react: `
|
|
1086
1001
|
// src/main.tsx
|
|
1087
1002
|
import { createRoot } from "react-dom/client";
|
|
1088
1003
|
import { DialexProvider } from "dialexjs/react";
|
|
@@ -1096,10 +1011,8 @@ createRoot(document.getElementById("root")!).render(
|
|
|
1096
1011
|
);
|
|
1097
1012
|
|
|
1098
1013
|
// In components: const dict = useDictionary("home")
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
case "elysia":
|
|
1102
|
-
logger.log(`
|
|
1014
|
+
`,
|
|
1015
|
+
elysia: `
|
|
1103
1016
|
import { Elysia } from "elysia";
|
|
1104
1017
|
import { dialexElysia } from "dialexjs/elysia";
|
|
1105
1018
|
import { dialex } from "./src/dialex.generated.js";
|
|
@@ -1108,10 +1021,8 @@ new Elysia()
|
|
|
1108
1021
|
.use(dialexElysia({ ...dialex }))
|
|
1109
1022
|
.get("/", ({ getDictionary }) => ({ title: getDictionary("home").title }))
|
|
1110
1023
|
.listen(3000);
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
case "sveltekit":
|
|
1114
|
-
logger.log(`
|
|
1024
|
+
`,
|
|
1025
|
+
sveltekit: `
|
|
1115
1026
|
// src/hooks.server.ts
|
|
1116
1027
|
import { dialexHandle } from "dialexjs/sveltekit";
|
|
1117
1028
|
import { dialex } from "./dialex.generated.js";
|
|
@@ -1120,10 +1031,8 @@ export const handle = dialexHandle({ ...dialex });
|
|
|
1120
1031
|
|
|
1121
1032
|
// src/app.html: <html lang="%dialex.lang%">
|
|
1122
1033
|
// In load functions: locals.getDictionary("home")
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
case "astro":
|
|
1126
|
-
logger.log(`
|
|
1034
|
+
`,
|
|
1035
|
+
astro: `
|
|
1127
1036
|
// src/middleware.ts
|
|
1128
1037
|
import { dialexAstro } from "dialexjs/astro";
|
|
1129
1038
|
import { dialex } from "./dialex.generated.js";
|
|
@@ -1131,10 +1040,8 @@ import { dialex } from "./dialex.generated.js";
|
|
|
1131
1040
|
export const onRequest = dialexAstro({ ...dialex });
|
|
1132
1041
|
|
|
1133
1042
|
// In pages: Astro.locals.getDictionary("home")
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
case "vue":
|
|
1137
|
-
logger.log(`
|
|
1043
|
+
`,
|
|
1044
|
+
vue: `
|
|
1138
1045
|
// src/main.ts
|
|
1139
1046
|
import { createApp } from "vue";
|
|
1140
1047
|
import { createDialex } from "dialexjs/vue";
|
|
@@ -1144,10 +1051,8 @@ import App from "./App.vue";
|
|
|
1144
1051
|
createApp(App).use(createDialex({ ...dialex })).mount("#app");
|
|
1145
1052
|
|
|
1146
1053
|
// In components: const dict = useDictionary("home")
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
case "svelte":
|
|
1150
|
-
logger.log(`
|
|
1054
|
+
`,
|
|
1055
|
+
svelte: `
|
|
1151
1056
|
<!-- src/App.svelte -->
|
|
1152
1057
|
<script lang="ts">
|
|
1153
1058
|
import { provideDialex, useDictionary } from "dialexjs/svelte";
|
|
@@ -1159,10 +1064,8 @@ createApp(App).use(createDialex({ ...dialex })).mount("#app");
|
|
|
1159
1064
|
|
|
1160
1065
|
<h1>{$home.title}</h1>
|
|
1161
1066
|
<button onclick={() => setLocale("tr")}>TR</button>
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
case "solid":
|
|
1165
|
-
logger.log(`
|
|
1067
|
+
`,
|
|
1068
|
+
solid: `
|
|
1166
1069
|
// src/index.tsx
|
|
1167
1070
|
import { render } from "solid-js/web";
|
|
1168
1071
|
import { DialexProvider } from "dialexjs/solid";
|
|
@@ -1179,10 +1082,8 @@ render(
|
|
|
1179
1082
|
);
|
|
1180
1083
|
|
|
1181
1084
|
// In components: const home = useDictionary("home"); then {home().title}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
case "react-router":
|
|
1185
|
-
logger.log(`
|
|
1085
|
+
`,
|
|
1086
|
+
"react-router": `
|
|
1186
1087
|
// app/dialex.server.ts
|
|
1187
1088
|
import { createDialexHandler } from "dialexjs/web";
|
|
1188
1089
|
import { dialex } from "./dialex.generated";
|
|
@@ -1208,10 +1109,8 @@ export default function App() {
|
|
|
1208
1109
|
</DialexProvider>
|
|
1209
1110
|
);
|
|
1210
1111
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
case "preact":
|
|
1214
|
-
logger.log(`
|
|
1112
|
+
`,
|
|
1113
|
+
preact: `
|
|
1215
1114
|
// src/index.tsx
|
|
1216
1115
|
import { render } from "preact";
|
|
1217
1116
|
import { DialexProvider } from "dialexjs/preact";
|
|
@@ -1226,10 +1125,8 @@ render(
|
|
|
1226
1125
|
);
|
|
1227
1126
|
|
|
1228
1127
|
// In components: const dict = useDictionary("home")
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
case "qwik":
|
|
1232
|
-
logger.log(`
|
|
1128
|
+
`,
|
|
1129
|
+
qwik: `
|
|
1233
1130
|
// src/i18n.ts
|
|
1234
1131
|
import { createDialexQwik } from "dialexjs/qwik";
|
|
1235
1132
|
import { dialex } from "./dialex.generated";
|
|
@@ -1244,10 +1141,8 @@ export default component$(() => {
|
|
|
1244
1141
|
});
|
|
1245
1142
|
|
|
1246
1143
|
// In components: const home = useDictionary("home"); then {home().title}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
case "lit":
|
|
1250
|
-
logger.log(`
|
|
1144
|
+
`,
|
|
1145
|
+
lit: `
|
|
1251
1146
|
// src/i18n.ts
|
|
1252
1147
|
import { createDialex } from "dialexjs/lit";
|
|
1253
1148
|
import { dialex } from "./dialex.generated";
|
|
@@ -1266,10 +1161,8 @@ class MyElement extends LitElement {
|
|
|
1266
1161
|
return html\`<h1>\${this.dx.dictionary("home").title}</h1>\`;
|
|
1267
1162
|
}
|
|
1268
1163
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
case "react-native":
|
|
1272
|
-
logger.log(`
|
|
1164
|
+
`,
|
|
1165
|
+
"react-native": `
|
|
1273
1166
|
// App.tsx
|
|
1274
1167
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
1275
1168
|
import { getLocales } from "expo-localization";
|
|
@@ -1289,10 +1182,8 @@ export default function App() {
|
|
|
1289
1182
|
}
|
|
1290
1183
|
|
|
1291
1184
|
// In components: const dict = useDictionary("home")
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
case "hapi":
|
|
1295
|
-
logger.log(`
|
|
1185
|
+
`,
|
|
1186
|
+
hapi: `
|
|
1296
1187
|
import Hapi from "@hapi/hapi";
|
|
1297
1188
|
import { dialexPlugin } from "dialexjs/hapi";
|
|
1298
1189
|
import { dialex } from "./src/dialex.generated.js";
|
|
@@ -1305,10 +1196,8 @@ server.route({
|
|
|
1305
1196
|
path: "/",
|
|
1306
1197
|
handler: (request) => ({ title: request.getDictionary("home").title }),
|
|
1307
1198
|
});
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
case "adonis":
|
|
1311
|
-
logger.log(`
|
|
1199
|
+
`,
|
|
1200
|
+
adonis: `
|
|
1312
1201
|
// app/middleware/dialex_middleware.ts
|
|
1313
1202
|
import { createDialexMiddleware } from "dialexjs/adonis";
|
|
1314
1203
|
import { dialex } from "#start/dialex.generated";
|
|
@@ -1319,10 +1208,8 @@ export default createDialexMiddleware({ ...dialex });
|
|
|
1319
1208
|
router.use([() => import("#middleware/dialex_middleware")]);
|
|
1320
1209
|
|
|
1321
1210
|
// In handlers: ctx.locale, ctx.getDictionary("home")
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
case "trpc":
|
|
1325
|
-
logger.log(`
|
|
1211
|
+
`,
|
|
1212
|
+
trpc: `
|
|
1326
1213
|
// src/trpc.ts
|
|
1327
1214
|
import { initTRPC } from "@trpc/server";
|
|
1328
1215
|
import { createDialexContext } from "dialexjs/trpc";
|
|
@@ -1337,23 +1224,70 @@ const t = initTRPC.context<Awaited<ReturnType<typeof createContext>>>().create()
|
|
|
1337
1224
|
export const appRouter = t.router({
|
|
1338
1225
|
greeting: t.procedure.query(({ ctx }) => ctx.getDictionary("home").title),
|
|
1339
1226
|
});
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
case "nuxt":
|
|
1343
|
-
logger.log(`
|
|
1227
|
+
`,
|
|
1228
|
+
nuxt: `
|
|
1344
1229
|
// nuxt.config.ts
|
|
1345
1230
|
export default defineNuxtConfig({
|
|
1346
1231
|
modules: ["dialexjs/nuxt"],
|
|
1347
1232
|
});
|
|
1348
1233
|
|
|
1349
1234
|
// In components: const dict = useDictionary("home")
|
|
1350
|
-
`
|
|
1351
|
-
|
|
1352
|
-
|
|
1235
|
+
`
|
|
1236
|
+
};
|
|
1237
|
+
/** Printed for a framework without a snippet of its own. */
|
|
1238
|
+
const FALLBACK = `
|
|
1353
1239
|
Import dictionaries and register your framework middleware to begin!
|
|
1354
1240
|
Run \`npm run dx:generate\` whenever you add new dictionary files.
|
|
1355
|
-
|
|
1356
|
-
|
|
1241
|
+
`;
|
|
1242
|
+
function printQuickstart(framework) {
|
|
1243
|
+
logger.log("");
|
|
1244
|
+
logger.info(pc.bold(`Quick start for ${framework.toUpperCase()}:`));
|
|
1245
|
+
logger.log(Object.hasOwn(SNIPPETS, framework) ? SNIPPETS[framework] : FALLBACK);
|
|
1246
|
+
}
|
|
1247
|
+
//#endregion
|
|
1248
|
+
//#region src/commands/init/starter-dictionary.ts
|
|
1249
|
+
/** Writes `home.content.ts` with one entry per locale, unless it is already there. */
|
|
1250
|
+
function createStarterDictionary(root, framework, localeList) {
|
|
1251
|
+
const targetDir = (framework === "react-router" || framework === "adonis") && fs.existsSync(path.join(root, "app")) ? path.join(root, "app") : fs.existsSync(path.join(root, "src")) ? path.join(root, "src") : root;
|
|
1252
|
+
const dictPath = path.join(targetDir, "home.content.ts");
|
|
1253
|
+
if (!fs.existsSync(dictPath)) {
|
|
1254
|
+
const dictContent = `import { defineDictionary } from "dialexjs";
|
|
1255
|
+
|
|
1256
|
+
export default defineDictionary("home", {
|
|
1257
|
+
${localeList.map((loc) => ` ${loc}: {
|
|
1258
|
+
title: "Welcome to Dialex (${loc})!",
|
|
1259
|
+
description: "Type-safe internationalization.",
|
|
1260
|
+
greeting: (name: string) => "Hello, " + name + "!",
|
|
1261
|
+
},`).join("\n")}
|
|
1262
|
+
});
|
|
1263
|
+
`;
|
|
1264
|
+
fs.writeFileSync(dictPath, dictContent, "utf-8");
|
|
1265
|
+
logger.success(`Created starter dictionary at ${pc.bold(path.relative(root, dictPath))}`);
|
|
1266
|
+
} else logger.info("home.content.ts already exists, skipping creation.");
|
|
1267
|
+
}
|
|
1268
|
+
//#endregion
|
|
1269
|
+
//#region src/commands/init/index.ts
|
|
1270
|
+
async function runInit(options = {}) {
|
|
1271
|
+
const root = options.cwd || process.cwd();
|
|
1272
|
+
const detected = detectFramework(root);
|
|
1273
|
+
const isInteractive = !options.yes && process.stdin.isTTY;
|
|
1274
|
+
let framework = options.framework || detected.framework;
|
|
1275
|
+
let defaultLocale = options.defaultLocale || "en";
|
|
1276
|
+
let localesInput = options.locales || "en,tr";
|
|
1277
|
+
if (isInteractive) {
|
|
1278
|
+
const answers = await askInit(options, detected);
|
|
1279
|
+
if (!answers) return;
|
|
1280
|
+
({framework, defaultLocale, localesInput} = answers);
|
|
1281
|
+
} else logger.info(`Auto-detected framework: ${pc.bold(framework)} (${detected.matchedRule})`);
|
|
1282
|
+
const localeList = Array.from(/* @__PURE__ */ new Set([defaultLocale, ...localesInput.split(",").map((s) => s.trim()).filter(Boolean)]));
|
|
1283
|
+
await writeDialexConfig(root, framework, defaultLocale, localeList);
|
|
1284
|
+
if (framework === "nuxt") await registerNuxtModule(root);
|
|
1285
|
+
createStarterDictionary(root, framework, localeList);
|
|
1286
|
+
generateDictionaries(root, { static: true });
|
|
1287
|
+
logger.success("Generated types and standalone dictionary registry!");
|
|
1288
|
+
updatePackageJson(root);
|
|
1289
|
+
if (options.ai === true || options.yes && options.ai !== false && options.noAi !== true) setupAiSupport(root);
|
|
1290
|
+
printQuickstart(framework);
|
|
1357
1291
|
logger.info("Keep dialex.generated.ts current: run `dx generate --watch` while you develop, or use the Dialex VS Code extension, which regenerates on save.");
|
|
1358
1292
|
if (isInteractive) p.outro(pc.green("✔ dialex is configured and ready to go!"));
|
|
1359
1293
|
}
|