"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.NpmPackage = exports.PackageDependencyKind = void 0;
const path = __importStar(require("node:path"));
const node_core_library_1 = require("@rushstack/node-core-library");
const BasePackage_1 = require("../base/BasePackage");
/**
 * The type of dependency; used by IPackageDependency.
 */
var PackageDependencyKind;
(function (PackageDependencyKind) {
    PackageDependencyKind[PackageDependencyKind["Normal"] = 0] = "Normal";
    /**
     * The dependency was listed in the optionalDependencies section of package.json.
     */
    PackageDependencyKind[PackageDependencyKind["Optional"] = 1] = "Optional";
    /**
     * The dependency should be a symlink to a project that is locally built by Rush..
     */
    PackageDependencyKind[PackageDependencyKind["LocalLink"] = 2] = "LocalLink";
})(PackageDependencyKind || (exports.PackageDependencyKind = PackageDependencyKind = {}));
class NpmPackage extends BasePackage_1.BasePackage {
    constructor(name, version, dependencies, folderPath) {
        super(name, version, folderPath, undefined);
        this.dependencies = dependencies.slice(0); // clone the array
        this.parent = undefined;
    }
    /**
     * Used by "npm link" when creating a Package object that represents symbolic links to be created.
     */
    static createLinkedNpmPackage(name, version, dependencies, folderPath) {
        return new NpmPackage(name, version, dependencies, folderPath);
    }
    /**
     * Used by "npm link" to simulate a temp project that is missing from the common/node_modules
     * folder (e.g. because it was added after the shrinkwrap file was regenerated).
     * @param packageJsonFilename - Filename of the source package.json
     *        Example: `C:\MyRepo\common\temp\projects\project1\package.json`
     * @param targetFolderName - Filename where it should have been installed
     *        Example: `C:\MyRepo\common\temp\node_modules\@rush-temp\project1`
     */
    static createVirtualTempPackage(packageJsonFilename, installFolderName) {
        const packageJson = node_core_library_1.JsonFile.load(packageJsonFilename);
        const npmPackage = {
            children: [],
            error: null,
            id: 0,
            isLink: false,
            package: packageJson,
            parent: null,
            path: installFolderName,
            realpath: installFolderName
        };
        return NpmPackage.createFromNpm(npmPackage);
    }
    /**
     * Recursive constructs a tree of NpmPackage objects using information returned
     * by the "read-package-tree" library.
     */
    static createFromNpm(npmPackage) {
        if (npmPackage.error) {
            throw new Error(`Failed to parse package.json for ${path.basename(npmPackage.path)}: ${npmPackage.error.message}`);
        }
        let dependencies = [];
        const dependencyNames = new Set();
        const packageJson = npmPackage.package;
        if (packageJson.optionalDependencies) {
            for (const dependencyName of Object.keys(packageJson.optionalDependencies)) {
                if (!dependencyNames.has(dependencyName)) {
                    dependencyNames.add(dependencyName);
                    dependencies.push({
                        name: dependencyName,
                        versionRange: packageJson.optionalDependencies[dependencyName],
                        kind: PackageDependencyKind.Optional
                    });
                }
            }
        }
        if (packageJson.dependencies) {
            for (const dependencyName of Object.keys(packageJson.dependencies)) {
                if (!dependencyNames.has(dependencyName)) {
                    dependencyNames.add(dependencyName);
                    dependencies.push({
                        name: dependencyName,
                        versionRange: packageJson.dependencies[dependencyName],
                        kind: PackageDependencyKind.Normal
                    });
                }
            }
        }
        if (packageJson.rushDependencies) {
            for (const dependencyName of Object.keys(packageJson.rushDependencies)) {
                if (!dependencyNames.has(dependencyName)) {
                    dependencyNames.add(dependencyName);
                    dependencies.push({
                        name: dependencyName,
                        versionRange: packageJson.dependencies[dependencyName],
                        kind: PackageDependencyKind.LocalLink
                    });
                }
            }
        }
        dependencies = dependencies.sort((a, b) => a.name.localeCompare(b.name));
        const newPackage = new NpmPackage(npmPackage.package.name, npmPackage.package.version, dependencies, 
        // NOTE: We don't use packageNode.realpath here, because if "npm unlink" was
        // performed without redoing "rush link", then a broken symlink is better than
        // a symlink to the wrong thing.
        npmPackage.path);
        for (const child of npmPackage.children) {
            newPackage.addChild(NpmPackage.createFromNpm(child));
        }
        return newPackage;
    }
    /**
     * Searches the node_modules hierarchy for the nearest matching package with the
     * given name.  Note that the nearest match may have an incompatible version.
     * If a match is found, then the "found" result will not be undefined.
     * In either case, the parentForCreate result indicates where the missing
     * dependency can be added, i.e. if the requested dependency was not found
     * or was found with an incompatible version.
     *
     * "cyclicSubtreeRoot" is a special optional parameter that specifies a different
     * root for the tree; the decoupledLocalDependencies feature uses this to isolate
     * certain devDependencies in their own subtree.
     */
    resolveOrCreate(dependencyName, cyclicSubtreeRoot) {
        let currentParent = this;
        let parentForCreate = undefined;
        for (;;) {
            // Does any child match?
            for (const child of currentParent.children) {
                // The package.json name can differ from the installation folder name, in the case of an NPM package alias
                // such as this:
                //
                // "dependencies": {
                //   "@alias-scope/alias-name": "npm:target-name@^1.2.3"
                // }
                //
                // Thus we need to compare child.installedName instead of child.name:
                if (child.installedName === dependencyName) {
                    // One of the children matched.  Note that parentForCreate may be
                    // undefined, e.g. if an immediate child is found but has the wrong version,
                    // then we have no place in the tree to create another version.
                    return { found: child, parentForCreate };
                }
            }
            // If no child matched, then make this node the "parentForCreate" where we
            // could add a missing dependency.
            parentForCreate = currentParent;
            if (!currentParent.parent || (cyclicSubtreeRoot && currentParent === cyclicSubtreeRoot)) {
                // We reached the root without finding a match
                // parentForCreate will be the root.
                return { found: undefined, parentForCreate };
            }
            // Continue walking upwards.
            currentParent = currentParent.parent;
        }
    }
    /**
     * Searches the node_modules hierarchy for the nearest matching package with the
     * given name.  If no match is found, then undefined is returned.
     */
    resolve(dependencyName) {
        return this.resolveOrCreate(dependencyName).found;
    }
}
exports.NpmPackage = NpmPackage;
//# sourceMappingURL=NpmPackage.js.map