"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const pnpm_logger_1 = require("pnpm-logger");
const fs = require("mz/fs");
const path = require("path");
const rimraf = require("rimraf-then");
const resolve_1 = require("../resolve");
const readPkg_1 = require("../fs/readPkg");
const exists = require("path-exists");
const fetchResolution_1 = require("./fetchResolution");
const logInstallStatus_1 = require("../logging/logInstallStatus");
const dirsum_1 = require("../fs/dirsum");
const pkgIsUntouched_1 = require("../pkgIsUntouched");
function fetch(spec, options) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            let fetchingPkg = null;
            let resolution = options.shrinkwrapResolution;
            let pkgId = options.pkgId;
            if (!resolution || options.update) {
                const resolveResult = yield resolve_1.default(spec, {
                    loggedPkg: options.loggedPkg,
                    root: options.root,
                    got: options.got,
                    localRegistry: options.localRegistry,
                    registry: options.registry,
                    metaCache: options.metaCache,
                    offline: options.offline,
                });
                // keep the shrinkwrap resolution when possible
                // to keep the original shasum
                if (pkgId !== resolveResult.id || !resolution) {
                    resolution = resolveResult.resolution;
                }
                pkgId = resolveResult.id;
                if (resolveResult.package) {
                    fetchingPkg = Promise.resolve(resolveResult.package);
                }
            }
            const id = pkgId;
            logInstallStatus_1.default({ status: 'resolved', pkgId: id, pkg: options.loggedPkg });
            const target = path.join(options.storePath, id);
            const fetchingFiles = options.fetchingLocker(id, () => fetchToStore({
                target,
                resolution: resolution,
                pkgId: id,
                got: options.got,
                localRegistry: options.localRegistry,
                offline: options.offline,
            }));
            if (fetchingPkg == null) {
                fetchingPkg = fetchingFiles.then(() => readPkg_1.default(target));
            }
            return {
                fetchingPkg,
                fetchingFiles,
                id,
                resolution,
                path: target,
                srcPath: resolution.type == 'directory'
                    ? resolution.root
                    : undefined,
                abort: () => __awaiter(this, void 0, void 0, function* () {
                    try {
                        yield fetchingFiles;
                    }
                    finally {
                        return rimraf(target);
                    }
                }),
            };
        }
        catch (err) {
            logInstallStatus_1.default({ status: 'error', pkg: options.loggedPkg });
            throw err;
        }
    });
}
exports.default = fetch;
function fetchToStore(opts) {
    return __awaiter(this, void 0, void 0, function* () {
        const target = opts.target;
        const targetExists = yield exists(target);
        if (targetExists) {
            // if target exists and it wasn't modified, then no need to refetch it
            if (yield pkgIsUntouched_1.default(target))
                return false;
            pnpm_logger_1.default.warn(`Refetching ${target} to store, as it was modified`);
        }
        // We fetch into targetStage directory first and then fs.rename() it to the
        // target directory.
        const targetStage = `${target}_stage`;
        yield rimraf(targetStage);
        if (targetExists) {
            yield rimraf(target);
        }
        yield fetchResolution_1.default(opts.resolution, targetStage, {
            got: opts.got,
            pkgId: opts.pkgId,
            localRegistry: opts.localRegistry,
            offline: opts.offline,
        });
        logInstallStatus_1.default({
            status: 'fetched',
            pkgId: opts.pkgId,
        });
        // fs.rename(oldPath, newPath) is an atomic operation, so we do it at the
        // end
        yield fs.rename(targetStage, target);
        createShasum(target);
        return true;
    });
}
function createShasum(dirPath) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const shasum = yield dirsum_1.default(dirPath);
            yield fs.writeFile(`${dirPath}_shasum`, shasum, 'utf8');
        }
        catch (err) {
            pnpm_logger_1.default.error({
                message: `Failed to calculate shasum for ${dirPath}`,
                err,
            });
        }
    });
}
//# sourceMappingURL=fetch.js.map