"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeCurrentLockfile = exports.writeWantedLockfile = void 0;
const error_1 = __importDefault(require("@pnpm/error"));
const logger_1 = __importDefault(require("./logger"));
const types_1 = require("@pnpm/types");
const constants_1 = require("@pnpm/constants");
const rimraf = require("@zkochan/rimraf");
const yaml = require("js-yaml");
const fs = require("mz/fs");
const path = require("path");
const R = require("ramda");
const writeFileAtomicCB = require("write-file-atomic");
function writeFileAtomic(filename, data) {
    return new Promise((resolve, reject) => writeFileAtomicCB(filename, data, {}, (err) => err ? reject(err) : resolve()));
}
const LOCKFILE_YAML_FORMAT = {
    lineWidth: 1000,
    noCompatMode: true,
    noRefs: true,
    sortKeys: true,
};
function writeWantedLockfile(pkgPath, wantedLockfile, opts) {
    return writeLockfile(constants_1.WANTED_LOCKFILE, pkgPath, wantedLockfile, opts);
}
exports.writeWantedLockfile = writeWantedLockfile;
async function writeCurrentLockfile(virtualStoreDir, currentLockfile, opts) {
    await fs.mkdir(virtualStoreDir, { recursive: true });
    return writeLockfile('lock.yaml', virtualStoreDir, currentLockfile, opts);
}
exports.writeCurrentLockfile = writeCurrentLockfile;
function writeLockfile(lockfileFilename, pkgPath, wantedLockfile, opts) {
    const lockfilePath = path.join(pkgPath, lockfileFilename);
    // empty lockfile is not saved
    if (isEmptyLockfile(wantedLockfile)) {
        return rimraf(lockfilePath);
    }
    const yamlDoc = yamlStringify(wantedLockfile, (opts === null || opts === void 0 ? void 0 : opts.forceSharedFormat) === true);
    return writeFileAtomic(lockfilePath, yamlDoc);
}
function yamlStringify(lockfile, forceSharedFormat) {
    try {
        return yaml.safeDump(normalizeLockfile(lockfile, forceSharedFormat), LOCKFILE_YAML_FORMAT);
    }
    catch (err) {
        if (err.message.includes('[object Undefined]')) {
            const brokenValuePath = findBrokenRecord(lockfile);
            if (brokenValuePath) {
                throw new error_1.default('LOCKFILE_STRINGIFY', `Failed to stringify the lockfile object. Undefined value at: ${brokenValuePath}`);
            }
        }
        throw err;
    }
}
function findBrokenRecord(obj) {
    for (let [key, value] of Object.entries(obj)) {
        if (key === '.')
            key = '[.]';
        switch (typeof value) {
            case 'undefined': {
                return key;
            }
            case 'object': {
                const brokenKey = findBrokenRecord(value);
                if (!brokenKey)
                    break;
                if (brokenKey.startsWith('[')) {
                    return `${key}${brokenKey}`;
                }
                return `${key}.${brokenKey}`;
            }
        }
    }
    return null;
}
function isEmptyLockfile(lockfile) {
    return R.values(lockfile.importers).every((importer) => { var _a, _b; return R.isEmpty((_a = importer.specifiers) !== null && _a !== void 0 ? _a : {}) && R.isEmpty((_b = importer.dependencies) !== null && _b !== void 0 ? _b : {}); });
}
function normalizeLockfile(lockfile, forceSharedFormat) {
    if (!forceSharedFormat && R.equals(R.keys(lockfile.importers), ['.'])) {
        const lockfileToSave = {
            ...lockfile,
            ...lockfile.importers['.'],
        };
        delete lockfileToSave.importers;
        for (const depType of types_1.DEPENDENCIES_FIELDS) {
            if (R.isEmpty(lockfileToSave[depType])) {
                delete lockfileToSave[depType];
            }
        }
        if (R.isEmpty(lockfileToSave.packages) || !lockfileToSave.packages) {
            delete lockfileToSave.packages;
        }
        return lockfileToSave;
    }
    else {
        const lockfileToSave = {
            ...lockfile,
            importers: R.keys(lockfile.importers).reduce((acc, alias) => {
                var _a;
                const importer = lockfile.importers[alias];
                const normalizedImporter = {
                    specifiers: importer.specifiers,
                };
                for (const depType of types_1.DEPENDENCIES_FIELDS) {
                    if (!R.isEmpty((_a = importer[depType]) !== null && _a !== void 0 ? _a : {})) {
                        normalizedImporter[depType] = importer[depType];
                    }
                }
                acc[alias] = normalizedImporter;
                return acc;
            }, {}),
        };
        if (R.isEmpty(lockfileToSave.packages) || !lockfileToSave.packages) {
            delete lockfileToSave.packages;
        }
        return lockfileToSave;
    }
}
function writeLockfiles(opts) {
    const wantedLockfilePath = path.join(opts.wantedLockfileDir, constants_1.WANTED_LOCKFILE);
    const currentLockfilePath = path.join(opts.currentLockfileDir, 'lock.yaml');
    // empty lockfile is not saved
    if (isEmptyLockfile(opts.wantedLockfile)) {
        return Promise.all([
            rimraf(wantedLockfilePath),
            rimraf(currentLockfilePath),
        ]);
    }
    const forceSharedFormat = (opts === null || opts === void 0 ? void 0 : opts.forceSharedFormat) === true;
    const yamlDoc = yamlStringify(opts.wantedLockfile, forceSharedFormat);
    // in most cases the `pnpm-lock.yaml` and `node_modules/.pnpm-lock.yaml` are equal
    // in those cases the YAML document can be stringified only once for both files
    // which is more efficient
    if (opts.wantedLockfile === opts.currentLockfile) {
        return Promise.all([
            writeFileAtomic(wantedLockfilePath, yamlDoc),
            (async () => {
                await fs.mkdir(path.dirname(currentLockfilePath), { recursive: true });
                await writeFileAtomic(currentLockfilePath, yamlDoc);
            })(),
        ]);
    }
    logger_1.default.debug({
        message: `\`${constants_1.WANTED_LOCKFILE}\` differs from \`${path.relative(opts.wantedLockfileDir, currentLockfilePath)}\``,
        prefix: opts.wantedLockfileDir,
    });
    const currentYamlDoc = yamlStringify(opts.currentLockfile, forceSharedFormat);
    return Promise.all([
        writeFileAtomic(wantedLockfilePath, yamlDoc),
        (async () => {
            await fs.mkdir(path.dirname(currentLockfilePath), { recursive: true });
            await writeFileAtomic(currentLockfilePath, currentYamlDoc);
        })(),
    ]);
}
exports.default = writeLockfiles;
//# sourceMappingURL=write.js.map