"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    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) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackageInstallationManager = void 0;
const path = require("path");
const constants = require("./constants");
const yok_1 = require("./common/yok");
const semver = require("semver");
class PackageInstallationManager {
    constructor($packageManager, $childProcess, $logger, $settingsService, $fs, $staticConfig, $projectDataService) {
        this.$packageManager = $packageManager;
        this.$childProcess = $childProcess;
        this.$logger = $logger;
        this.$settingsService = $settingsService;
        this.$fs = $fs;
        this.$staticConfig = $staticConfig;
        this.$projectDataService = $projectDataService;
    }
    getLatestVersion(packageName) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.getVersion(packageName, constants.PackageVersion.LATEST);
        });
    }
    getNextVersion(packageName) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.getVersion(packageName, constants.PackageVersion.NEXT);
        });
    }
    getLatestCompatibleVersion(packageName, referenceVersion) {
        return __awaiter(this, void 0, void 0, function* () {
            referenceVersion = referenceVersion || this.$staticConfig.version;
            const isPreReleaseVersion = semver.prerelease(referenceVersion) !== null;
            // if the user has some v.v.v-prerelease-xx.xx pre-release version, include pre-release versions in the search query.
            const compatibleVersionRange = isPreReleaseVersion
                ? `~${referenceVersion}`
                : `~${semver.major(referenceVersion)}.${semver.minor(referenceVersion)}.0`;
            const latestVersion = yield this.getLatestVersion(packageName);
            if (semver.satisfies(latestVersion, compatibleVersionRange)) {
                return latestVersion;
            }
            return ((yield this.getMaxSatisfyingVersion(packageName, compatibleVersionRange)) || latestVersion);
        });
    }
    getMaxSatisfyingVersion(packageName, versionRange) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const data = yield this.$packageManager.view(packageName, {
                versions: true,
            });
            let versions;
            if (typeof data === "string") {
                versions = [data];
            }
            else if (data === null || data === void 0 ? void 0 : data.versions) {
                versions = data.versions;
            }
            else {
                versions = data;
            }
            if (!versions || !Array.isArray(versions)) {
                return null;
            }
            return (_a = semver.maxSatisfying(versions, versionRange)) === null || _a === void 0 ? void 0 : _a.toString();
        });
    }
    getMaxSatisfyingVersionSafe(packageName, versionIdentifier) {
        return __awaiter(this, void 0, void 0, function* () {
            let maxDependencyVersion;
            if (semver.valid(versionIdentifier)) {
                maxDependencyVersion = versionIdentifier;
            }
            else if (semver.validRange(versionIdentifier)) {
                maxDependencyVersion = yield this.getMaxSatisfyingVersion(packageName, versionIdentifier);
            }
            else {
                maxDependencyVersion = yield this.$packageManager.getTagVersion(packageName, versionIdentifier);
            }
            return maxDependencyVersion;
        });
    }
    getInstalledDependencyVersion(packageName, projectDir) {
        return __awaiter(this, void 0, void 0, function* () {
            const projectData = this.$projectDataService.getProjectData(projectDir);
            const devDependencies = projectData.devDependencies || {};
            const dependencies = projectData.dependencies || {};
            const referencedVersion = dependencies[packageName] || devDependencies[packageName];
            const installedVersion = yield this.getMaxSatisfyingVersionSafe(packageName, referencedVersion);
            return installedVersion;
        });
    }
    getLatestCompatibleVersionSafe(packageName, referenceVersion) {
        return __awaiter(this, void 0, void 0, function* () {
            let version = "";
            const canGetVersionFromNpm = yield this.$packageManager.isRegistered(packageName);
            if (canGetVersionFromNpm) {
                version = yield this.getLatestCompatibleVersion(packageName, referenceVersion);
            }
            return version;
        });
    }
    install(packageToInstall, projectDir, opts) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const pathToSave = projectDir;
                const version = (opts && opts.version) || null;
                const dependencyType = (opts && opts.dependencyType) || null;
                return yield this.installCore(packageToInstall, pathToSave, version, dependencyType);
            }
            catch (error) {
                this.$logger.trace(error);
                throw error;
            }
        });
    }
    uninstall(packageToUninstall, projectDir, opts) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                return yield this.$packageManager.uninstall(packageToUninstall, opts, projectDir);
            }
            catch (error) {
                this.$logger.trace(error);
                throw error;
            }
        });
    }
    getInspectorFromCache(inspectorNpmPackageName, projectDir) {
        return __awaiter(this, void 0, void 0, function* () {
            const inspectorPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName);
            // local installation takes precedence over cache
            if (this.inspectorAlreadyInstalled(inspectorPath)) {
                return inspectorPath;
            }
            const cachePath = this.getInspectorCachePath();
            this.prepareCacheDir(cachePath);
            const pathToPackageInCache = path.join(cachePath, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName);
            const iOSFrameworkNSValue = this.$projectDataService.getRuntimePackage(projectDir, "ios" /* constants.PlatformTypes.ios */);
            const version = yield this.getLatestCompatibleVersion(inspectorNpmPackageName, iOSFrameworkNSValue.version);
            let shouldInstall = !this.$fs.exists(pathToPackageInCache);
            if (!shouldInstall) {
                try {
                    const installedVersion = this.$fs.readJson(path.join(pathToPackageInCache, constants.PACKAGE_JSON_FILE_NAME)).version;
                    shouldInstall = version !== installedVersion;
                }
                catch (err) {
                    shouldInstall = true;
                }
            }
            if (shouldInstall) {
                yield this.$childProcess.exec(`npm install ${inspectorNpmPackageName}@${version} --prefix ${cachePath}`, { maxBuffer: 250 * 1024 });
            }
            this.$logger.info("Using inspector from cache.");
            return pathToPackageInCache;
        });
    }
    clearInspectorCache() {
        this.$fs.deleteDirectorySafe(this.getInspectorCachePath());
    }
    getInspectorCachePath() {
        return path.join(this.$settingsService.getProfileDir(), constants.INSPECTOR_CACHE_DIRNAME);
    }
    prepareCacheDir(cacheDirName) {
        this.$fs.ensureDirectoryExists(cacheDirName);
        const cacheDirPackageJsonLocation = path.join(cacheDirName, constants.PACKAGE_JSON_FILE_NAME);
        if (!this.$fs.exists(cacheDirPackageJsonLocation)) {
            this.$fs.writeJson(cacheDirPackageJsonLocation, {
                name: constants.INSPECTOR_CACHE_DIRNAME,
                version: "0.1.0",
            });
        }
    }
    inspectorAlreadyInstalled(pathToInspector) {
        if (this.$fs.exists(pathToInspector)) {
            return true;
        }
        return false;
    }
    installCore(packageName, pathToSave, version, dependencyType) {
        return __awaiter(this, void 0, void 0, function* () {
            const possiblePackageName = path.resolve(packageName);
            if (this.$fs.exists(possiblePackageName)) {
                packageName = possiblePackageName;
            }
            version =
                version || (yield this.getLatestCompatibleVersionSafe(packageName));
            const installResultInfo = yield this.npmInstall(packageName, pathToSave, version, dependencyType);
            const installedPackageName = installResultInfo.name;
            const pathToInstalledPackage = path.join(pathToSave, "node_modules", installedPackageName);
            return pathToInstalledPackage;
        });
    }
    npmInstall(packageName, pathToSave, version, dependencyType) {
        return __awaiter(this, void 0, void 0, function* () {
            this.$logger.info(`Installing ${packageName}`);
            packageName = packageName + (version ? `@${version}` : "");
            const npmOptions = { silent: true, "save-exact": true };
            if (dependencyType) {
                npmOptions[dependencyType] = true;
            }
            return yield this.$packageManager.install(packageName, pathToSave, npmOptions);
        });
    }
    /**
     * This function must not be used with packageName being a URL or local file,
     * because npm view doens't work with those
     */
    getVersion(packageName, version) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            let data = yield this.$packageManager.view(packageName, {
                "dist-tags": true,
            });
            data = (_a = data === null || data === void 0 ? void 0 : data["dist-tags"]) !== null && _a !== void 0 ? _a : data;
            this.$logger.trace("Using version %s. ", data[version]);
            return data[version];
        });
    }
}
exports.PackageInstallationManager = PackageInstallationManager;
yok_1.injector.register("packageInstallationManager", PackageInstallationManager);
//# sourceMappingURL=package-installation-manager.js.map