UNPKG

fs-fixture

Version:

Easily create test fixtures at a temporary file-system path

301 lines (293 loc) 10.6 kB
import { CopyOptions } from 'node:fs'; import fs from 'node:fs/promises'; /** * A subset of `fs/promises` methods used by FsFixture. * Compatible with Node.js `fs/promises`, `@platformatic/vfs`, * `memfs`, and other fs-compatible implementations. * * Pass a custom implementation to `createFixture({ fs })` * to use a virtual filesystem. */ type FsPromises = { readFile: { (path: string, options?: { encoding?: null; } | null): Promise<Buffer>; (path: string, options: BufferEncoding | { encoding: BufferEncoding; }): Promise<string>; }; writeFile(path: string, data: string | Buffer, options?: BufferEncoding | { encoding?: BufferEncoding; } | null): Promise<void>; readdir: { (path: string, options?: { withFileTypes?: false; }): Promise<string[]>; (path: string, options: { withFileTypes: true; }): Promise<Array<{ name: string; isFile(): boolean; isDirectory(): boolean; }>>; }; mkdir(path: string, options?: { recursive?: boolean; }): Promise<string | undefined>; rename(oldPath: string, newPath: string): Promise<void>; access(path: string, mode?: number): Promise<void>; rm?(path: string, options?: { recursive?: boolean; force?: boolean; }): Promise<void>; unlink?(path: string): Promise<void>; rmdir?(path: string): Promise<void>; symlink?(target: string, path: string, type?: string | null): Promise<void>; cp?(source: string, destination: string, options?: { recursive?: boolean; }): Promise<void>; mkdtemp?(prefix: string): Promise<string>; }; declare class FsFixture { /** * Path to the fixture directory. */ readonly path: string; readonly fs: FsPromises; /** * Create a Fixture instance from a path. Does not create the fixture directory. * * @param fixturePath - The path to the fixture directory * @param fsApi - Optional fs/promises-compatible API. Defaults to real node:fs/promises. */ constructor(fixturePath: string, fsApi?: FsPromises); /** * Get the full path to a subpath in the fixture directory. * * @param subpaths - Path segments to join with the fixture directory * @returns The absolute path to the subpath * * @example * ```ts * fixture.getPath('dir', 'file.txt') * // => '/tmp/fs-fixture-123/dir/file.txt' * ``` */ getPath(...subpaths: string[]): string; /** * Check if the fixture exists. Pass in a subpath to check if it exists. * * @param subpath - Optional subpath to check within the fixture directory * @returns Promise resolving to true if the path exists, false otherwise */ exists(subpath?: string): Promise<boolean>; /** * Delete the fixture directory or a subpath within it. * * @param subpath - Optional subpath to delete within the fixture directory. * Defaults to deleting the entire fixture. * @returns Promise that resolves when deletion is complete */ rm(subpath?: string): Promise<void>; /** * Copy a file or directory into the fixture directory. * * @param sourcePath - The source path to copy from * @param destinationSubpath - Optional destination path within the fixture. * If omitted, uses the basename of sourcePath. * If ends with path separator, appends basename of sourcePath. * @param options - Copy options (e.g., recursive, filter) * @returns Promise that resolves when copy is complete */ cp(sourcePath: string, destinationSubpath?: string, options?: CopyOptions): Promise<void>; /** * Create a new folder in the fixture directory (including parent directories). * * @param folderPath - The folder path to create within the fixture * @returns Promise that resolves when directory is created */ mkdir(folderPath: string): Promise<string | undefined>; /** * Move or rename a file or directory within the fixture. * This is a wrapper around `node:fs/promises.rename`. * * @param sourcePath - The source path relative to the fixture root * @param destinationPath - The destination path relative to the fixture root * @returns Promise that resolves when the move is complete * * @example * ```ts * // Rename a file * await fixture.mv('old-name.txt', 'new-name.txt') * * // Move a file into a directory * await fixture.mv('file.txt', 'src/file.txt') * * // Rename a directory * await fixture.mv('src', 'lib') * ``` */ mv(sourcePath: string, destinationPath: string): Promise<void>; /** * Read a file from the fixture directory. * * @param filePath - The file path within the fixture to read * @param options - Optional encoding or read options. * When encoding is specified, returns a string; otherwise returns a Buffer. * @returns Promise resolving to file contents as string or Buffer */ readFile: typeof fs.readFile; /** * Read the contents of a directory in the fixture. * * @param directoryPath - The directory path within the fixture to read. * Defaults to the fixture root when empty string is passed. * @param options - Optional read directory options. * Use `{ withFileTypes: true }` to get Dirent objects. * @returns Promise resolving to array of file/directory names or Dirent objects */ readdir: typeof fs.readdir; /** * Create or overwrite a file in the fixture directory. * * @param filePath - The file path within the fixture to write * @param data - The content to write (string or Buffer) * @param options - Optional encoding or write options * @returns Promise that resolves when file is written */ writeFile: typeof fs.writeFile; /** * Read and parse a JSON file from the fixture directory. * * @param filePath - The JSON file path within the fixture to read * @returns Promise resolving to the parsed JSON content * * @example * ```ts * const data = await fixture.readJson<{ name: string }>('config.json') * console.log(data.name) // Typed as string * ``` */ readJson<T = unknown>(filePath: string): Promise<T>; /** * Create or overwrite a JSON file in the fixture directory. * * @param filePath - The JSON file path within the fixture to write * @param json - The data to serialize as JSON * @param space - Number of spaces or string to use for indentation. Defaults to 2. * @returns Promise that resolves when file is written * * @example * ```ts * // Default 2-space indentation * await fixture.writeJson('config.json', { key: 'value' }) * * // 4-space indentation * await fixture.writeJson('config.json', { key: 'value' }, 4) * * // Tab indentation * await fixture.writeJson('config.json', { key: 'value' }, '\t') * * // Minified (no formatting) * await fixture.writeJson('config.json', { key: 'value' }, 0) * ``` */ writeJson(filePath: string, json: unknown, space?: string | number): Promise<void>; /** * Resource management cleanup * https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html */ [Symbol.asyncDispose](): Promise<void>; } type FsFixtureType = FsFixture; declare class PathBase { readonly path: string; constructor(path: string); } type SymlinkType = 'file' | 'dir' | 'junction'; declare class Symlink extends PathBase { readonly target: string; readonly type?: SymlinkType; constructor(target: string, type?: SymlinkType, filePath?: string); } type ApiBase = { fixturePath: string; getPath(...subpaths: string[]): string; symlink(targetPath: string, /** * Symlink type for Windows. Defaults to auto-detect by Node. */ type?: SymlinkType): Symlink; }; type Api = ApiBase & { filePath: string; }; type FileTree = { [path: string]: string | Buffer | FileTree | ((api: Api) => string | Buffer | Symlink); }; type FilterFunction = CopyOptions['filter']; type CreateFixtureOptions = { /** * The temporary directory to create the fixtures in. * Defaults to `os.tmpdir()`. * * Accepts either a string path or a URL object. * * Tip: use `new URL('.', import.meta.url)` to the get the file's directory (not the file). */ tempDir?: string | URL; /** * Function to filter files to copy when using a template path. * Return `true` to copy the item, `false` to ignore it. */ templateFilter?: FilterFunction; /** * Custom fs/promises-compatible API for fixture operations. * Use this to create fixtures in a virtual filesystem instead of on disk. * * Required: readFile, writeFile, readdir (with withFileTypes), * mkdir, rename, access. * Optional: rm (or unlink + rmdir as fallback), symlink, cp, mkdtemp. * * @example * ```ts * import { create, MemoryProvider } from '@platformatic/vfs' * const vfs = create(new MemoryProvider()) * const fixture = await createFixture({ 'file.txt': 'hi' }, { fs: vfs.promises }) * ``` */ fs?: FsPromises; }; /** * Create a temporary test fixture directory. * * @param source - Optional source to create the fixture from: * - If omitted, creates an empty fixture directory * - If a string, copies the directory at that path to the fixture * - If a FileTree object, creates files and directories from the object structure * @param options - Optional configuration for fixture creation * @returns Promise resolving to an FsFixture instance * * @example * ```ts * // Create empty fixture * const fixture = await createFixture() * * // Create from object * const fixture = await createFixture({ * 'file.txt': 'content', * 'dir/nested.txt': 'nested content', * 'binary.bin': Buffer.from('binary'), * }) * * // Create from template directory * const fixture = await createFixture('./my-template') * * // Cleanup * await fixture.rm() * ``` */ declare const createFixture: (source?: string | FileTree, options?: CreateFixtureOptions) => Promise<FsFixture>; export { createFixture }; export type { CreateFixtureOptions, FileTree, FsFixtureType as FsFixture, FsPromises };