npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

fs-fixture

v2.16.0

Published

Easily create test fixtures at a temporary file-system path

Readme

Simple API to create disposable test fixtures on disk. Tiny (1.1 kB gzipped) with zero dependencies!

Features

  • 📁 Create files & directories from simple objects
  • 🧹 Automatic cleanup with using keyword
  • 📝 Built-in JSON read/write support
  • 🔗 Symlink support
  • 💾 Binary file support with Buffers
  • 🎯 TypeScript-first with full type safety
  • 🔄 File methods inherit types directly from Node.js fs module
  • 🔌 Pluggable filesystem — use with @platformatic/vfs, memfs, or any fs/promises-compatible API

Installation

npm install fs-fixture

Quick start

import { createFixture } from 'fs-fixture'

// Create a temporary fixture
const fixture = await createFixture({
    'package.json': JSON.stringify({ name: 'my-app' }),
    'src/index.js': 'console.log("Hello world")'
})

// Read files
const content = await fixture.readFile('src/index.js', 'utf8')

// Cleanup when done
await fixture.rm()

Auto cleanup with using keyword

Uses TypeScript 5.2+ Explicit Resource Management for automatic cleanup:

await using fixture = await createFixture({
    'config.json': '{ "setting": true }'
})

// Fixture is automatically cleaned up when exiting scope

Usage

Creating fixtures

From an object:

const fixture = await createFixture({
    'package.json': '{ "name": "test" }',
    'src/index.js': 'export default () => {}',
    'src/utils': {
        'helper.js': 'export const help = () => {}'
    }
})

From a template directory:

// Copies an existing directory structure
const fixture = await createFixture('./test-templates/basic')

Empty fixture:

// Create an empty temporary directory
const fixture = await createFixture()

From an initializer:

const fixture = await createFixture(async ({ path, writeJson }) => {
    await writeJson('package.json', {
        name: 'test-package'
    })

    // Test-specific setup that needs the fixture path.
    await initializeProject(path)

    return {
        'src/index.js': 'export default 42'
    }
})

Prefer a FileTree when a fixture only needs files and directories. Use an initializer function when setup is complex, imperative, or ordered, such as initializing a Git repository or running a project setup helper. It keeps that setup with the fixture it configures.

The initializer receives the new fixture before it is returned. It can perform setup directly and optionally return a FileTree to create after setup completes. Returned files overwrite regular files created during setup, but the tree does not replace the fixture directory. If setup fails, fs-fixture removes the fixture before it rethrows the error.

Working with files

readFile and writeFile inherit their type signatures directly from Node.js fs/promises. readdir preserves Node.js overloads and also lists the fixture root when called without a path.

Read files:

// Read as string (type: Promise<string>)
const text = await fixture.readFile('config.txt', 'utf8')

// Read as buffer (type: Promise<Buffer>)
const binary = await fixture.readFile('image.png')

Write files:

await fixture.writeFile('output.txt', 'Hello world')
await fixture.writeFile('data.bin', Buffer.from([0x89, 0x50]))

JSON operations:

// Write JSON with formatting
await fixture.writeJson('config.json', { port: 3000 })

// Read and parse JSON with type safety
type Config = { port: number }
const config = await fixture.readJson<Config>('config.json')

Working with directories

// Create directories
await fixture.mkdir('nested/folders')

// List fixture root contents
const rootFiles = await fixture.readdir()

// List directory contents
const files = await fixture.readdir('src')

// List root entries with options
const rootEntries = await fixture.readdir('', { withFileTypes: true })

// Copy files into fixture
await fixture.cp('/path/to/file.txt', 'copied-file.txt')

// Move or rename files
await fixture.mv('old-name.txt', 'new-name.txt')
await fixture.mv('file.txt', 'src/file.txt')

// Check if path exists
if (await fixture.exists('optional-file.txt')) {
    // ...
}

Call readdir() to list the fixture root. Pass '' as the path when listing the root with options.

Advanced features

Dynamic content with functions:

const fixture = await createFixture({
    'target.txt': 'original file',
    'info.txt': ({ fixturePath }) => `Created at: ${fixturePath}`,
    'link.txt': ({ symlink }) => symlink('./target.txt')
})

Use a FileTree entry function for isolated dynamic file content. Use an initializer function when setup requires multiple imperative or ordered operations.

Symlinks:

const fixture = await createFixture({
    'index.js': 'import pkg from \'pkg\'',

    // Symlink individual file or directory
    'node_modules/pkg': ({ symlink }) => symlink(process.cwd()),

    // Symlink entire directory (useful for sharing node_modules)
    node_modules: ({ symlink }) => symlink(path.resolve('node_modules'))
})

Binary files:

const fixture = await createFixture({
    'image.png': Buffer.from(imageData),
    'generated.bin': () => Buffer.from('dynamic binary content')
})

Path syntax:

const fixture = await createFixture({
    // Nested object syntax
    src: {
        utils: {
            'helper.js': 'export const help = () => {}'
        }
    },

    // Or path syntax (creates same structure)
    'src/utils/helper.js': 'export const help = () => {}'
})

[!TIP] Path syntax also works for grouped prefixes, so you can keep related files together without repeating the shared path:

await createFixture({
    'file.js': 'import { a } from "my-pkg";',

    'node_modules/my-pkg': {
        'package.json': JSON.stringify({
            name: 'my-pkg',
            type: 'module',
            exports: './index.js'
        }),
        'index.js': 'export const a = 1;'
    }
})

Custom filesystem

Pass any fs/promises-compatible API via the fs option to use a virtual filesystem instead of disk:

import { create, MemoryProvider } from '@platformatic/vfs'
import { createFixture } from 'fs-fixture'

const fs = create(new MemoryProvider()).promises
const fixture = await createFixture({
    'package.json': JSON.stringify({ name: 'test' }),
    'src/index.js': 'export default 42'
}, { fs })

await fixture.readFile('src/index.js', 'utf8') // 'export default 42'

Works with any library that implements the fs/promises API shape, including @platformatic/vfs, the future node:vfs, and memfs.

[!NOTE] With a custom fs, files only exist in that fs instance. Use fixture.readFile() or fixture.fs to access them — fixture.path is a virtual path that doesn't exist on the real disk.

[!NOTE] Template directory sources (string paths) are not supported with custom filesystems because most virtual fs implementations lack recursive cp. Use a FileTree object instead.

API

createFixture(source?, options?)

Creates a temporary fixture directory and returns a FsFixture instance.

Parameters:

  • source (optional): String path to template directory, FileTree object defining the structure, or initializer function
  • options.tempDir (optional): Custom temp directory. Defaults to os.tmpdir()
  • options.templateFilter (optional): Filter function when copying from template directory
  • options.fs (optional): Custom fs/promises-compatible API for virtual filesystem support

Returns: Promise<FsFixture>

const fixture = await createFixture()
const fixture = await createFixture({ 'file.txt': 'content' })
const fixture = await createFixture('./template-dir')
const fixture = await createFixture(fixture => ({ 'path.txt': fixture.path }))
const fixture = await createFixture({}, { tempDir: './custom-temp' })

FsFixture Methods

| Method | Description | |--------|-------------| | fixture.path | Absolute path to the fixture directory | | fixture.fs | The underlying fs/promises API used by the fixture | | getPath(...paths) | Get absolute path to file/directory in fixture | | exists(path?) | Check if file/directory exists | | rm(path?) | Delete file/directory (or entire fixture if no path) | | readFile(path, encoding?) | Read file as string or Buffer | | writeFile(path, content) | Write string or Buffer to file | | readJson<T>(path) | Read and parse JSON file | | writeJson(path, data, space?) | Write JSON with optional formatting | | readdir(), readdir(path, options?) | List fixture root or directory contents. Pass '' for root options. | | mkdir(path) | Create directory (recursive) | | cp(source, dest?) | Copy file/directory into fixture | | mv(source, dest) | Move or rename file/directory |

Types

type FileTree = {
    [path: string]: string | Buffer | FileTree | ((api: Api) => string | Buffer | Symlink)
}

type Api = {
    fixturePath: string // Fixture root path
    filePath: string // Current file path
    getPath: (...paths: string[]) => string // Get path from fixture root
    symlink: (target: string) => Symlink // Create a symlink
}

FsPromises is the exported contract for custom filesystem implementations:

import type { FsPromises } from 'fs-fixture'

| Capability | Methods | Requirement | |------------|---------|-------------| | Core fixture operations | readFile, writeFile, readdir, mkdir, rename, access | Required | | Removal | rm, or unlink and rmdir | One removal strategy is required when calling fixture.rm() | | Symlinks | symlink | Required only when a FileTree contains a symlink | | Copying | cp | Required only when calling fixture.cp() | | Temporary directories | mkdtemp | Optional. fs-fixture generates fixture paths with a counter when omitted. |

The exported type defines the exact overloads and option shapes. fixture.readdir() is a fixture convenience method. A custom fs readdir always receives the fixture's absolute path.

Related

manten

Lightweight testing library for Node.js