UNPKG

@lingui/cli

Version:

CLI for working wit message catalogs

153 lines (152 loc) 7.11 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 help_1 = require("./api/help"); const api_1 = require("./api"); const compileLocale_1 = require("./api/compile/compileLocale"); const threads_1 = require("threads"); const resolveWorkersOptions_1 = require("./api/resolveWorkersOptions"); const ms_1 = __importDefault(require("ms")); async function command(config, options) { const startTime = Date.now(); // 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; if (!options.workersOptions.poolSize) { // single threaded const catalogs = await (0, api_1.getCatalogs)(config); for (const locale of config.locales) { try { await (0, compileLocale_1.compileLocale)(catalogs, locale, options, config, doMerge, console); } catch (err) { if (err.name === "ProgramExit") { errored = true; } else { throw err; } } } } else { if (!config.resolvedConfigPath) { throw new Error("Multithreading is only supported when lingui config loaded from file system, not passed by API"); } options.verbose && console.log(`Use worker pool of size ${options.workersOptions.poolSize}`); const pool = (0, threads_1.Pool)(() => (0, threads_1.spawn)(new threads_1.Worker("./workers/compileWorker")), { size: options.workersOptions.poolSize }); try { for (const locale of config.locales) { pool.queue(async (worker) => { const { logs, error, exitProgram } = await worker.compileLocale(locale, options, doMerge, config.resolvedConfigPath); if (logs.errors) { console.error(logs.errors); } if (exitProgram) { errored = true; return; } if (error) { throw error; } }); } await pool.completed(true); } finally { await pool.terminate(true); } } console.log(`Done in ${(0, ms_1.default)(Date.now() - startTime)}`); return !errored; } if (require.main === module) { commander_1.program .description("Compile message catalogs 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("--workers <n>", "Number of worker threads to use (default: CPU count - 1, capped at 8). Pass `--workers 1` to disable worker threads and run everything in a single process") .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") .option("--output-prefix <prefix>", "Add a custom string to the beginning of compiled files (header/prefix). Useful for tools like linters or coverage directives. Defaults to '/*eslint-disable*/'") .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, workersOptions: (0, resolveWorkersOptions_1.resolveWorkersOptions)(options), 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 outputPrefix: options.outputPrefix, })); 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); } }); } }