import * as fsp from "node:fs/promises";
import { codeModeExcludedFiles, codeModeSyncExcludedDirectories, } from "../file-system-helpers.js";
import { getLogger } from "../util/logger.js";
function isNodeError(error) {
    return error instanceof Error && "code" in error;
}
/**
 * Lists the contents of a local directory. The function will load the contents of files into memory, but as it is a generator, this
 * will happen lazily, so only one file will be loaded into memory at a time. The function will also ignore files in the directories
 * listed in `codeModeSyncExcludedDirectories` and `codeModeExcludedFiles`.
 *
 * @param localDirectoryPath - The path to the local directory to list.
 * @returns An async generator that yields the contents of the directory.
 */
export async function* listLocalDirectory(localDirectoryPath) {
    const logger = getLogger();
    const localDirHandle = await fsp.opendir(localDirectoryPath);
    for await (const localDirEntry of localDirHandle) {
        const name = localDirEntry.name
            // On macOS, filenames in the filesystem are typically stored in decomposed form from a Unicode normalization perspective
            // (i.e. NFD). We need to convert it to precomposed form (i.e. NFC) to match what everything else uses and expects.
            .normalize("NFC");
        const localFilePath = `${localDirectoryPath}/${name}`;
        if (localDirEntry.isFile()) {
            if (codeModeExcludedFiles.includes(name)) {
                continue;
            }
            try {
                const status = await fsp.lstat(localFilePath);
                // extract the executable bit from the owner's permissions, this is how git does it
                const executable = (status.mode & fsp.constants.S_IXUSR) !== 0;
                const fileContents = await fsp.readFile(localFilePath);
                yield {
                    type: "-",
                    executable,
                    name,
                    localPath: localFilePath,
                    contents: fileContents,
                };
            }
            catch (error) {
                if (isNodeError(error) && error.code === "ENOENT") {
                    logger.warn(`File was deleted during directory scan, skipping: ${localFilePath}`);
                    continue;
                }
                throw error;
            }
        }
        else if (localDirEntry.isDirectory()) {
            if (codeModeSyncExcludedDirectories.includes(name)) {
                continue;
            }
            yield {
                type: "d",
                name,
                localPath: localFilePath,
            };
        }
        else if (localDirEntry.isSymbolicLink()) {
            try {
                const target = await fsp.readlink(localFilePath);
                yield {
                    type: "l",
                    name,
                    localPath: localFilePath,
                    target,
                };
            }
            catch (error) {
                if (isNodeError(error) && error.code === "ENOENT") {
                    logger.warn(`Symlink was deleted during directory scan, skipping: ${localFilePath}`);
                    continue;
                }
                throw error;
            }
        }
        else {
            // ignore files of other types
            continue;
        }
    }
}
//# sourceMappingURL=list-dir.js.map