"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 url = require("url");
const registryUrl = require("registry-url");
const loadJsonFile = require("load-json-file");
const writeJsonFile = require("write-json-file");
const path = require("path");
const getRegistryFolderName_1 = require("./getRegistryFolderName");
const pnpm_logger_1 = require("pnpm-logger");
const pLimit = require("p-limit");
// prevents simultainous operations on the meta.json
// otherwise it would cause EPERM exceptions
const metafileOperationLimits = {};
function loadPkgMetaNonCached(spec, localRegistry, got, metaCache) {
    return __awaiter(this, void 0, void 0, function* () {
        if (metaCache.has(spec.name)) {
            return metaCache.get(spec.name);
        }
        const registry = getRegistryFolderName_1.default(registryUrl(spec.scope));
        const pkgMirror = path.join(localRegistry, registry, spec.name);
        const limit = metafileOperationLimits[pkgMirror] = metafileOperationLimits[pkgMirror] || pLimit(1);
        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.spec]) {
                return meta;
            }
        }
        try {
            const meta = yield fromRegistry(got, spec);
            // only save meta to cache, when it is fresh
            metaCache.set(spec.name, meta);
            limit(() => saveMeta(pkgMirror, meta));
            return meta;
        }
        catch (err) {
            const meta = yield loadMeta(localRegistry);
            if (!meta)
                throw err;
            pnpm_logger_1.default.error(err);
            pnpm_logger_1.default.info(`Using cached meta from ${localRegistry}`);
            return meta;
        }
    });
}
exports.default = loadPkgMetaNonCached;
function fromRegistry(got, spec) {
    return __awaiter(this, void 0, void 0, function* () {
        const uri = toUri(spec);
        const meta = yield got.getJSON(uri);
        return meta;
    });
}
// Don't let the name confuse you, 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 = 'package.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/2'
 */
function toUri(spec) {
    let name;
    if (spec.name.substr(0, 1) === '@') {
        name = '@' + encodeURIComponent(spec.name.substr(1));
    }
    else {
        name = encodeURIComponent(spec.name);
    }
    return url.resolve(registryUrl(spec.scope), name);
}
//# sourceMappingURL=loadPackageMeta.js.map