@lingui/cli
Version:
CLI for working wit message catalogs
128 lines (127 loc) • 6.59 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = command;
const commander_1 = require("commander");
const conf_1 = require("@lingui/conf");
const path_1 = __importDefault(require("path"));
const formats_1 = require("./api/formats");
const promises_1 = __importDefault(require("fs/promises"));
const normalize_path_1 = __importDefault(require("normalize-path"));
const bundleSource_1 = require("./extract-experimental/bundleSource");
const getEntryPoints_1 = require("./extract-experimental/getEntryPoints");
const picocolors_1 = __importDefault(require("picocolors"));
const threads_1 = require("threads");
const resolveWorkersOptions_1 = require("./api/resolveWorkersOptions");
const extractFromBundleAndWrite_1 = require("./extract-experimental/extractFromBundleAndWrite");
async function command(linguiConfig, options) {
var _a;
options.verbose && console.log("Extracting messages from source files…");
const config = (_a = linguiConfig.experimental) === null || _a === void 0 ? void 0 : _a.extractor;
if (!config) {
throw new Error("The configuration for experimental extractor is empty. Please read the docs.");
}
console.log(picocolors_1.default.yellow([
"You have using an experimental feature",
"Experimental features are not covered by semver, and may cause unexpected or broken application behavior." +
" Use at your own risk.",
"",
].join("\n")));
// unfortunately we can't use os.tmpdir() in this case
// on windows it might create a folder on a different disk then source code is stored
// (tmpdir would be always on C: but code could be stored on D:)
// and then relative path in sourcemaps produced by esbuild will be broken.
// sourcemaps itself doesn't allow to have absolute windows path, because they are not URL compatible.
// that's why we store esbuild bundles in .lingui folder
const tmpPrefix = ".lingui/";
await promises_1.default.mkdir(tmpPrefix, { recursive: true });
const tempDir = await promises_1.default.mkdtemp(tmpPrefix);
await promises_1.default.rm(tempDir, { recursive: true, force: true });
const bundleResult = await (0, bundleSource_1.bundleSource)(linguiConfig, (0, getEntryPoints_1.getEntryPoints)(config.entries), tempDir, linguiConfig.rootDir);
const stats = [];
let commandSuccess = true;
if (options.workersOptions.poolSize) {
if (!linguiConfig.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("./extract-experimental/workers/extractWorker")), { size: options.workersOptions.poolSize });
try {
for (const outFile of Object.keys(bundleResult.metafile.outputs)) {
const { entryPoint } = bundleResult.metafile.outputs[outFile];
pool.queue(async (extractFromBundleAndWrite) => {
const { success, stat } = await extractFromBundleAndWrite(linguiConfig.resolvedConfigPath, entryPoint, outFile, config.output, options.template, options.locales || linguiConfig.locales, options.clean, options.overwrite);
commandSuccess && (commandSuccess = success);
stats.push({
entry: (0, normalize_path_1.default)(path_1.default.relative(linguiConfig.rootDir, entryPoint)),
content: stat,
});
});
}
await pool.completed();
}
finally {
await pool.terminate(true);
}
}
else {
const format = await (0, formats_1.getFormat)(linguiConfig.format, linguiConfig.formatOptions, linguiConfig.sourceLocale);
for (const outFile of Object.keys(bundleResult.metafile.outputs)) {
const { entryPoint } = bundleResult.metafile.outputs[outFile];
const { success, stat } = await (0, extractFromBundleAndWrite_1.extractFromBundleAndWrite)({
entryPoint,
bundleFile: outFile,
outputPattern: config.output,
format,
linguiConfig,
locales: options.locales || linguiConfig.locales,
overwrite: options.overwrite,
clean: options.clean,
template: options.template,
});
commandSuccess && (commandSuccess = success);
stats.push({
entry: (0, normalize_path_1.default)(path_1.default.relative(linguiConfig.rootDir, entryPoint)),
content: stat,
});
}
}
// cleanup temp directory
await promises_1.default.rm(tempDir, { recursive: true, force: true });
stats
.sort((a, b) => a.entry.localeCompare(b.entry))
.forEach(({ entry, content }) => {
console.log([`Catalog statistics for ${entry}:`, content, ""].join("\n"));
});
return commandSuccess;
}
if (require.main === module) {
commander_1.program
.option("--config <path>", "Path to the config file")
.option("--template", "Extract to template")
.option("--overwrite", "Overwrite translations for source locale")
.option("--clean", "Remove obsolete translations")
.option("--locale <locale, [...]>", "Only extract the specified locales")
.option("--verbose", "Verbose output")
.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")
.parse(process.argv);
const options = commander_1.program.opts();
const config = (0, conf_1.getConfig)({
configPath: options.config,
});
const result = command(config, {
verbose: options.verbose || false,
template: options.template,
locales: (_a = options.locale) === null || _a === void 0 ? void 0 : _a.split(","),
overwrite: options.overwrite,
clean: options.clean,
workersOptions: (0, resolveWorkersOptions_1.resolveWorkersOptions)(options),
}).then(() => {
if (!result)
process.exit(1);
});
}