"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NpmNodeModulesCollector = void 0;
const nodeModulesCollector_js_1 = require("./nodeModulesCollector.js");
const packageManager_js_1 = require("./packageManager.js");
class NpmNodeModulesCollector extends nodeModulesCollector_js_1.NodeModulesCollector {
    constructor() {
        super(...arguments);
        this.installOptions = {
            manager: packageManager_js_1.PM.NPM,
            lockfile: "package-lock.json",
        };
    }
    getArgs() {
        return ["list", "-a", "--include", "prod", "--include", "optional", "--omit", "dev", "--json", "--long", "--silent"];
    }
    async collectAllDependencies(tree) {
        for (const [key, value] of Object.entries(tree.dependencies || {})) {
            if (this.isDuplicatedNpmDependency(value)) {
                continue;
            }
            // Use the key (alias name) instead of value.name for npm aliased packages
            // e.g., { "foo": { name: "@scope/bar", ... } } should be stored as "foo@version"
            // This ensures aliased packages are copied to the correct location in node_modules
            const normalizedDep = key !== value.name ? { ...value, name: key } : value;
            this.allDependencies.set(this.packageVersionString(normalizedDep), normalizedDep);
            await this.collectAllDependencies(value);
        }
    }
    async extractProductionDependencyGraph(tree, dependencyId) {
        var _a;
        if (this.productionGraph[dependencyId]) {
            return;
        }
        const isDuplicateDep = this.isDuplicatedNpmDependency(tree);
        const resolvedDeps = isDuplicateDep ? (_a = this.allDependencies.get(dependencyId)) === null || _a === void 0 ? void 0 : _a.dependencies : tree.dependencies;
        // Initialize with empty dependencies array first to mark this dependency as "in progress"
        // After initialization, if there are libraries with the same name+version later, they will not be searched recursively again
        // This will prevents infinite loops when circular dependencies are encountered.
        this.productionGraph[dependencyId] = { dependencies: [] };
        const collectedDependencies = [];
        if (resolvedDeps && Object.keys(resolvedDeps).length > 0) {
            for (const packageName in resolvedDeps) {
                if (!this.isProdDependency(packageName, tree)) {
                    continue;
                }
                const dependency = resolvedDeps[packageName];
                // Use the key (alias name) for aliased packages to match how they're stored in allDependencies
                const normalizedName = packageName !== dependency.name ? packageName : dependency.name;
                const childDependencyId = `${normalizedName}@${dependency.version}`;
                await this.extractProductionDependencyGraph(dependency, childDependencyId);
                collectedDependencies.push(childDependencyId);
            }
        }
        this.productionGraph[dependencyId] = { dependencies: collectedDependencies };
    }
    // Check: is package already included as a prod dependency due to another package?
    // We need to check this to prevent infinite loops in case of duplicated dependencies
    isDuplicatedNpmDependency(tree) {
        const { _dependencies = {}, dependencies = {} } = tree;
        const isDuplicateDep = Object.keys(_dependencies).length > 0 && Object.keys(dependencies).length === 0;
        return isDuplicateDep;
    }
    // `npm list` provides explicit list of deps in _dependencies
    isProdDependency(packageName, tree) {
        var _a;
        return ((_a = tree._dependencies) === null || _a === void 0 ? void 0 : _a[packageName]) != null;
    }
    async parseDependenciesTree(jsonBlob) {
        return Promise.resolve(JSON.parse(jsonBlob));
    }
}
exports.NpmNodeModulesCollector = NpmNodeModulesCollector;
//# sourceMappingURL=npmNodeModulesCollector.js.map