UNPKG

@synstack/fs

Version:
1,569 lines (1,563 loc) 48.9 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/fs.index.ts var fs_index_exports = {}; __export(fs_index_exports, { FsDir: () => FsDir2, FsFile: () => FsFile, dir: () => dir, file: () => file, files: () => files, filesFromDir: () => filesFromDir, fsDir: () => fsDir, fsFile: () => fsFile, fsFiles: () => fsFiles, fsFilesFromDir: () => fsFilesFromDir }); module.exports = __toCommonJS(fs_index_exports); // src/dir.lib.ts var import_git = require("@synstack/git"); var import_glob2 = require("@synstack/glob"); var import_path4 = require("@synstack/path"); var import_pipe2 = require("@synstack/pipe"); var fsSync2 = __toESM(require("fs"), 1); var fs2 = __toESM(require("fs/promises"), 1); // src/dirs-array.lib.ts var import_enhance = require("@synstack/enhance"); var import_path = require("@synstack/path"); var dirsArrayMethods = { filter(fn) { return fsDirs(this.filter(fn)); }, toPaths() { return this.map((dir2) => dir2.path); }, relativePathsFrom(dirOrFileOrPath) { return this.map((dir2) => dir2.relativePathFrom(dirOrFileOrPath)); } }; var fsDirs = (dirs2) => (0, import_enhance.enhance)("dirs_array", dirs2.map(fsDir), dirsArrayMethods); var dirs = fsDirs; // src/file.lib.ts var import_glob = require("@synstack/glob"); var import_json = require("@synstack/json"); var import_markdown = require("@synstack/markdown"); var import_path2 = require("@synstack/path"); var import_pipe = require("@synstack/pipe"); var import_str = require("@synstack/str"); var import_xml = require("@synstack/xml"); var import_yaml = require("@synstack/yaml"); var fsSync = __toESM(require("fs"), 1); var fs = __toESM(require("fs/promises"), 1); var FsFile = class _FsFile extends import_pipe.Pipeable { _path; _encoding; _schema; /** * Create a new FsFile instance from a path, a list of paths to be resolved, or an existing FsFile instance. * The resulting path will be an absolute path. * * @param paths - A path or an existing FsFile instance * @returns A new FsFile instance with UTF-8 encoding * * ```typescript * import { fsFile } from "@synstack/fs"; * * const relativeFile = fsFile("./relative/path.txt"); * const existingFile = fsFile(fsFile("/path/to/existing.txt")); * ``` */ static from(arg) { if (arg instanceof _FsFile) return arg; return new _FsFile(import_path2.path.resolve(arg), "utf-8"); } constructor(path3, encoding, schema) { super(); this._path = path3; this._encoding = encoding ?? "utf-8"; this._schema = schema ?? void 0; } /** * Provide a validation schema for the file content. To be used with: * - `.read.json` * - `.write.json` * - `.read.yaml` * - `.write.yaml` */ /** * Provide a validation schema for JSON/YAML operations. * The schema will be used to validate data when reading or writing JSON/YAML files. * * @typeParam NewSchema - The Zod schema type for validation * @param schema - A Zod schema to validate JSON/YAML data * @returns A new FsFile instance with the schema attached * * ```typescript * import { fsFile } from "@synstack/fs"; * import { z } from "zod"; * * const ConfigSchema = z.object({ * port: z.number(), * host: z.string() * }); * * const config = await fsFile("config.json") * .schema(ConfigSchema) * .read.json(); * // config is typed as { port: number, host: string } * ``` */ schema(schema) { return new _FsFile(this._path, this._encoding, schema); } /** * Get the path of the file. * * @returns The absolute path of the file */ valueOf() { return this._path; } /** * Get the current instance of the file. * Used for type compatibility with Pipeable. * * @returns The current FsFile instance */ instanceOf() { return this; } // #region sub actions /** * Access the read operations for the file. * Provides methods for reading file contents in various formats. * * @returns An FsFileRead instance with methods for reading the file * * ```typescript * const content = await fsFile("data.txt").read.text(); * const json = await fsFile("config.json").read.json(); * const yaml = await fsFile("config.yml").read.yaml(); * ``` */ get read() { return new FsFileRead( this._path, this._encoding, this._schema ); } /** * Access the write operations for the file. * Provides methods for writing content to the file in various formats. * * @returns An FsFileWrite instance with methods for writing to the file * * ```typescript * await fsFile("data.txt").write.text("Hello"); * await fsFile("config.json").write.json({ hello: "world" }); * await fsFile("config.yml").write.yaml({ config: true }); * ``` */ get write() { return new FsFileWrite( this._path, this._encoding, "overwrite", this._schema ); } // #endregion // #region sync /** * Get the absolute path of the file. * * @returns The absolute path as a string */ get path() { return this._path; } /** * Get the absolute path of the directory containing the file. * * @returns The directory path as a string */ dirPath() { return import_path2.path.dirname(this._path); } /** * Get an FsDir instance representing the directory containing the file. * * @returns An FsDir instance for the parent directory * * ```typescript * const file = fsFile("/path/to/file.txt"); * const parentDir = file.dir(); // FsDir for "/path/to" * ``` */ dir() { return FsDir2.cwd(this.dirPath()); } /** * Get the name of the file including its extension. * * @returns The file name with extension * * ```typescript * const file = fsFile("/path/to/document.txt"); * console.log(file.fileName()); // "document.txt" * ``` */ fileName() { return import_path2.path.filename(this._path); } /** * Get the extension of the file. * * @returns The file extension including the dot (e.g., ".txt") * * ```typescript * const file = fsFile("/path/to/document.txt"); * console.log(file.fileExtension()); // ".txt" * ``` */ fileExtension() { return import_path2.path.fileExtension(this._path); } /** * Get the name of the file without its extension. * * @returns The file name without extension * * ```typescript * const file = fsFile("/path/to/document.txt"); * console.log(file.fileNameWithoutExtension()); // "document" * ``` */ fileNameWithoutExtension() { return import_path2.path.filenameWithoutExtension(this._path); } /** * Get the MIME type of the file based on its extension. * * @returns The MIME type string or null if it cannot be determined * * ```typescript * const file = fsFile("/path/to/image.png"); * console.log(file.mimeType()); // "image/png" * ``` */ mimeType() { return import_path2.path.mimeType(this._path); } /** * Create a new FsFile instance with a path relative to this file's directory. * * @param relativePath - The relative path from this file's directory * @returns A new FsFile instance for the target path * * ```typescript * import { fsFile } from "@synstack/fs"; * * const sourceFile = fsFile("/path/to/source.txt"); * const targetFile = sourceFile.toFile("../output/target.txt"); * // targetFile.path === "/path/output/target.txt" * ``` */ toFile(relativePath) { const newPath = import_path2.path.resolve(this.dirPath(), relativePath); return new _FsFile(newPath); } /** * Create a new FsDir instance with a path relative to this file's directory. * * @param relativePath - The relative path from this file's directory * @returns A new FsDir instance for the target directory * * ```typescript * import { fsFile } from "@synstack/fs"; * * const sourceFile = fsFile("/path/to/source.txt"); * const outputDir = sourceFile.toDir("../output"); * // outputDir.path === "/path/output" * ``` */ toDir(relativePath) { const newPath = import_path2.path.join(this.dirPath(), relativePath); return FsDir2.cwd(newPath); } /** * Get the relative path from another file to this file * * ```ts * import { fsFile } from "@synstack/fs"; * * const file1 = fsFile("/path/to/file1.txt"); * const file2 = fsFile("/path/to-other/file2.txt"); * * console.log(file1.relativePathFrom(file2)); // ../to/file1.txt * ``` */ relativePathFrom(dirOrFile) { if (dirOrFile instanceof _FsFile) return this.relativePathFrom(dirOrFile.dir()); return import_path2.path.relative(dirOrFile.path, this.path); } /** * Get the relative path to go from this file to another * * ```ts * import { fsFile } from "@synstack/fs"; * * const file1 = fsFile("/path/to/file1.txt"); * const file2 = fsFile("/path/to-other/file2.txt"); * * console.log(file1.relativePathTo(file2)); // ../to-other/file2.txt * ``` */ relativePathTo(dirOrFileOrPath) { return import_path2.path.relative(this.dirPath(), dirOrFileOrPath.path); } /** * Check if the file is located within the specified directory. * * @param dirOrPath - The directory or path to check against * @returns True if the file is in the directory, false otherwise * * ```typescript * import { fsFile, fsDir } from "@synstack/fs"; * * const sourceFile = fsFile("/path/to/file.txt"); * console.log(sourceFile.isInDir(fsDir("/path"))); // true * console.log(sourceFile.isInDir(fsDir("/other"))); // false * ``` */ isInDir(dirOrPath) { return import_path2.path.isInPath(dirOrPath.valueOf(), this._path.valueOf()); } /** * Delete the file from the file system. * If the file doesn't exist, the operation is silently ignored. * * @returns A promise that resolves when the file is deleted * * ```typescript * import { fsFile } from "@synstack/fs"; * * const tempFile = fsFile("./temp.txt"); * await tempFile.write.text("temporary content"); * await tempFile.remove(); // File is deleted * ``` */ async remove() { await fs.rm(this._path, { recursive: true }).catch((e) => { if (e.code === "ENOENT") return; throw e; }); } /** * @deprecated Use {@link remove} instead. */ rm() { return this.remove(); } /** * Delete the file from the file system synchronously. * If the file doesn't exist, the operation is silently ignored. * * @synchronous * * ```typescript * import { fsFile } from "@synstack/fs"; * * const tempFile = fsFile("./temp.txt"); * tempFile.write.textSync("temporary content"); * tempFile.removeSync(); // File is deleted immediately * ``` */ removeSync() { try { fsSync.rmSync(this._path, { recursive: false }); } catch (error) { if (error.code === "ENOENT") return; throw error; } } /** * @deprecated Use {@link removeSync} instead. */ rmSync() { this.removeSync(); } /** * Move the file to a new location. * * @param newPath - The new path for the file or an existing FsFile instance * @returns A promise that resolves the new file */ async move(newPath) { const newFile = _FsFile.from(newPath); await fs.rename(this._path, newFile.path); return newFile; } /** * Move the file to a new location synchronously. * * @param newPath - The new path for the file or an existing FsFile instance * @returns The new file */ moveSync(newPath) { const newFile = _FsFile.from(newPath); fsSync.renameSync(this._path, newFile.path); return newFile; } /** * Check if the file exists in the file system. * * @returns A promise that resolves to true if the file exists, false otherwise * * ```typescript * import { fsFile } from "@synstack/fs"; * * const configFile = fsFile("./config.json"); * if (await configFile.exists()) { * const config = await configFile.read.json(); * } * ``` */ async exists() { return fs.access(this._path, fs.constants.F_OK).then(() => true).catch(() => false); } /** * Check if the file exists in the file system synchronously. * * @synchronous * @returns True if the file exists, false otherwise * * ```typescript * import { fsFile } from "@synstack/fs"; * * const configFile = fsFile("./config.json"); * if (configFile.existsSync()) { * const config = configFile.read.jsonSync(); * } * ``` */ existsSync() { try { fsSync.accessSync(this._path); return true; } catch (error) { if (error.code === "ENOENT") return false; throw error; } } /** * Get the creation date of the file. * * @returns A promise that resolves to the file's creation date * @throws If the file doesn't exist or cannot be accessed * * ```typescript * import { fsFile } from "@synstack/fs"; * * const sourceFile = fsFile("./source.txt"); * const created = await sourceFile.creationDate(); * console.log(`File created on: ${created.toISOString()}`); * ``` */ async creationDate() { const fileStats = await fs.stat(this._path); return fileStats.birthtime; } /** * Get the creation date of the file synchronously. * * @synchronous * @returns The file's creation date * @throws If the file doesn't exist or cannot be accessed * * ```typescript * import { fsFile } from "@synstack/fs"; * * const sourceFile = fsFile("./source.txt"); * const created = sourceFile.creationDateSync(); * console.log(`File created on: ${created.toISOString()}`); * ``` */ creationDateSync() { const stats = fsSync.statSync(this._path); return stats.birthtime; } /** * Check if the file path matches any of the provided glob patterns. * * @param globs - One or more glob patterns to match against, either as separate arguments or an array * @returns True if the file matches any pattern, false otherwise * * ```typescript * import { fsFile } from "@synstack/fs"; * * const sourceFile = fsFile("./src/components/Button.tsx"); * console.log(sourceFile.matchesGlobs("**\/*.tsx")); // true * console.log(sourceFile.matchesGlobs(["*.css", "*.html"])); // false * console.log(sourceFile.matchesGlobs("**\/*.ts", "**\/*.tsx")); // true * ``` */ matchesGlobs(...globs) { return import_glob.glob.matches(this._path, ...globs); } /** * Capture parts of the file path using a glob pattern * * ```ts * import { fsFile } from "@synstack/fs"; * * const myFile = fsFile("/my-domain/my-sub-domain/features/feature-name.controller.ts"); * const res = myFile.globCapture("/(*)/(*)/features/(*).controller.ts"); * if (!res) throw new Error("File doesn't match glob pattern"); * console.log(res[1]); // my-domain * console.log(res[2]); // my-sub-domain * console.log(res[3]); // feature-name.controller.ts * ``` */ globCapture(globPattern) { return import_glob.glob.capture(globPattern, this._path); } }; var FsFileRead = class { _path; _encoding; _schema; constructor(path3, encoding, schema) { this._path = path3; this._encoding = encoding; this._schema = schema; } get path() { return this._path; } // #region sync /** * Read the file contents as a string. * * @returns A promise that resolves to the file contents as a string * @throws If the file doesn't exist or cannot be read * * ```typescript * const content = await fsFile("data.txt").read.text(); * console.log(content); // "Hello, World!" * ``` */ async text() { return fs.readFile(this._path, this._encoding); } /** * Read the file contents as a string synchronously. * * @synchronous * @returns The file contents as a string * @throws If the file doesn't exist or cannot be read * * ```typescript * const content = fsFile("data.txt").read.textSync(); * console.log(content); // "Hello, World!" * ``` */ textSync() { return fsSync.readFileSync(this._path, this._encoding); } /** * Read the file contents and return a chainable string instance. * Used for further manipulation of the content using @synstack/str methods. * * @returns A promise that resolves to a chainable string instance * @throws If the file doesn't exist or cannot be read * * ```typescript * const content = await fsFile("data.txt").read.str(); * const lines = content * .split("\n") * .filter((line) => line.trim().length > 0); * ``` */ async str() { return this.text().then(import_str.str); } /** * Read the file contents and return a chainable string instance synchronously. * Used for further manipulation of the content using @synstack/str methods. * * @synchronous * @returns A chainable string instance * @throws If the file doesn't exist or cannot be read * * ```typescript * const content = fsFile("data.txt").read.strSync(); * const lines = content * .split("\n") * .filter((line) => line.trim().length > 0); * ``` */ strSync() { return (0, import_str.str)(this.textSync()); } /** * Read and parse the file contents as JSON. * If a schema is provided, the parsed data will be validated against it. * * @returns A promise that resolves to the parsed JSON data * @throws If the file doesn't exist, cannot be read, or contains invalid JSON * @throws If schema validation fails when a schema is provided * * ```typescript * interface Config { * port: number; * host: string; * } * * const config = await fsFile("config.json") * .schema(ConfigSchema) * .read.json(); * // config is automatically typed as the schema's output type * ``` */ json() { return this.text().then( (t) => import_json.json.deserialize(t, { schema: this._schema }) ); } /** * Read and parse the file contents as JSON synchronously. * If a schema is provided, the parsed data will be validated against it. * * @synchronous * @returns The parsed JSON data * @throws If the file doesn't exist, cannot be read, or contains invalid JSON * @throws If schema validation fails when a schema is provided * * ```typescript * const config = fsFile("config.json") * .schema(ConfigSchema) * .read.jsonSync(); * // config is automatically typed as the schema's output type * ``` */ jsonSync() { return import_json.json.deserialize(this.textSync(), { schema: this._schema }); } /** * Read and parse the file contents as YAML. * If a schema is provided, the parsed data will be validated against it. * * @typeParam T - The type of the parsed YAML data * @returns A promise that resolves to the parsed YAML data * @throws If the file doesn't exist, cannot be read, or contains invalid YAML * @throws If schema validation fails when a schema is provided * * ```typescript * interface Config { * environment: string; * settings: Record<string, unknown>; * } * * const config = await fsFile("config.yml") * .schema(ConfigSchema) * .read.yaml(); * // config is automatically typed as the schema's output type * ``` */ yaml() { return this.text().then( (t) => import_yaml.yaml.deserialize(t, { schema: this._schema }) ); } /** * Read and parse the file contents as YAML synchronously. * If a schema is provided, the parsed data will be validated against it. * * @typeParam T - The type of the parsed YAML data * @synchronous * @returns The parsed YAML data * @throws If the file doesn't exist, cannot be read, or contains invalid YAML * @throws If schema validation fails when a schema is provided * * ```typescript * const config = fsFile("config.yml") * .schema(ConfigSchema) * .read.yamlSync(); * // config is automatically typed as the schema's output type * ``` */ yamlSync() { return import_yaml.yaml.deserialize(this.textSync(), { schema: this._schema }); } /** * Read and parse the file contents as XML using @synstack/xml. * This parser is specifically designed for LLM-related XML processing. * * @typeParam T - The type of the parsed XML nodes array, must extend Array<Xml.Node> * @returns A promise that resolves to the parsed XML nodes * @throws If the file doesn't exist, cannot be read, or contains invalid XML * @see {@link https://github.com/pAIrprogio/synscript/tree/main/packages/xml|@synstack/xml documentation} * * ```typescript * interface XmlNode { * tag: string; * attributes: Record<string, string>; * children: Array<XmlNode>; * } * * const nodes = await fsFile("data.xml").read.xml<XmlNode[]>(); * console.log(nodes[0].tag); // "root" * console.log(nodes[0].attributes.id); // "main" * ``` * * @remarks * - Uses a non-spec-compliant XML parser tailored for LLM use cases * - Optimized for simple XML structures commonly used in LLM responses * - Does not support all XML features (see documentation for details) */ async xml() { return this.text().then((content) => import_xml.xml.parse(content)); } /** * Read and parse the file contents as XML synchronously using @synstack/xml. * This parser is specifically designed for LLM-related XML processing. * * @typeParam T - The type of the parsed XML nodes array, must extend Array<Xml.Node> * @synchronous * @returns The parsed XML nodes * @throws If the file doesn't exist, cannot be read, or contains invalid XML * @see {@link https://github.com/pAIrprogio/synscript/tree/main/packages/xml|@synstack/xml documentation} * * ```typescript * const nodes = fsFile("data.xml").read.xmlSync<XmlNode[]>(); * console.log(nodes[0].tag); // "root" * console.log(nodes[0].attributes.id); // "main" * ``` * * @remarks * - Uses a non-spec-compliant XML parser tailored for LLM use cases * - Optimized for simple XML structures commonly used in LLM responses * - Does not support all XML features (see documentation for details) */ xmlSync() { return import_xml.xml.parse(this.textSync()); } /** * Read the file contents and encode them as a base64 string. * Useful for handling binary data or preparing content for data URLs. * * @returns A promise that resolves to the file contents as a base64-encoded string * @throws If the file doesn't exist or cannot be read * * ```typescript * // Read and encode an image file * const imageBase64 = await fsFile("image.png").read.base64(); * console.log(imageBase64); // "iVBORw0KGgoAAAANSUhEUgAA..." * * // Create a data URL for use in HTML/CSS * const dataUrl = `data:image/png;base64,${imageBase64}`; * ``` */ async base64() { return fs.readFile(this._path, "base64"); } /** * Read the file contents and encode them as a base64 string synchronously. * Useful for handling binary data or preparing content for data URLs. * * @synchronous * @returns The file contents as a base64-encoded string * @throws If the file doesn't exist or cannot be read * * ```typescript * const imageBase64 = fsFile("image.png").read.base64Sync(); * const dataUrl = `data:image/png;base64,${imageBase64}`; * ``` */ base64Sync() { return fsSync.readFileSync(this._path, "base64"); } /** * Read the file contents and create a synstack-compatible Base64Data object. * This format includes MIME type information along with the base64-encoded data. * * @param defaultMimeType - The MIME type to use if it cannot be determined from the file extension * @returns A promise that resolves to a Base64Data object containing the encoded content and MIME type * @throws If the file doesn't exist or cannot be read * * ```typescript * // Read an image with automatic MIME type detection * const imageData = await fsFile("image.png").read.base64Data(); * console.log(imageData); * // { * // type: "base64", * // data: "iVBORw0KGgoAAAANSUhEUgAA...", * // mimeType: "image/png" * // } * * // Specify a custom MIME type for a binary file * const data = await fsFile("custom.bin") * .read.base64Data("application/custom"); * ``` */ async base64Data(defaultMimeType = "application/octet-stream") { return { type: "base64", data: await this.base64(), mimeType: defaultMimeType }; } /** * Read the file contents and create a synstack-compatible Base64Data object synchronously. * This format includes MIME type information along with the base64-encoded data. * * @param defaultMimeType - The MIME type to use if it cannot be determined from the file extension * @synchronous * @returns A Base64Data object containing the encoded content and MIME type * @throws If the file doesn't exist or cannot be read * * ```typescript * const imageData = fsFile("image.png").read.base64DataSync(); * console.log(imageData); * // { * // type: "base64", * // data: "iVBORw0KGgoAAAANSUhEUgAA...", * // mimeType: "image/png" * // } * ``` */ base64DataSync(defaultMimeType = "application/octet-stream") { return { type: "base64", data: this.base64Sync(), mimeType: defaultMimeType }; } /** * Read the file contents and parse them as a markdown document. * If a schema is provided, the header data will be validated before returning. * * @returns A promise that resolves to the markdown document * @throws If the file doesn't exist, cannot be read, or contains invalid markdown * @throws If schema validation fails when a schema is provided */ md() { return this.text().then( (t) => import_markdown.MdDoc.withOptions({ schema: this._schema }).fromString(t) ); } /** * Read the file contents and parse them as a markdown document synchronously. * If a schema is provided, the header data will be validated before returning. * * @synchronous * @returns The markdown document * @throws If the file doesn't exist, cannot be read, or contains invalid markdown * @throws If schema validation fails when a schema is provided */ mdSync() { return import_markdown.MdDoc.withOptions({ schema: this._schema }).fromString(this.textSync()); } }; var FsFileWrite = class _FsFileWrite { _path; _encoding; _schema; _mode; constructor(path3, encoding, mode = "overwrite", schema) { this._path = path3; this._encoding = encoding; this._schema = schema; this._mode = mode; } /** * Set the write mode of the file * @argument preserve: If the file already exists, it will be left untouched * @argument overwrite: If the file already exists, it will be overwritten */ mode(writeMode) { return new _FsFileWrite(this._path, this._encoding, writeMode, this._schema); } /** * Write text content to a file asynchronously. * Creates parent directories if they don't exist. * Respects the write mode (overwrite/preserve) setting. * * @param content - The content to write, will be converted to string using toString() * @returns A promise that resolves when the write operation is complete * @throws If the write operation fails or if parent directory creation fails */ async text(content) { if (this._mode === "preserve" && await FsFile.from(this._path).exists()) return; const dirname = import_path2.path.dirname(this._path); await fs.mkdir(dirname, { recursive: true }); await fs.writeFile(this._path, content.toString(), this._encoding); } /** * Write text content to a file synchronously. * Creates parent directories if they don't exist. * Respects the write mode (overwrite/preserve) setting. * * @param content - The content to write, will be converted to string using toString() * @synchronous * @throws If the write operation fails or if parent directory creation fails */ textSync(content) { if (this._mode === "preserve" && FsFile.from(this._path).existsSync()) return; const dirname = import_path2.path.dirname(this._path); fsSync.mkdirSync(dirname, { recursive: true }); fsSync.writeFileSync(this._path, content.toString(), this._encoding); } /** * Write data as JSON to a file asynchronously. * The data will be serialized using JSON.stringify. * If a schema is provided, the data will be validated before writing. * * @typeParam T - The type of data being written * @param data - The data to write, must be JSON-serializable * @returns A promise that resolves when the write operation is complete * @throws If schema validation fails or if the write operation fails */ async json(data) { return this.text(import_json.json.serialize(data, { schema: this._schema })); } // Todo: add mergeJson /** * Write data as formatted JSON to a file asynchronously. * The data will be serialized using JSON.stringify with pretty printing. * If a schema is provided, the data will be validated before writing. * * @typeParam T - The type of data being written * @param data - The data to write, must be JSON-serializable * @returns A promise that resolves when the write operation is complete * @throws If schema validation fails or if the write operation fails */ async prettyJson(data) { return this.text( import_json.json.serialize(data, { schema: this._schema, pretty: true }) + "\n" ); } /** * Write data as JSON to a file synchronously. * The data will be serialized using JSON.stringify. * If a schema is provided, the data will be validated before writing. * * @typeParam T - The type of data being written * @param data - The data to write, must be JSON-serializable * @synchronous * @throws If schema validation fails or if the write operation fails */ jsonSync(data) { return this.textSync(import_json.json.serialize(data, { schema: this._schema })); } /** * Write data as formatted JSON to a file synchronously. * The data will be serialized using JSON.stringify with pretty printing. * If a schema is provided, the data will be validated before writing. * * @typeParam T - The type of data being written * @param data - The data to write, must be JSON-serializable * @synchronous * @throws If schema validation fails or if the write operation fails */ prettyJsonSync(data) { return this.textSync( import_json.json.serialize(data, { schema: this._schema, pretty: true }) + "\n" ); } /** * Write data as YAML to a file asynchronously. * The data will be serialized using YAML.stringify. * If a schema is provided, the data will be validated before writing. * * @typeParam T - The type of data being written * @param data - The data to write, must be YAML-serializable * @throws If schema validation fails or if the write operation fails */ async yaml(data) { return this.text(import_yaml.yaml.serialize(data, { schema: this._schema })); } /** * Write data as YAML to a file synchronously. * The data will be serialized using YAML.stringify. * If a schema is provided, the data will be validated before writing. * * @typeParam T - The type of data being written * @param data - The data to write, must be YAML-serializable * @synchronous * @throws If schema validation fails or if the write operation fails */ yamlSync(data) { return this.textSync(import_yaml.yaml.serialize(data, { schema: this._schema })); } /** * Write base64-encoded data to a file asynchronously. * Creates parent directories if they don't exist. * Respects the write mode (overwrite/preserve) setting. * * @param data - The base64-encoded string to write * @throws If the write operation fails or if parent directory creation fails */ async base64(data) { if (this._mode === "preserve" && await FsFile.from(this._path).exists()) return; const dirname = import_path2.path.dirname(this._path); await fs.mkdir(dirname, { recursive: true }); await fs.writeFile(this._path, data.toString(), "base64"); } /** * Write base64-encoded data to a file synchronously. * Creates parent directories if they don't exist. * Respects the write mode (overwrite/preserve) setting. * * @param data - The base64-encoded string to write * @synchronous * @throws If the write operation fails or if parent directory creation fails */ base64Sync(data) { if (this._mode === "preserve" && FsFile.from(this._path).existsSync()) return; const dirname = import_path2.path.dirname(this._path); fsSync.mkdirSync(dirname, { recursive: true }); return fsSync.writeFileSync(this._path, data.toString(), "base64"); } /** * Write a markdown document to a file asynchronously. * The markdown document will be serialized using MdDoc.toMd. * If a schema is provided, the data will be validated before writing. * * @param data - The markdown document to write * @throws If schema validation fails or if the write operation fails */ md(data) { return this.text(data.toMd()); } /** * Write a markdown document to a file synchronously. * The markdown document will be serialized using MdDoc.toMd. * If a schema is provided, the data will be validated before writing. * * @param data - The markdown document to write * @throws If schema validation fails or if the write operation fails * @synchronous */ mdSync(data) { return this.textSync(data.toMd()); } }; var fsFile = FsFile.from; var file = FsFile.from; // src/files-array.lib.ts var import_enhance2 = require("@synstack/enhance"); var import_path3 = require("@synstack/path"); var filesArrayMethods = { filter(fn) { return fsFiles(this.filter(fn)); }, filterGlobs(...patterns) { return this.filter((file2) => file2.matchesGlobs(...patterns)); }, filterMimeTypes(...mimeTypes) { const types = new Set(mimeTypes); return this.filter((file2) => { const mimeType = file2.mimeType(); if (mimeType === null) return false; return types.has(mimeType); }); }, filterDir(dirOrPath) { return this.filter((file2) => file2.isInDir(dirOrPath)); }, toPaths() { return this.map((file2) => file2.path); }, relativePathsFrom(dirOrFileOrPath) { return this.map((file2) => file2.relativePathFrom(dirOrFileOrPath)); } }; var fsFiles = (files2) => (0, import_enhance2.enhance)( "files_array", files2.map((f) => { if (f instanceof FsFile) return f; return fsFile(f); }), filesArrayMethods ); var files = fsFiles; var fsFilesFromDir = (dir2) => (files2) => (0, import_enhance2.enhance)( "files_array", files2.map((f) => { if (f instanceof FsFile) return f; return dir2.file(f); }), filesArrayMethods ); var filesFromDir = fsFilesFromDir; // src/dir.lib.ts var FsDir2 = class _FsDir extends import_pipe2.Pipeable { _path; constructor(path3) { super(); this._path = path3; } /** * Get the string representation of the directory path. * * @returns The absolute path of the directory as a string * * ```typescript * const srcDir = fsDir("./src"); * console.log(srcDir.toString()); // "/absolute/path/to/src" * ``` */ toString() { return this._path; } /** * Get the primitive value of the directory path. * * @returns The absolute path of the directory */ valueOf() { return this._path; } /** * Get the current instance of the directory. * Used for type compatibility with Pipeable. * * @returns The current FsDir instance */ instanceOf() { return this; } /** * Get the absolute path of the directory. * * @returns The absolute path as a string * * ```typescript * const srcDir = fsDir("./src"); * console.log(srcDir.path); // "/absolute/path/to/src" * ``` */ get path() { return this._path; } /** * Get the relative path from this directory to another file/directory * * @param dirOrfile - The other directory * @returns The relative path as a string */ relativePathTo(dirOrfile) { return import_path4.path.relative(this.path, dirOrfile.path); } /** * Get the relative path from another file/directory to this directory * * @param dirOrFile - The other directory * @returns The relative path as a string */ relativePathFrom(dirOrFile) { if (dirOrFile instanceof FsFile) return this.relativePathFrom(dirOrFile.dir()); return import_path4.path.relative(dirOrFile.path, this.path); } static cwd(arg) { if (arg instanceof _FsDir) return arg; return new _FsDir(import_path4.path.resolve(arg)); } /** * Create a new directory instance with a path relative to this directory. * * @alias {@link to} * @param relativePath - The relative path to append to the current directory * @returns A new FsDir instance representing the combined path * * ```typescript * const projectDir = fsDir("/path/to/project"); * * // Navigate to subdirectories * const srcDir = projectDir.toDir("src"); * const testDir = projectDir.toDir("tests"); * * // Navigate up and down * const siblingDir = srcDir.toDir("../other"); * ``` */ toDir(relativePath) { const newPath = import_path4.path.join(this._path, relativePath); return new _FsDir(newPath); } /** * Create a new directory instance with a path relative to this directory. * * @alias {@link toDir} * @param relativePath - The relative path to append to the current directory * @returns A new FsDir instance representing the combined path * * ```typescript * const projectDir = fsDir("/path/to/project"); * * // Navigate to subdirectories * const srcDir = projectDir.to("src"); * const testDir = projectDir.to("tests"); * * // Navigate up and down * const siblingDir = srcDir.to("../other"); * ``` */ to(relativePath) { return this.toDir(relativePath); } /** * Create a new file instance with a path relative to this directory. * * @alias {@link file} * @param relativePath - The relative path to the file from this directory * @returns A new FsFile instance for the specified path * @throws If an absolute path is provided * * ```typescript * const srcDir = fsDir("./src"); * * // Access files in the directory * const configFile = srcDir.toFile("config.json"); * const deepFile = srcDir.toFile("components/Button.tsx"); * * // Error: Cannot use absolute paths * srcDir.toFile("/absolute/path"); // throws Error * ``` */ toFile(relativePath) { if (import_path4.path.isAbsolute(relativePath)) throw new Error(` Trying to access a dir file from an absolute paths: - Folder path: ${this._path} - File path: ${relativePath} `); return FsFile.from(import_path4.path.resolve(this._path, relativePath)); } /** * Create a new file instance with a path relative to this directory. * * @alias {@link toFile} * @param relativePath - The relative path to the file from this directory * @returns A new FsFile instance for the specified path * @throws If an absolute path is provided * * ```typescript * const srcDir = fsDir("./src"); * * // Access files in the directory * const configFile = srcDir.file("config.json"); * const deepFile = srcDir.file("components/Button.tsx"); * * // Error: Cannot use absolute paths * srcDir.file("/absolute/path"); // throws Error * ``` */ file(relativePath) { return this.toFile(relativePath); } /** * Get the name of the directory (last segment of the path). * * @returns The directory name without the full path * * ```typescript * const srcDir = fsDir("/path/to/project/src"); * console.log(srcDir.name()); // "src" * ``` */ name() { return import_path4.path.filename(this._path); } /** * Check if the directory exists in the file system. * * @returns A promise that resolves to true if the directory exists, false otherwise * * ```typescript * const configDir = fsDir("./config"); * * if (await configDir.exists()) { * // Directory exists, safe to use * const files = await configDir.glob("*.json"); * } else { * // Create the directory first * await configDir.make(); * } * ``` */ async exists() { return fs2.access(this._path, fsSync2.constants.F_OK).then(() => true).catch(() => false); } /** * Check if the directory exists in the file system synchronously. * * @synchronous * @returns True if the directory exists, false otherwise * * ```typescript * const configDir = fsDir("./config"); * * if (configDir.existsSync()) { * // Directory exists, safe to use * const files = configDir.globSync("*.json"); * } else { * // Create the directory first * configDir.makeSync(); * } * ``` */ existsSync() { try { fsSync2.accessSync(this._path, fsSync2.constants.F_OK); return true; } catch (error) { if (error.code === "ENOENT") return false; throw error; } } /** * Create the directory and any necessary parent directories. * * @returns A promise that resolves when the directory is created * * ```typescript * const assetsDir = fsDir("./dist/assets/images"); * * // Creates all necessary parent directories * await assetsDir.make(); * ``` */ async make() { await fs2.mkdir(this._path, { recursive: true }); } /** * Create the directory and any necessary parent directories synchronously. * * @synchronous * * ```typescript * const assetsDir = fsDir("./dist/assets/images"); * * // Creates all necessary parent directories immediately * assetsDir.makeSync(); * ``` */ makeSync() { fsSync2.mkdirSync(this._path, { recursive: true }); } /** * Move the directory to a new location. * * @param newPath - The new path for the directory * @returns A promise that resolves the new directory */ async move(newPath) { const newDir = _FsDir.cwd(newPath); await fs2.rename(this._path, newDir.path); return newDir; } /** * Move the directory to a new location synchronously. * * @param newPath - The new path for the directory * @returns The new directory */ moveSync(newPath) { const newDir = _FsDir.cwd(newPath); fsSync2.renameSync(this._path, newDir.path); return newDir; } /** * Remove the directory and all its contents recursively. * If the directory doesn't exist, the operation is silently ignored. * * @returns A promise that resolves when the directory is removed * * ```typescript * const tempDir = fsDir("./temp"); * * // Remove directory and all contents * await tempDir.rm(); * ``` */ async rm() { await fs2.rm(this._path, { recursive: true }).catch((e) => { if (e.code === "ENOENT") return; throw e; }); } /** * Remove the directory and all its contents recursively synchronously. * If the directory doesn't exist, the operation is silently ignored. * * @synchronous * * ```typescript * const tempDir = fsDir("./temp"); * * // Remove directory and all contents immediately * tempDir.rmSync(); * ``` */ rmSync() { try { fsSync2.rmSync(this._path, { recursive: true }); } catch (error) { if (error.code === "ENOENT") return; throw error; } } /** * Find files in the directory that match the specified glob patterns. * * @param patterns - One or more glob patterns to match against * @returns A promise that resolves to an FsFileArray containing the matching files * * ```typescript * const srcDir = fsDir("./src"); * * // Find all TypeScript files * const tsFiles = await srcDir.glob("**\/*.ts"); * * // Find multiple file types * const assets = await srcDir.glob("**\/*.{png,jpg,svg}"); * * // Use array of patterns * const configs = await srcDir.glob(["*.json", "*.yaml"]); * ``` */ glob(...patterns) { return import_glob2.glob.cwd(this._path).options({ absolute: true }).find(...patterns).then(fsFiles); } /** * Find files in the directory that match the specified glob patterns synchronously. * * @param patterns - One or more glob patterns to match against * @returns An FsFileArray containing the matching files * @synchronous * * ```typescript * const srcDir = fsDir("./src"); * * // Find all TypeScript files synchronously * const tsFiles = srcDir.globSync("**\/*.ts"); * * // Find multiple file types * const assets = srcDir.globSync("**\/*.{png,jpg,svg}"); * ``` */ globSync(...patterns) { return fsFiles( import_glob2.glob.cwd(this._path).options({ absolute: true }).findSync(...patterns) ); } /** * Find folders in the directory that match the specified glob patterns. * * @param patterns - One or more glob patterns to match against * @returns A promise that resolves to an FsDirArray containing the matching folders */ globDirs(...patterns) { const patternsWithTrailingSlash = patterns.flat().map((p) => import_glob2.glob.ensureDirTrailingSlash(p)); return import_glob2.glob.cwd(this._path).options({ nodir: false, absolute: true }).find(patternsWithTrailingSlash).then(dirs); } /** * Find folders in the directory that match the specified glob patterns synchronously. * * @param patterns - One or more glob patterns to match against * @returns An FsDirArray containing the matching folders * @synchronous */ globDirsSync(...patterns) { const patternsWithTrailingSlash = patterns.flat().map((p) => import_glob2.glob.ensureDirTrailingSlash(p)); return dirs( import_glob2.glob.cwd(this._path).options({ nodir: false, absolute: true }).findSync(patternsWithTrailingSlash) ); } /** * Find files tracked by git in the directory. * * @returns A promise that resolves to an FsFileArray containing the git-tracked files * * ```typescript * const projectDir = fsDir("./project"); * * // Get all git-tracked files in the directory * const trackedFiles = await projectDir.gitLs(); * ``` */ async gitLs() { return import_git.git.ls(this._path).then(fsFilesFromDir(this)); } /** * Execute a command in the directory. */ async exec(template, ...args) { const { execa } = await import("execa").catch(() => { throw new Error( "The `execa` package is not installed. Please install it first." ); }); return execa({ cwd: this.path })(template, ...args); } }; var fsDir = FsDir2.cwd; var dir = FsDir2.cwd; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { FsDir, FsFile, dir, file, files, filesFromDir, fsDir, fsFile, fsFiles, fsFilesFromDir }); //# sourceMappingURL=fs.index.cjs.map