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