"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.scanDependenciesRecursively = scanDependenciesRecursively;
const node_module_1 = __importDefault(require("node:module"));
const utils_1 = require("./utils");
const concurrency_1 = require("../concurrency");
const utils_2 = require("../utils");
// NOTE(@kitten): There's no need to search very deep for modules
// We don't expect native modules to be excessively nested in the dependency tree
const MAX_DEPTH = 8;
const createNodeModulePathsCreator = () => {
    const _nodeModulePathCache = new Map();
    return async function getNodeModulePaths(packagePath) {
        const outputPaths = [];
        const nodeModulePaths = node_module_1.default._nodeModulePaths(packagePath);
        for (let idx = 0; idx < nodeModulePaths.length; idx++) {
            const nodeModulePath = nodeModulePaths[idx];
            let target = _nodeModulePathCache.get(nodeModulePath);
            if (target === undefined) {
                target = await (0, utils_2.maybeRealpath)(nodeModulePath);
                if (idx !== 0) {
                    _nodeModulePathCache.set(nodeModulePath, target);
                }
            }
            if (target != null) {
                outputPaths.push(target);
            }
        }
        return outputPaths;
    };
};
async function resolveDependencies(packageJson, nodeModulePaths, depth, shouldIncludeDependency) {
    const dependencies = Object.create(null);
    if (packageJson.dependencies != null && typeof packageJson.dependencies === 'object') {
        Object.assign(dependencies, packageJson.dependencies);
    }
    // NOTE(@kitten): Also traverse devDependencies for top-level package.json
    if (depth === 0 &&
        packageJson.devDependencies != null &&
        typeof packageJson.devDependencies === 'object') {
        Object.assign(dependencies, packageJson.devDependencies);
    }
    if (packageJson.peerDependencies != null && typeof packageJson.peerDependencies === 'object') {
        const peerDependenciesMeta = packageJson.peerDependenciesMeta != null &&
            typeof packageJson.peerDependenciesMeta === 'object'
            ? packageJson.peerDependenciesMeta
            : undefined;
        for (const dependencyName in packageJson.peerDependencies) {
            // NOTE(@kitten): We only check peer dependencies because some package managers auto-install them
            // which would mean they'd have no reference in any dependencies. However, optional peer dependencies
            // don't auto-install and we can skip them
            if (!isOptionalPeerDependencyMeta(peerDependenciesMeta, dependencyName)) {
                dependencies[dependencyName] = '';
            }
        }
    }
    const resolveDependency = async (dependencyName) => {
        for (let idx = 0; idx < nodeModulePaths.length; idx++) {
            const originPath = (0, utils_2.fastJoin)(nodeModulePaths[idx], dependencyName);
            const nodeModulePath = await (0, utils_2.maybeRealpath)(originPath);
            if (nodeModulePath != null) {
                return {
                    source: 0 /* DependencyResolutionSource.RECURSIVE_RESOLUTION */,
                    name: dependencyName,
                    version: '',
                    path: nodeModulePath,
                    originPath,
                    duplicates: null,
                    depth,
                };
            }
        }
        return null;
    };
    const modules = await (0, concurrency_1.taskAll)(Object.keys(dependencies).filter((dependencyName) => shouldIncludeDependency(dependencyName)), (dependencyName) => resolveDependency(dependencyName));
    return modules.filter((resolution) => resolution != null);
}
async function scanDependenciesRecursively(rawPath, { shouldIncludeDependency = utils_1.defaultShouldIncludeDependency, limitDepth } = {}) {
    const rootPath = await (0, utils_2.maybeRealpath)(rawPath);
    if (!rootPath) {
        return {};
    }
    const _visitedPackagePaths = new Set();
    const getNodeModulePaths = createNodeModulePathsCreator();
    const maxDepth = limitDepth != null ? limitDepth : MAX_DEPTH;
    const recurse = async (resolution, depth = 0) => {
        const searchResults = Object.create(null);
        if (_visitedPackagePaths.has(resolution.path)) {
            return searchResults;
        }
        else {
            _visitedPackagePaths.add(resolution.path);
        }
        const [nodeModulePaths, packageJson] = await Promise.all([
            getNodeModulePaths(resolution.path),
            (0, utils_2.loadPackageJson)((0, utils_2.fastJoin)(resolution.path, 'package.json')),
        ]);
        if (!packageJson) {
            return searchResults;
        }
        else {
            resolution.version = packageJson.version || '';
        }
        const modules = await resolveDependencies(packageJson, nodeModulePaths, depth, shouldIncludeDependency);
        for (let idx = 0; idx < modules.length; idx++) {
            searchResults[modules[idx].name] = modules[idx];
        }
        if (depth + 1 < maxDepth) {
            const childResults = await (0, concurrency_1.taskAll)(modules, (resolution) => recurse(resolution, depth + 1));
            return (0, utils_1.mergeResolutionResults)(childResults, searchResults);
        }
        else {
            return searchResults;
        }
    };
    const searchResults = await recurse({
        source: 0 /* DependencyResolutionSource.RECURSIVE_RESOLUTION */,
        name: '',
        version: '',
        path: rootPath,
        originPath: rawPath,
        duplicates: null,
        depth: -1,
    });
    return searchResults;
}
const isOptionalPeerDependencyMeta = (peerDependenciesMeta, packageName) => {
    return (peerDependenciesMeta &&
        peerDependenciesMeta[packageName] != null &&
        typeof peerDependenciesMeta[packageName] === 'object' &&
        'optional' in peerDependenciesMeta[packageName] &&
        !!peerDependenciesMeta[packageName].optional);
};
//# sourceMappingURL=resolution.js.map