UNPKG

@lingui/cli

Version:

CLI for working wit message catalogs

187 lines (186 loc) 8.35 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.command = command; const picocolors_1 = __importDefault(require("picocolors")); const chokidar_1 = __importDefault(require("chokidar")); const commander_1 = require("commander"); const conf_1 = require("@lingui/conf"); const compile_1 = require("./api/compile"); const help_1 = require("./api/help"); const api_1 = require("./api"); const getCatalogs_1 = require("./api/catalog/getCatalogs"); const normalize_path_1 = __importDefault(require("normalize-path")); const path_1 = __importDefault(require("path")); class ProgramExit extends Error { } async function command(config, options) { const catalogs = await (0, api_1.getCatalogs)(config); // Check config.compile.merge if catalogs for current locale are to be merged into a single compiled file const doMerge = !!config.catalogsMergePath; console.log("Compiling message catalogs…"); let errored = false; await Promise.all(config.locales.map(async (locale) => { try { await compileLocale(locale, catalogs, options, config, doMerge); } catch (err) { if (err instanceof ProgramExit) { errored = true; } else { throw err; } } })); return !errored; } async function compileLocale(locale, catalogs, options, config, doMerge) { let mergedCatalogs = {}; await Promise.all(catalogs.map(async (catalog) => { const { messages, missing: missingMessages } = await catalog.getTranslations(locale, { fallbackLocales: config.fallbackLocales, sourceLocale: config.sourceLocale, }); if (!options.allowEmpty && locale !== config.pseudoLocale && missingMessages.length > 0) { console.error(picocolors_1.default.red(`Error: Failed to compile catalog for locale ${picocolors_1.default.bold(locale)}!`)); if (options.verbose) { console.error(picocolors_1.default.red("Missing translations:")); missingMessages.forEach((missing) => { const source = missing.source || missing.source === missing.id ? `: (${missing.source})` : ""; console.error(`${missing.id}${source}`); }); } else { console.error(picocolors_1.default.red(`Missing ${missingMessages.length} translation(s)`)); } console.error(); throw new ProgramExit(); } if (doMerge) { mergedCatalogs = Object.assign(Object.assign({}, mergedCatalogs), messages); } else { if (!(await compileAndWrite(locale, config, options, catalog, messages))) { throw new ProgramExit(); } } })); if (doMerge) { const result = await compileAndWrite(locale, config, options, await (0, getCatalogs_1.getCatalogForMerge)(config), mergedCatalogs); if (!result) { throw new ProgramExit(); } } } async function compileAndWrite(locale, config, options, catalogToWrite, messages) { const namespace = options.typescript ? "ts" : options.namespace || config.compileNamespace; const { source: compiledCatalog, errors } = (0, compile_1.createCompiledCatalog)(locale, messages, { strict: false, namespace, pseudoLocale: config.pseudoLocale, compilerBabelOptions: config.compilerBabelOptions, }); if (errors.length) { let message = (0, api_1.createCompilationErrorMessage)(locale, errors); if (options.failOnCompileError) { message += `These errors fail command execution because \`--strict\` option passed`; console.error(picocolors_1.default.red(message)); return false; } else { message += `You can fail command execution on these errors by passing \`--strict\` option`; console.error(picocolors_1.default.red(message)); } } let compiledPath = await catalogToWrite.writeCompiled(locale, compiledCatalog, namespace); compiledPath = (0, normalize_path_1.default)(path_1.default.relative(config.rootDir, compiledPath)); options.verbose && console.error(picocolors_1.default.green(`${locale}${compiledPath}`)); return true; } if (require.main === module) { commander_1.program .description("Add compile message catalogs and add language data (plurals) to compiled bundle.") .option("--config <path>", "Path to the config file") .option("--strict", "Disable defaults for missing translations") .option("--verbose", "Verbose output") .option("--typescript", "Create Typescript definition for compiled bundle") .option("--namespace <namespace>", "Specify namespace for compiled bundle. Ex: cjs(default) -> module.exports, es -> export, window.test -> window.test") .option("--watch", "Enables Watch Mode") .option("--debounce <delay>", "Debounces compilation for given amount of milliseconds") .on("--help", function () { console.log("\n Examples:\n"); console.log(" # Compile translations and use defaults or message IDs for missing translations"); console.log(` $ ${(0, help_1.helpRun)("compile")}`); console.log(""); console.log(" # Compile translations but fail when there are missing"); console.log(" # translations (don't replace missing translations with"); console.log(" # default messages or message IDs)"); console.log(` $ ${(0, help_1.helpRun)("compile --strict")}`); }) .parse(process.argv); const options = commander_1.program.opts(); const config = (0, conf_1.getConfig)({ configPath: options.config }); let previousRun = Promise.resolve(true); const compile = () => { previousRun = previousRun.then(() => command(config, { verbose: options.watch || options.verbose || false, allowEmpty: !options.strict, failOnCompileError: !!options.strict, typescript: options.typescript || config.compileNamespace === "ts" || false, namespace: options.namespace, // we want this to be undefined if user does not specify so default can be used })); return previousRun; }; let debounceTimer; const dispatchCompile = () => { // Skip debouncing if not enabled if (!options.debounce) compile(); // CLear the previous timer if there is any, and schedule the next debounceTimer && clearTimeout(debounceTimer); debounceTimer = setTimeout(() => compile(), options.debounce); }; // Check if Watch Mode is enabled if (options.watch) { console.info(picocolors_1.default.bold("Initializing Watch Mode...")); (async function initWatch() { const format = await (0, api_1.getFormat)(config.format, config.formatOptions, config.sourceLocale); const catalogs = await (0, api_1.getCatalogs)(config); const paths = []; config.locales.forEach((locale) => { catalogs.forEach((catalog) => { paths.push(`${catalog.path .replace(/{locale}/g, locale) .replace(/{name}/g, "*")}${format.getCatalogExtension()}`); }); }); const watcher = chokidar_1.default.watch(paths, { persistent: true, }); const onReady = () => { console.info(picocolors_1.default.green(picocolors_1.default.bold("Watcher is ready!"))); watcher .on("add", () => dispatchCompile()) .on("change", () => dispatchCompile()); }; watcher.on("ready", () => onReady()); })(); } else { compile().then((results) => { if (!results) { process.exit(1); } console.log("Done!"); }); } }