diff --git a/examples/android/languine.config.ts b/examples/android/languine.config.mjs similarity index 67% rename from examples/android/languine.config.ts rename to examples/android/languine.config.mjs index a1f072f..85ca54f 100644 --- a/examples/android/languine.config.ts +++ b/examples/android/languine.config.mjs @@ -1,7 +1,5 @@ -import { defineConfig } from "languine"; - -export default defineConfig({ - version: "0.6.7", +export default { + version: "0.6.8", locale: { source: "en", targets: ["es"], @@ -15,4 +13,4 @@ export default defineConfig({ provider: "openai", model: "gpt-4-turbo", }, -}); +}; diff --git a/packages/cli/package.json b/packages/cli/package.json index a620ac3..73300da 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "languine", - "version": "0.6.7", + "version": "0.6.8", "type": "module", "bin": "dist/index.js", "main": "dist/config.js", diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index fd9d213..7108108 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -6,7 +6,7 @@ import chalk from "chalk"; import { installDependencies } from "../install.js"; import { providers } from "../providers.js"; import type { PresetOptions, Provider } from "../types.js"; -import { configPath } from "../utils.js"; +import { configFile, generateConfig } from "../utils.js"; async function createDirectoryOrFile(filePath: string, isDirectory = false) { try { @@ -78,6 +78,19 @@ export async function init(preset?: string) { intro("Let's set up your i18n configuration"); + const configType = (await select({ + message: "What type of config file would you like to use?", + options: [ + { value: "ts", label: "TypeScript", hint: "recommended" }, + { value: "mjs", label: "JavaScript" }, + ], + })) as "ts" | "mjs"; + + // Install dependencies only if we're using TypeScript for types in config + if (configType === "ts") { + await installDependencies(); + } + const sourceLanguage = (await select({ message: "What is your source language?", options: [ @@ -169,27 +182,16 @@ export async function init(preset?: string) { message: "Which model should be used for translations?", options: models, })) as string; - const configContent = `import { defineConfig } from "languine"; - -export default defineConfig({ - version: "${require("../../package.json").version}", - locale: { - source: "${sourceLanguage}", - targets: ${JSON.stringify(targetLanguages.split(",").map((l) => l.trim()))}, - }, - files: { - ${fileFormat.includes("-") ? `"${fileFormat}"` : fileFormat}: { - include: ${JSON.stringify(filesPatterns)}, - }, - }, - llm: { - provider: "${provider}", - model: "${model}", - }, -})`; - - // Install dependencies - await installDependencies(); + const configContent = generateConfig({ + version: require("../../package.json").version, + sourceLanguage, + targetLanguages: targetLanguages.split(",").map((l) => l.trim()), + fileFormat, + filesPatterns, + provider, + model, + configType, + }); try { const targetLangs = [ @@ -217,7 +219,8 @@ export default defineConfig({ } // Write config file - await fs.writeFile(configPath, configContent); + const { path: configPath } = await configFile(configType); + await fs.writeFile(configPath, configContent, "utf-8"); outro( "Configuration file and language files/directories created successfully!", ); diff --git a/packages/cli/src/commands/instructions.ts b/packages/cli/src/commands/instructions.ts index 0f09c9b..3428216 100644 --- a/packages/cli/src/commands/instructions.ts +++ b/packages/cli/src/commands/instructions.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import { intro, outro, text } from "@clack/prompts"; import chalk from "chalk"; -import { configPath, getConfig } from "../utils.js"; +import { configFile, getConfig } from "../utils.js"; export async function instructions() { intro("Let's customize your translation prompt"); @@ -22,6 +22,7 @@ export async function instructions() { config.instructions = customInstructions as string; // Write updated config + const { path: configPath } = await configFile(); await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8"); outro(chalk.green("Translation prompt updated successfully!")); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 5028e08..fcd0691 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node import "./envs.js"; -import { select } from "@clack/prompts"; +import { isCancel, select } from "@clack/prompts"; import chalk from "chalk"; import dedent from "dedent"; import { clean } from "./commands/clean.js"; @@ -46,6 +46,10 @@ const command = ], })); +if (isCancel(command)) { + process.exit(0); +} + const targetLocale = process.argv[3]; const preset = process.argv.includes("--preset") ? process.argv[process.argv.indexOf("--preset") + 1] @@ -53,15 +57,15 @@ const preset = process.argv.includes("--preset") const force = process.argv.includes("--force") || process.argv.includes("-f"); if (command === "init") { - init(preset); + await init(preset); } else if (command === "translate") { - translate(targetLocale, force); + await translate(targetLocale, force); } else if (command === "instructions") { - instructions(); + await instructions(); } else if (command === "diff") { - diff(); + await diff(); } else if (command === "clean") { - clean(); + await clean(); } else if (command === "available") { console.log(dedent` ${chalk.cyan("init")} Initialize a new Languine configuration diff --git a/packages/cli/src/install.ts b/packages/cli/src/install.ts index ac3fe62..8d24495 100644 --- a/packages/cli/src/install.ts +++ b/packages/cli/src/install.ts @@ -5,15 +5,15 @@ export async function installDependencies() { const s = spinner(); try { - s.start("Installing dependencies..."); + s.start("Installing Languine as a dev dependency..."); const pm = await findPreferredPM(); await execAsync(`${pm?.name} install languine -D`); - s.stop("Dependencies installed successfully"); + s.stop("Languine installed successfully"); } catch (error) { - s.stop("Failed to install dependencies"); + s.stop("Failed to install Languine"); throw error; } } diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index e735595..b38f40a 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -9,6 +9,8 @@ import type { Jiti } from "jiti"; import preferredPM from "preferred-pm"; import type { Config } from "./types.js"; +const CONFIG_NAME = "languine.config"; + export async function getApiKey(name: string, key: string) { if (key in process.env) { return process.env[key]; @@ -62,27 +64,84 @@ export async function getApiKey(name: string, key: string) { })(); } -export const configPath = path.join(process.cwd(), "languine.config.ts"); -const name = "languine.config"; +export function generateConfig({ + version, + sourceLanguage, + targetLanguages, + fileFormat, + filesPatterns, + provider, + model, + configType, +}: { + version: string; + sourceLanguage: string; + targetLanguages: string[]; + fileFormat: string; + filesPatterns: string[]; + provider: string; + model: string; + configType: "ts" | "mjs"; +}) { + const formatKey = fileFormat.includes("-") ? `"${fileFormat}"` : fileFormat; + + const configBody = `{ + version: "${version}", + locale: { + source: "${sourceLanguage}", + targets: [${targetLanguages.map((l) => `"${l}"`).join(", ")}], + }, + files: { + ${formatKey}: { + include: [${filesPatterns.map((p) => `"${p}"`).join(", ")}], + } + }, + llm: { + provider: "${provider}", + model: "${model}", + } +}`; -let jiti: Jiti | undefined; + if (configType === "mjs") { + return `export default ${configBody}`; + } -export async function getConfig(): Promise { - let config: Config; - let target: string | undefined; + return `import { defineConfig } from "languine"; + +export default defineConfig(${configBody})`; +} + +export async function configFile(configType?: "ts" | "mjs") { const files = await fs.readdir(process.cwd()); + const configFile = files.find( + (file: string) => + file.startsWith(`${CONFIG_NAME}.`) && + (file.endsWith(".ts") || file.endsWith(".mjs")), + ); + + // If configType is specified, use that + // Otherwise try to detect from existing file, falling back to ts + const format = configType || (configFile?.endsWith(".mjs") ? "mjs" : "ts"); + const filePath = path.join( + process.cwd(), + configFile || `${CONFIG_NAME}.${format}`, + ); + + return { + path: filePath, + format, + }; +} - for (const file of files) { - if (file.startsWith(`${name}.`)) { - target = path.resolve(file); - break; - } - } +let jiti: Jiti | undefined; + +export async function getConfig(): Promise { + const { path: filePath, format } = await configFile(); - if (!target) { + if (!filePath) { outro( chalk.red( - "Could not find languine.config.ts. Run 'languine init' first.", + `Could not find ${CONFIG_NAME}.${format}. Run 'languine init' first.`, ), ); @@ -90,7 +149,7 @@ export async function getConfig(): Promise { } try { - const configModule = await import(pathToFileURL(target).href); + const configModule = await import(pathToFileURL(filePath).href); return configModule.default; } catch (error) { const { createJiti } = await import("jiti"); @@ -105,7 +164,7 @@ export async function getConfig(): Promise { }); return await jiti - .import(target) + .import(filePath) .then((mod) => (mod as unknown as { default: Config }).default); } }