"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findUp = findUp;
exports.findUpSync = findUpSync;
exports.isDirSync = isDirSync;
exports.isDirEmptySync = isDirEmptySync;
exports.isSymLinkSync = isSymLinkSync;
exports.readDirNames = readDirNames;
exports.readDirNamesSync = readDirNamesSync;
exports.readFileBinary = readFileBinary;
exports.readFileUtf8 = readFileUtf8;
exports.readFileBinarySync = readFileBinarySync;
exports.readFileUtf8Sync = readFileUtf8Sync;
exports.readJson = readJson;
exports.readJsonSync = readJsonSync;
exports.remove = remove;
exports.removeSync = removeSync;
exports.safeReadFile = safeReadFile;
exports.safeStatsSync = safeStatsSync;
exports.safeReadFileSync = safeReadFileSync;
exports.uniqueSync = uniqueSync;
exports.writeJson = writeJson;
exports.writeJsonSync = writeJsonSync;
/**
 * @fileoverview File system utilities with cross-platform path handling.
 * Provides enhanced fs operations, glob matching, and directory traversal functions.
 */
const abort_signal_1 = require("./constants/abort-signal");
const globs_1 = require("./globs");
const json_1 = require("./json");
const path_1 = require("./path");
const sorts_1 = require("./sorts");
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
// `exports.SomeName = void 0;` which causes runtime errors.
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
const ArrayIsArray = Array.isArray;
// IMPORTANT: Do not use destructuring here - use direct assignment instead.
// tsgo has a bug that incorrectly transpiles destructured exports, resulting in
// `exports.SomeName = void 0;` which causes runtime errors.
// See: https://github.com/SocketDev/socket-packageurl-js/issues/3
const ObjectFreeze = Object.freeze;
const defaultRemoveOptions = ObjectFreeze({
    __proto__: null,
    force: true,
    maxRetries: 3,
    recursive: true,
    retryDelay: 200,
});
let _fs;
/**
 * Lazily load the fs module to avoid Webpack errors.
 */
/*@__NO_SIDE_EFFECTS__*/
function getFs() {
    if (_fs === undefined) {
        // Use non-'node:' prefixed require to avoid Webpack errors.
        // eslint-disable-next-line n/prefer-node-protocol
        _fs = /*@__PURE__*/ require('fs');
    }
    return _fs;
}
let _path;
/**
 * Lazily load the path module to avoid Webpack errors.
 * @private
 */
/*@__NO_SIDE_EFFECTS__*/
function getPath() {
    if (_path === undefined) {
        // Use non-'node:' prefixed require to avoid Webpack errors.
        // eslint-disable-next-line n/prefer-node-protocol
        _path = /*@__PURE__*/ require('path');
    }
    return _path;
}
/**
 * Process directory entries and filter for directories.
 * @private
 */
/*@__NO_SIDE_EFFECTS__*/
function innerReadDirNames(dirents, dirname, options) {
    const { ignore, includeEmpty = true, sort = true, } = { __proto__: null, ...options };
    const path = getPath();
    const names = dirents
        .filter((d) => d.isDirectory() &&
        (includeEmpty ||
            !isDirEmptySync(path.join(dirname || d.parentPath, d.name), {
                ignore,
            })))
        .map((d) => d.name);
    return sort ? names.sort(sorts_1.naturalCompare) : names;
}
/**
 * Stringify JSON with custom formatting options.
 * @private
 */
/*@__NO_SIDE_EFFECTS__*/
function stringify(json, EOL = '\n', finalEOL = true, replacer, spaces = 2) {
    const EOF = finalEOL ? EOL : '';
    const str = JSON.stringify(json, replacer, spaces);
    return `${str.replace(/\n/g, EOL)}${EOF}`;
}
/**
 * Find a file or directory by traversing up parent directories.
 */
/*@__NO_SIDE_EFFECTS__*/
async function findUp(name, options) {
    const { cwd = process.cwd(), signal = abort_signal_1 } = {
        __proto__: null,
        ...options,
    };
    let { onlyDirectories = false, onlyFiles = true } = {
        __proto__: null,
        ...options,
    };
    if (onlyDirectories) {
        onlyFiles = false;
    }
    if (onlyFiles) {
        onlyDirectories = false;
    }
    const fs = getFs();
    const path = getPath();
    let dir = path.resolve(cwd);
    const { root } = path.parse(dir);
    const names = ArrayIsArray(name) ? name : [name];
    while (dir && dir !== root) {
        for (const n of names) {
            if (signal?.aborted) {
                return undefined;
            }
            const thePath = path.join(dir, n);
            try {
                // eslint-disable-next-line no-await-in-loop
                const stats = await fs.promises.stat(thePath);
                if (!onlyDirectories && stats.isFile()) {
                    return thePath;
                }
                if (!onlyFiles && stats.isDirectory()) {
                    return thePath;
                }
            }
            catch { }
        }
        dir = path.dirname(dir);
    }
    return undefined;
}
/**
 * Synchronously find a file or directory by traversing up parent directories.
 */
/*@__NO_SIDE_EFFECTS__*/
function findUpSync(name, options) {
    const { cwd = process.cwd(), stopAt } = {
        __proto__: null,
        ...options,
    };
    let { onlyDirectories = false, onlyFiles = true } = {
        __proto__: null,
        ...options,
    };
    if (onlyDirectories) {
        onlyFiles = false;
    }
    if (onlyFiles) {
        onlyDirectories = false;
    }
    const fs = getFs();
    const path = getPath();
    let dir = path.resolve(cwd);
    const { root } = path.parse(dir);
    const stopDir = stopAt ? path.resolve(stopAt) : undefined;
    const names = ArrayIsArray(name) ? name : [name];
    while (dir && dir !== root) {
        // Check if we should stop at this directory.
        if (stopDir && dir === stopDir) {
            // Check current directory but don't go up.
            for (const n of names) {
                const thePath = path.join(dir, n);
                try {
                    const stats = fs.statSync(thePath);
                    if (!onlyDirectories && stats.isFile()) {
                        return thePath;
                    }
                    if (!onlyFiles && stats.isDirectory()) {
                        return thePath;
                    }
                }
                catch { }
            }
            return undefined;
        }
        for (const n of names) {
            const thePath = path.join(dir, n);
            try {
                const stats = fs.statSync(thePath);
                if (!onlyDirectories && stats.isFile()) {
                    return thePath;
                }
                if (!onlyFiles && stats.isDirectory()) {
                    return thePath;
                }
            }
            catch { }
        }
        dir = path.dirname(dir);
    }
    return undefined;
}
/**
 * Check if a path is a directory synchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
function isDirSync(filepath) {
    const fs = getFs();
    return fs.existsSync(filepath) && !!safeStatsSync(filepath)?.isDirectory();
}
/**
 * Check if a directory is empty synchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
function isDirEmptySync(dirname, options) {
    const { ignore = globs_1.defaultIgnore } = {
        __proto__: null,
        ...options,
    };
    const fs = getFs();
    try {
        const files = fs.readdirSync(dirname);
        const { length } = files;
        if (length === 0) {
            return true;
        }
        const matcher = (0, globs_1.getGlobMatcher)(ignore, {
            cwd: (0, path_1.pathLikeToString)(dirname),
        });
        let ignoredCount = 0;
        for (let i = 0; i < length; i += 1) {
            const file = files[i];
            if (file && matcher(file)) {
                ignoredCount += 1;
            }
        }
        return ignoredCount === length;
    }
    catch {
        // Return false for non-existent paths or other errors.
        return false;
    }
}
/**
 * Check if a path is a symbolic link synchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
function isSymLinkSync(filepath) {
    const fs = getFs();
    try {
        return fs.lstatSync(filepath).isSymbolicLink();
    }
    catch { }
    return false;
}
/**
 * Read directory names asynchronously with filtering and sorting.
 */
/*@__NO_SIDE_EFFECTS__*/
async function readDirNames(dirname, options) {
    const fs = getFs();
    try {
        return innerReadDirNames(await fs.promises.readdir(dirname, {
            __proto__: null,
            encoding: 'utf8',
            withFileTypes: true,
        }), String(dirname), options);
    }
    catch { }
    return [];
}
/**
 * Read directory names synchronously with filtering and sorting.
 */
/*@__NO_SIDE_EFFECTS__*/
function readDirNamesSync(dirname, options) {
    const fs = getFs();
    try {
        return innerReadDirNames(fs.readdirSync(dirname, {
            __proto__: null,
            encoding: 'utf8',
            withFileTypes: true,
        }), String(dirname), options);
    }
    catch { }
    return [];
}
/**
 * Read a file as binary data asynchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
async function readFileBinary(filepath, options) {
    // Don't specify encoding to get a Buffer.
    const opts = typeof options === 'string' ? { encoding: options } : options;
    const fs = getFs();
    return await fs.promises.readFile(filepath, {
        signal: abort_signal_1,
        ...opts,
        encoding: null,
    });
}
/**
 * Read a file as UTF-8 text asynchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
async function readFileUtf8(filepath, options) {
    const opts = typeof options === 'string' ? { encoding: options } : options;
    const fs = getFs();
    return await fs.promises.readFile(filepath, {
        signal: abort_signal_1,
        ...opts,
        encoding: 'utf8',
    });
}
/**
 * Read a file as binary data synchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
function readFileBinarySync(filepath, options) {
    // Don't specify encoding to get a Buffer
    const opts = typeof options === 'string' ? { encoding: options } : options;
    const fs = getFs();
    return fs.readFileSync(filepath, {
        ...opts,
        encoding: null,
    });
}
/**
 * Read a file as UTF-8 text synchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
function readFileUtf8Sync(filepath, options) {
    const opts = typeof options === 'string' ? { encoding: options } : options;
    const fs = getFs();
    return fs.readFileSync(filepath, {
        ...opts,
        encoding: 'utf8',
    });
}
/**
 * Read and parse a JSON file asynchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
async function readJson(filepath, options) {
    const opts = typeof options === 'string' ? { encoding: options } : options;
    const { reviver, throws, ...fsOptions } = {
        __proto__: null,
        ...opts,
    };
    const shouldThrow = throws === undefined || !!throws;
    const fs = getFs();
    let content = '';
    try {
        content = await fs.promises.readFile(filepath, {
            __proto__: null,
            encoding: 'utf8',
            ...fsOptions,
        });
    }
    catch (e) {
        if (shouldThrow) {
            throw e;
        }
        return undefined;
    }
    return (0, json_1.jsonParse)(content, {
        filepath: String(filepath),
        reviver,
        throws: shouldThrow,
    });
}
/**
 * Read and parse a JSON file synchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
function readJsonSync(filepath, options) {
    const opts = typeof options === 'string' ? { encoding: options } : options;
    const { reviver, throws, ...fsOptions } = {
        __proto__: null,
        ...opts,
    };
    const shouldThrow = throws === undefined || !!throws;
    const fs = getFs();
    let content = '';
    try {
        content = fs.readFileSync(filepath, {
            __proto__: null,
            encoding: 'utf8',
            ...fsOptions,
        });
    }
    catch (e) {
        if (shouldThrow) {
            throw e;
        }
        return undefined;
    }
    return (0, json_1.jsonParse)(content, {
        filepath: String(filepath),
        reviver,
        throws: shouldThrow,
    });
}
/**
 * Remove a file or directory asynchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
async function remove(filepath, options) {
    // Attempt to workaround occasional ENOTEMPTY errors in Windows.
    // https://github.com/jprichardson/node-fs-extra/issues/532#issuecomment-1178360589
    const fs = getFs();
    await fs.promises.rm(filepath, {
        ...defaultRemoveOptions,
        ...options,
    });
}
/**
 * Remove a file or directory synchronously.
 */
/*@__NO_SIDE_EFFECTS__*/
function removeSync(filepath, options) {
    const fs = getFs();
    fs.rmSync(filepath, {
        ...defaultRemoveOptions,
        ...options,
    });
}
/**
 * Safely read a file asynchronously, returning undefined on error.
 */
/*@__NO_SIDE_EFFECTS__*/
async function safeReadFile(filepath, options) {
    const opts = typeof options === 'string' ? { encoding: options } : options;
    const fs = getFs();
    try {
        return await fs.promises.readFile(filepath, {
            signal: abort_signal_1,
            ...opts,
        });
    }
    catch { }
    return undefined;
}
/**
 * Safely get file stats synchronously, returning undefined on error.
 */
/*@__NO_SIDE_EFFECTS__*/
function safeStatsSync(filepath, options) {
    const opts = typeof options === 'string' ? { encoding: options } : options;
    const fs = getFs();
    try {
        return fs.statSync(filepath, {
            __proto__: null,
            throwIfNoEntry: false,
            ...opts,
        });
    }
    catch { }
    return undefined;
}
/**
 * Safely read a file synchronously, returning undefined on error.
 */
/*@__NO_SIDE_EFFECTS__*/
function safeReadFileSync(filepath, options) {
    const opts = typeof options === 'string' ? { encoding: options } : options;
    const fs = getFs();
    try {
        return fs.readFileSync(filepath, {
            __proto__: null,
            ...opts,
        });
    }
    catch { }
    return undefined;
}
/**
 * Generate a unique filepath by adding number suffix if the path exists.
 */
/*@__NO_SIDE_EFFECTS__*/
function uniqueSync(filepath) {
    const fs = getFs();
    const path = getPath();
    const filepathStr = String(filepath);
    // If the file doesn't exist, return as is
    if (!fs.existsSync(filepathStr)) {
        return filepathStr;
    }
    const dirname = path.dirname(filepathStr);
    const ext = path.extname(filepathStr);
    const basename = path.basename(filepathStr, ext);
    let counter = 1;
    let uniquePath;
    do {
        uniquePath = path.join(dirname, `${basename}-${counter}${ext}`);
        counter++;
    } while (fs.existsSync(uniquePath));
    return uniquePath;
}
/**
 * Write JSON content to a file asynchronously with formatting.
 */
/*@__NO_SIDE_EFFECTS__*/
async function writeJson(filepath, jsonContent, options) {
    if (typeof options === 'string') {
        options = { encoding: options };
    }
    const { EOL, finalEOL, replacer, spaces, ...fsOptions } = {
        __proto__: null,
        ...options,
    };
    const fs = getFs();
    const jsonString = stringify(jsonContent, EOL, finalEOL, replacer, spaces);
    await fs.promises.writeFile(filepath, jsonString, {
        encoding: 'utf8',
        ...fsOptions,
        __proto__: null,
    });
}
/**
 * Write JSON content to a file synchronously with formatting.
 */
/*@__NO_SIDE_EFFECTS__*/
function writeJsonSync(filepath, jsonContent, options) {
    if (typeof options === 'string') {
        options = { encoding: options };
    }
    const { EOL, finalEOL, replacer, spaces, ...fsOptions } = {
        __proto__: null,
        ...options,
    };
    const fs = getFs();
    const jsonString = stringify(jsonContent, EOL, finalEOL, replacer, spaces);
    fs.writeFileSync(filepath, jsonString, {
        encoding: 'utf8',
        ...fsOptions,
        __proto__: null,
    });
}
