"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 logger_1 = require("@pnpm/logger");
const getRegistryName = require("encode-registry");
const loadJsonFile = require("load-json-file");
const pLimit = require("p-limit");
const path = require("path");
const url = require("url");
const writeJsonFile = require("write-json-file");
const errorTypes_1 = require("../../errorTypes");
// prevents simultainous operations on the meta.json
// otherwise it would cause EPERM exceptions
const metafileOperationLimits = {};
function loadPkgMetaNonCached(spec, opts) {
    return __awaiter(this, void 0, void 0, function* () {
        opts = opts || {};
        if (opts.metaCache.has(spec.name)) {
            return opts.metaCache.get(spec.name);
        }
        const registryName = getRegistryName(opts.registry);
        const pkgMirror = path.join(opts.storePath, registryName, spec.name);
        const limit = metafileOperationLimits[pkgMirror] = metafileOperationLimits[pkgMirror] || pLimit(1);
        if (opts.offline) {
            const meta = yield limit(() => loadMeta(pkgMirror));
            if (meta)
                return meta;
            throw new errorTypes_1.PnpmError('NO_OFFLINE_META', `Failed to resolve ${spec.rawSpec} in package mirror ${pkgMirror}`);
        }
        if (spec.type === 'version') {
            const meta = yield limit(() => loadMeta(pkgMirror));
            // use the cached meta only if it has the required package version
            // otherwise it is probably out of date
            if (meta && meta.versions && meta.versions[spec.fetchSpec]) {
                return meta;
            }
        }
        try {
            const meta = yield fromRegistry(opts.got, spec, opts.registry, opts.downloadPriority);
            // only save meta to cache, when it is fresh
            opts.metaCache.set(spec.name, meta);
            limit(() => saveMeta(pkgMirror, meta));
            return meta;
        }
        catch (err) {
            const meta = yield loadMeta(opts.storePath);
            if (!meta)
                throw err;
            logger_1.default.error(err);
            logger_1.default.info(`Using cached meta from ${opts.storePath}`);
            return meta;
        }
    });
}
exports.default = loadPkgMetaNonCached;
function fromRegistry(got, spec, registry, downloadPriority) {
    return __awaiter(this, void 0, void 0, function* () {
        const uri = toUri(spec, registry);
        const meta = yield got.getJSON(uri, registry, downloadPriority);
        return meta;
    });
}
// This file contains meta information
// about all the packages published by the same name, not just the manifest
// of one package/version
const META_FILENAME = 'index.json';
function loadMeta(pkgMirror) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            return yield loadJsonFile(path.join(pkgMirror, META_FILENAME));
        }
        catch (err) {
            return null;
        }
    });
}
function saveMeta(pkgMirror, meta) {
    return writeJsonFile(path.join(pkgMirror, META_FILENAME), meta);
}
/**
 * Converts package data (from `npa()`) to a URI
 *
 * @example
 *     toUri({ name: 'rimraf', rawSpec: '2' })
 *     // => 'https://registry.npmjs.org/rimraf'
 *
 * Although it is possible to download the needed package.json with one request
 * by passing the spec like this: 'https://registry.npmjs.org/rimraf/2'
 * This increases the number of HTTP requests during installation and slows down
 * pnpm up to twice!
 */
function toUri(spec, registry) {
    let name;
    if (spec.name.substr(0, 1) === '@') {
        name = '@' + encodeURIComponent(spec.name.substr(1));
    }
    else {
        name = encodeURIComponent(spec.name);
    }
    return url.resolve(registry, name);
}
//# sourceMappingURL=loadPackageMeta.js.map