UNPKG

@hi18n/cli

Version:

Message internationalization meets immutability and type-safety - command line tool

116 lines (107 loc) 3.13 kB
import fs from "node:fs"; import path from "node:path"; import { glob } from "glob"; import { TSESLint } from "@typescript-eslint/utils"; import { getCollectCatalogDefinitionsRule, type CatalogDef, } from "@hi18n/eslint-plugin/internal-rules"; import { loadConfig } from "./config.ts"; import type { Hi18nCatalogData } from "@hi18n/tools-core"; export type Options = { cwd: string; }; export async function export_(options: Options): Promise<void> { const { cwd } = options; const config = await loadConfig(cwd); const include = config.include; const exclude = config.exclude; if (include === undefined || include.length === 0) { throw new Error("No include specified"); } if (!config.connector) { throw new Error("Connector not configured"); } const catalogDefs: CatalogDef[] = []; const linterConfig: TSESLint.FlatConfig.Config = { languageOptions: { parser: config.parser as TSESLint.Parser.LooseParserModule, parserOptions: config.parserOptions, }, plugins: { "@hi18n": { rules: { "collect-catalog-definitions": getCollectCatalogDefinitionsRule( (record) => catalogDefs.push(record), ), }, }, }, }; const linter = new TSESLint.Linter({ cwd }); const files: string[] = []; for (const includeGlob of include) { files.push( ...(await glob(includeGlob, { cwd, nodir: true, ignore: exclude ?? [], })), ); } for (const relative of files) { const filename = path.join(cwd, relative); const source = await fs.promises.readFile(filename, "utf-8"); const messages = linter.verify( source, [ { files: ["**"] }, { ...linterConfig, rules: { "@hi18n/collect-catalog-definitions": [ "error", { requestMessages: true, }, ], }, }, ], { filename }, ); checkMessages(relative, messages); } const translations: Record<string, Hi18nCatalogData> = {}; for (const catalogDef of catalogDefs) { if (!catalogDef.locale) { throw new Error(`No locale given in ${catalogDef.catalogLocation.path}`); } const locale = catalogDef.locale; const catalogData = (translations[locale] ??= {}); for (const [key, messageDef] of Object.entries(catalogDef.messages)) { catalogData[key] = { raw: messageDef.value }; } } const c = config.connector.connector( config.configPath, config.connectorOptions, ); if (!c.exportData) { throw new Error("This connector doesn't support exporting"); } await c.exportData({ translations }); } function checkMessages( filepath: string, messages: TSESLint.Linter.LintMessage[], ) { for (const message of messages) { if (/^Definition for rule .* was not found\.$/.test(message.message)) { // We load ESLint with minimal rules. Ignore the "missing rule" error. continue; } if (message.severity >= 2) throw new Error(`Error on ${filepath}: ${message.message}`); } }