@lingui/cli
Version:
CLI for working wit message catalogs
187 lines (186 loc) • 8.35 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 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!");
});
}
}