"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);
            }
        });
    }
}
