"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.IndividualVersionPolicy = exports.LockStepVersionPolicy = exports.VersionPolicy = exports.VersionPolicyDefinitionName = exports.BumpType = void 0;
const semver = __importStar(require("semver"));
const node_core_library_1 = require("@rushstack/node-core-library");
const objectUtilities_1 = require("../utilities/objectUtilities");
/**
 * Type of version bumps
 * @public
 *
 * @internalRemarks
 * This is a copy of the semver ReleaseType enum, but with the `none` value added and
 * the `premajor` and `prepatch` omitted.
 * See {@link LockStepVersionPolicy._getReleaseType}.
 *
 * TODO: Consider supporting `premajor` and `prepatch` in the future.
 */
var BumpType;
(function (BumpType) {
    // No version bump
    BumpType[BumpType["none"] = 0] = "none";
    // Prerelease version bump
    BumpType[BumpType["prerelease"] = 1] = "prerelease";
    // Patch version bump
    BumpType[BumpType["patch"] = 2] = "patch";
    // Preminor version bump
    BumpType[BumpType["preminor"] = 3] = "preminor";
    // Minor version bump
    BumpType[BumpType["minor"] = 4] = "minor";
    // Major version bump
    BumpType[BumpType["major"] = 5] = "major";
})(BumpType || (exports.BumpType = BumpType = {}));
/**
 * Version policy base type names
 * @public
 */
var VersionPolicyDefinitionName;
(function (VersionPolicyDefinitionName) {
    VersionPolicyDefinitionName[VersionPolicyDefinitionName["lockStepVersion"] = 0] = "lockStepVersion";
    VersionPolicyDefinitionName[VersionPolicyDefinitionName["individualVersion"] = 1] = "individualVersion";
})(VersionPolicyDefinitionName || (exports.VersionPolicyDefinitionName = VersionPolicyDefinitionName = {}));
/**
 * This is the base class for version policy which controls how versions get bumped.
 * @public
 */
class VersionPolicy {
    get _versionFormatForCommit() {
        var _a, _b;
        return (_b = (_a = this._json.dependencies) === null || _a === void 0 ? void 0 : _a.versionFormatForCommit) !== null && _b !== void 0 ? _b : 'original';
    }
    get _versionFormatForPublish() {
        var _a, _b;
        return (_b = (_a = this._json.dependencies) === null || _a === void 0 ? void 0 : _a.versionFormatForPublish) !== null && _b !== void 0 ? _b : 'original';
    }
    /**
     * Version policy name
     */
    get policyName() {
        return this._json.policyName;
    }
    /**
     * Version policy definition name
     */
    get definitionName() {
        return node_core_library_1.Enum.getValueByKey(VersionPolicyDefinitionName, this._json.definitionName);
    }
    /**
     * Determines if a version policy wants to opt out of changelog files.
     */
    get exemptFromRushChange() {
        var _a;
        return (_a = this._json.exemptFromRushChange) !== null && _a !== void 0 ? _a : false;
    }
    /**
     * Determines if a version policy wants to opt in to including email.
     */
    get includeEmailInChangeFile() {
        var _a;
        return (_a = this._json.includeEmailInChangeFile) !== null && _a !== void 0 ? _a : false;
    }
    /**
     * @internal
     */
    constructor(versionPolicyJson) {
        this._json = versionPolicyJson;
    }
    /**
     * Loads from version policy json
     *
     * @param versionPolicyJson - version policy Json
     *
     * @internal
     */
    static load(versionPolicyJson) {
        const definition = node_core_library_1.Enum.getValueByKey(VersionPolicyDefinitionName, versionPolicyJson.definitionName);
        if (definition === VersionPolicyDefinitionName.lockStepVersion) {
            // eslint-disable-next-line @typescript-eslint/no-use-before-define
            return new LockStepVersionPolicy(versionPolicyJson);
        }
        else if (definition === VersionPolicyDefinitionName.individualVersion) {
            // eslint-disable-next-line @typescript-eslint/no-use-before-define
            return new IndividualVersionPolicy(versionPolicyJson);
        }
        return undefined;
    }
    /**
     * Whether it is a lockstepped version policy
     */
    get isLockstepped() {
        return this.definitionName === VersionPolicyDefinitionName.lockStepVersion;
    }
    /**
     * Tells the version policy to modify any dependencies in the target package
     * to values used for publishing.
     */
    setDependenciesBeforePublish(packageName, configuration) {
        if (this._versionFormatForPublish === 'exact') {
            const project = configuration.getProjectByName(packageName);
            const packageJsonEditor = project.packageJsonEditor;
            for (const dependency of packageJsonEditor.dependencyList) {
                const rushDependencyProject = configuration.getProjectByName(dependency.name);
                if (rushDependencyProject) {
                    const dependencyVersion = rushDependencyProject.packageJson.version;
                    dependency.setVersion(dependencyVersion);
                }
            }
            packageJsonEditor.saveIfModified();
        }
    }
    /**
     * Tells the version policy to modify any dependencies in the target package
     * to values used for checked-in source.
     */
    setDependenciesBeforeCommit(packageName, configuration) {
        if (this._versionFormatForCommit === 'wildcard') {
            const project = configuration.getProjectByName(packageName);
            const packageJsonEditor = project.packageJsonEditor;
            for (const dependency of packageJsonEditor.dependencyList) {
                const rushDependencyProject = configuration.getProjectByName(dependency.name);
                if (rushDependencyProject) {
                    dependency.setVersion('*');
                }
            }
            packageJsonEditor.saveIfModified();
        }
    }
}
exports.VersionPolicy = VersionPolicy;
/**
 * This policy indicates all related projects should use the same version.
 * @public
 */
class LockStepVersionPolicy extends VersionPolicy {
    /**
     * The type of bump for next bump.
     */
    // nextBump is probably not needed. It can be prerelease only.
    // Other types of bumps can be passed in as a parameter to bump method, so can identifier.
    get nextBump() {
        return this._json.nextBump !== undefined ? node_core_library_1.Enum.getValueByKey(BumpType, this._json.nextBump) : undefined;
    }
    /**
     * The main project for the version policy.
     *
     * If the value is provided, change logs will only be generated in that project.
     * If the value is not provided, change logs will be hosted in each project associated with the policy.
     */
    get mainProject() {
        return this._json.mainProject;
    }
    /**
     * @internal
     */
    constructor(versionPolicyJson) {
        super(versionPolicyJson);
        this._version = new semver.SemVer(versionPolicyJson.version);
    }
    /**
     * The value of the lockstep version
     */
    get version() {
        return this._version.format();
    }
    /**
     * Returns an updated package json that satisfies the version policy.
     *
     * @param project - input package json
     * @param force - force update even when the project version is higher than the policy version.
     */
    ensure(project, force) {
        const packageVersion = new semver.SemVer(project.version);
        const compareResult = packageVersion.compare(this._version);
        if (compareResult === 0) {
            return undefined;
        }
        else if (compareResult > 0 && !force) {
            const errorMessage = `Version ${project.version} in package ${project.name}` +
                ` is higher than locked version ${this._version.format()}.`;
            throw new Error(errorMessage);
        }
        return this._updatePackageVersion(project, this._version);
    }
    /**
     * Bumps the version of the lockstep policy
     *
     * @param bumpType - Overwrite bump type in version-policy.json with the provided value.
     * @param identifier - Prerelease identifier if bump type is prerelease.
     */
    bump(bumpType, identifier) {
        const nextBump = bumpType !== null && bumpType !== void 0 ? bumpType : this.nextBump;
        if (nextBump === undefined) {
            // let change files drive version bump.
            return;
        }
        this._version.inc(this._getReleaseType(nextBump), identifier);
        this._json.version = this.version;
    }
    /**
     * Updates the version of the policy directly with a new value
     * @param newVersionString - New version
     */
    update(newVersionString) {
        const newVersion = new semver.SemVer(newVersionString);
        if (!newVersion || this._version === newVersion) {
            return false;
        }
        this._version = newVersion;
        this._json.version = this.version;
        return true;
    }
    /**
     * Validates the specified version and throws if the version does not satisfy lockstep version.
     *
     * @param versionString - version string
     * @param packageName - package name
     */
    validate(versionString, packageName) {
        const versionToTest = new semver.SemVer(versionString, false);
        if (this._version.compare(versionToTest) !== 0) {
            throw new Error(`Invalid version ${versionString} in ${packageName}`);
        }
    }
    _updatePackageVersion(project, newVersion) {
        const updatedProject = (0, objectUtilities_1.cloneDeep)(project);
        updatedProject.version = newVersion.format();
        return updatedProject;
    }
    _getReleaseType(bumpType) {
        // Eventually we should just use ReleaseType and get rid of bump type.
        return BumpType[bumpType];
    }
}
exports.LockStepVersionPolicy = LockStepVersionPolicy;
/**
 * This policy indicates all related projects get version bump driven by their own changes.
 * @public
 */
class IndividualVersionPolicy extends VersionPolicy {
    /**
     * The major version that has been locked
     */
    get lockedMajor() {
        return this._json.lockedMajor;
    }
    /**
     * @internal
     */
    constructor(versionPolicyJson) {
        super(versionPolicyJson);
    }
    /**
     * Returns an updated package json that satisfies the version policy.
     *
     * @param project - input package json
     * @param force - force update even when the project version is higher than the policy version.
     */
    ensure(project, force) {
        if (this.lockedMajor) {
            const version = new semver.SemVer(project.version);
            if (version.major < this.lockedMajor) {
                const updatedProject = (0, objectUtilities_1.cloneDeep)(project);
                updatedProject.version = `${this.lockedMajor}.0.0`;
                return updatedProject;
            }
            else if (version.major > this.lockedMajor) {
                const errorMessage = `Version ${project.version} in package ${project.name}` +
                    ` is higher than locked major version ${this.lockedMajor}.`;
                throw new Error(errorMessage);
            }
        }
        return undefined;
    }
    /**
     * Bumps version.
     * Individual version policy lets change files drive version bump. This method currently does not do anything.
     *
     * @param bumpType - bump type
     * @param identifier - prerelease id
     */
    bump(bumpType, identifier) {
        // individual version policy lets change files drive version bump.
    }
    /**
     * Validates the specified version and throws if the version does not satisfy the policy.
     *
     * @param versionString - version string
     * @param packageName - package name
     */
    validate(versionString, packageName) {
        const versionToTest = new semver.SemVer(versionString, false);
        if (this.lockedMajor !== undefined) {
            if (this.lockedMajor !== versionToTest.major) {
                throw new Error(`Invalid major version ${versionString} in ${packageName}`);
            }
        }
    }
}
exports.IndividualVersionPolicy = IndividualVersionPolicy;
//# sourceMappingURL=VersionPolicy.js.map