@lingui/cli
Version:
CLI for working wit message catalogs
172 lines (171 loc) • 7.55 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = command;
const picocolors_1 = __importDefault(require("picocolors"));
const chokidar_1 = __importDefault(require("chokidar"));
const commander_1 = require("commander");
const path_1 = __importDefault(require("path"));
const conf_1 = require("@lingui/conf");
const api_1 = require("./api");
const stats_1 = require("./api/stats");
const help_1 = require("./api/help");
const ora_1 = __importDefault(require("ora"));
const normalize_path_1 = __importDefault(require("normalize-path"));
async function command(config, options) {
options.verbose && console.log("Extracting messages from source files…");
const catalogs = await (0, api_1.getCatalogs)(config);
const catalogStats = {};
let commandSuccess = true;
const spinner = (0, ora_1.default)().start();
await Promise.all(catalogs.map(async (catalog) => {
const result = await catalog.make(Object.assign(Object.assign({}, options), { orderBy: config.orderBy }));
catalogStats[(0, normalize_path_1.default)(path_1.default.relative(config.rootDir, catalog.path))] = result || {};
commandSuccess && (commandSuccess = Boolean(result));
}));
if (commandSuccess) {
spinner.succeed();
}
else {
spinner.fail();
}
Object.entries(catalogStats).forEach(([key, value]) => {
console.log(`Catalog statistics for ${key}: `);
console.log((0, stats_1.printStats)(config, value).toString());
console.log();
});
if (!options.watch) {
console.log(`(Use "${picocolors_1.default.yellow((0, help_1.helpRun)("extract"))}" to update catalogs with new messages.)`);
console.log(`(Use "${picocolors_1.default.yellow((0, help_1.helpRun)("compile"))}" to compile catalogs for production. Alternatively, use bundler plugins: https://lingui.dev/ref/cli#compiling-catalogs-in-ci)`);
}
// If service key is present in configuration, synchronize with cloud translation platform
if (typeof config.service === "object" &&
config.service.name &&
config.service.name.length) {
const moduleName = config.service.name.charAt(0).toLowerCase() + config.service.name.slice(1);
try {
const module = require(`./services/${moduleName}`);
await module
.default(config, options)
.then(console.log)
.catch(console.error);
}
catch (err) {
console.error(`Can't load service module ${moduleName}`, err);
}
}
return commandSuccess;
}
if (require.main === module) {
commander_1.program
.option("--config <path>", "Path to the config file")
.option("--locale <locale, [...]>", "Only extract the specified locales", (value) => {
return value
.split(",")
.map((s) => s.trim())
.filter(Boolean);
})
.option("--overwrite", "Overwrite translations for source locale")
.option("--clean", "Remove obsolete translations")
.option("--debounce <delay>", "Debounces extraction for given amount of milliseconds")
.option("--verbose", "Verbose output")
.option("--convert-from <format>", "Convert from previous format of message catalogs")
.option("--watch", "Enables Watch Mode")
.parse(process.argv);
const options = commander_1.program.opts();
const config = (0, conf_1.getConfig)({
configPath: options.config,
});
let hasErrors = false;
const prevFormat = options.convertFrom;
if (prevFormat && config.format === prevFormat) {
hasErrors = true;
console.error("Trying to migrate message catalog to the same format");
console.error(`Set ${picocolors_1.default.bold("new")} format in LinguiJS configuration\n` +
` and ${picocolors_1.default.bold("previous")} format using --convert-from option.`);
console.log();
console.log(`Example: Convert from lingui format to minimal`);
console.log(picocolors_1.default.yellow((0, help_1.helpRun)(`extract --convert-from lingui`)));
process.exit(1);
}
if (options.locale) {
const missingLocale = options.locale.find((l) => !config.locales.includes(l));
if (missingLocale) {
hasErrors = true;
console.error(`Locale ${picocolors_1.default.bold(missingLocale)} does not exist.`);
console.error();
}
}
if (hasErrors)
process.exit(1);
const extract = (filePath) => {
return command(config, {
verbose: options.watch || options.verbose || false,
clean: options.watch ? false : options.clean || false,
overwrite: options.watch || options.overwrite || false,
locale: options.locale,
watch: options.watch || false,
files: (filePath === null || filePath === void 0 ? void 0 : filePath.length) ? filePath : undefined,
prevFormat,
});
};
const changedPaths = new Set();
let debounceTimer;
let previousExtract = Promise.resolve(true);
const dispatchExtract = (filePath) => {
// Skip debouncing if not enabled but still chain them so no racing issue
// on deleting the tmp folder.
if (!options.debounce) {
previousExtract = previousExtract.then(() => extract(filePath));
return previousExtract;
}
filePath === null || filePath === void 0 ? void 0 : filePath.forEach((path) => changedPaths.add(path));
// CLear the previous timer if there is any, and schedule the next
debounceTimer && clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
const filePath = [...changedPaths];
changedPaths.clear();
await extract(filePath);
}, options.debounce);
};
// Check if Watch Mode is enabled
if (options.watch) {
console.info(picocolors_1.default.bold("Initializing Watch Mode..."));
(async function initWatch() {
const catalogs = await (0, api_1.getCatalogs)(config);
const paths = [];
const ignored = [];
catalogs.forEach((catalog) => {
paths.push(...catalog.include);
ignored.push(...catalog.exclude);
});
const watcher = chokidar_1.default.watch(paths, {
ignored: ["/(^|[/\\])../", ...ignored],
persistent: true,
});
const onReady = () => {
console.info(picocolors_1.default.green(picocolors_1.default.bold("Watcher is ready!")));
watcher
.on("add", (path) => dispatchExtract([path]))
.on("change", (path) => dispatchExtract([path]));
};
watcher.on("ready", () => onReady());
})();
}
else if (commander_1.program.args) {
// this behaviour occurs when we extract files by his name
// for ex: lingui extract src/app, this will extract only files included in src/app
extract(commander_1.program.args).then((result) => {
if (!result)
process.exit(1);
});
}
else {
extract().then((result) => {
if (!result)
process.exit(1);
});
}
}