import { r as resolvePath, j as joinPaths, g as getDirectoryPath } from './path-utils-DDA4_T0g.js';

class ResolveModuleError extends Error {
    code;
    constructor(code, message) {
        super(message);
        this.code = code;
    }
}
const defaultDirectoryIndexFiles = ["index.mjs", "index.js"];
/**
 * Resolve a module
 * @param host
 * @param name
 * @param options
 * @returns
 */
async function resolveModule(host, name, options) {
    const realpath = async (x) => resolvePath(await host.realpath(x));
    const { baseDir } = options;
    const absoluteStart = baseDir === "" ? "." : await realpath(resolvePath(baseDir));
    if (!(await isDirectory(host, absoluteStart))) {
        throw new TypeError(`Provided basedir '${baseDir}'is not a directory.`);
    }
    // Check if the module name is referencing a path(./foo, /foo, file:/foo)
    if (/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/.test(name)) {
        const res = resolvePath(absoluteStart, name);
        const m = (await loadAsFile(res)) || (await loadAsDirectory(res));
        if (m) {
            return m;
        }
    }
    const module = await findAsNodeModule(name, absoluteStart);
    if (module)
        return module;
    throw new ResolveModuleError("MODULE_NOT_FOUND", `Cannot find module '${name}' from '${baseDir}'`);
    /**
     * Returns a list of all the parent directory and the given one.
     */
    function listAllParentDirs(baseDir) {
        const paths = [baseDir];
        let current = getDirectoryPath(baseDir);
        while (current !== paths[paths.length - 1]) {
            paths.push(current);
            current = getDirectoryPath(current);
        }
        return paths;
    }
    function getPackageCandidates(name, baseDir) {
        const dirs = listAllParentDirs(baseDir);
        return dirs.flatMap((x) => [
            { path: x, type: "self" },
            { path: joinPaths(x, "node_modules", name), type: "node_modules" },
        ]);
    }
    async function findAsNodeModule(name, baseDir) {
        const dirs = getPackageCandidates(name, baseDir);
        for (const { type, path } of dirs) {
            if (type === "node_modules") {
                if (await isDirectory(host, path)) {
                    const n = await loadAsDirectory(path, true);
                    if (n)
                        return n;
                }
            }
            else if (type === "self") {
                const pkgFile = resolvePath(path, "package.json");
                if (await isFile(host, pkgFile)) {
                    const pkg = await readPackage(host, pkgFile);
                    if (pkg.name === name) {
                        const n = await loadPackage(path, pkg);
                        if (n)
                            return n;
                    }
                }
            }
        }
        return undefined;
    }
    async function loadAsDirectory(directory, mustBePackage) {
        const pkgFile = resolvePath(directory, "package.json");
        if (await isFile(host, pkgFile)) {
            const pkg = await readPackage(host, pkgFile);
            return loadPackage(directory, pkg);
        }
        if (mustBePackage) {
            return undefined;
        }
        for (const file of options.directoryIndexFiles ?? defaultDirectoryIndexFiles) {
            const resolvedFile = await loadAsFile(joinPaths(directory, file));
            if (resolvedFile) {
                return resolvedFile;
            }
        }
        return undefined;
    }
    async function loadPackage(directory, pkg) {
        const mainFile = options.resolveMain ? options.resolveMain(pkg) : pkg.main;
        if (typeof mainFile !== "string") {
            throw new TypeError(`package "${pkg.name}" main must be a string but was '${mainFile}'`);
        }
        const mainFullPath = resolvePath(directory, mainFile);
        let loaded;
        try {
            loaded = (await loadAsFile(mainFullPath)) ?? (await loadAsDirectory(mainFullPath));
        }
        catch (e) {
            throw new Error(`Cannot find module '${mainFullPath}'. Please verify that the package.json has a valid "main" entry`);
        }
        if (loaded) {
            if (loaded.type === "module") {
                return loaded;
            }
            return {
                type: "module",
                path: await realpath(directory),
                mainFile: loaded.path,
                manifest: pkg,
            };
        }
        else {
            throw new ResolveModuleError("INVALID_MAIN", `Package ${pkg.name} main file "${mainFile}" is invalid.`);
        }
    }
    async function loadAsFile(file) {
        if (await isFile(host, file)) {
            return resolvedFile(file);
        }
        const extensions = [".mjs", ".js"];
        for (const ext of extensions) {
            const fileWithExtension = file + ext;
            if (await isFile(host, fileWithExtension)) {
                return resolvedFile(fileWithExtension);
            }
        }
        return undefined;
    }
    async function resolvedFile(path) {
        return { type: "file", path: await realpath(path) };
    }
}
async function readPackage(host, pkgfile) {
    const content = await host.readFile(pkgfile);
    return JSON.parse(content);
}
async function isDirectory(host, path) {
    try {
        const stats = await host.stat(path);
        return stats.isDirectory();
    }
    catch (e) {
        if (e.code === "ENOENT" || e.code === "ENOTDIR") {
            return false;
        }
        throw e;
    }
}
async function isFile(host, path) {
    try {
        const stats = await host.stat(path);
        return stats.isFile();
    }
    catch (e) {
        if (e.code === "ENOENT" || e.code === "ENOTDIR") {
            return false;
        }
        throw e;
    }
}

export { ResolveModuleError, resolveModule };
