@lingui/cli
Version:
CLI for working wit message catalogs
153 lines (152 loc) • 7.11 kB
JavaScript
;
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);
}
});
}
}