# mem-fs-editor

> File edition helpers working on top of mem-fs

Latest version **12.0.9** (published 2026-09-05) · MIT license · 0 weekly downloads

## Install

```sh
npm install mem-fs-editor
pnpm add mem-fs-editor
yarn add mem-fs-editor
bun add mem-fs-editor
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 12.0.9 |
| Published | 2026-09-05 |
| First published | 2014-11-06 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | 20 \|\| >=22 |
| Dependencies | 14 |
| Unpacked size | 57.1 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 128 |
| Author | Simon Boudrias |
| Maintainers | sboudrias, mshima |

## Links

- npm: https://www.npmjs.com/package/mem-fs-editor
- Repository: https://github.com/SBoudrias/mem-fs
- Homepage: https://github.com/SBoudrias/mem-fs#readme
- Issues: https://github.com/SBoudrias/mem-fs/issues
- npm.io page: https://npm.io/package/mem-fs-editor

## Dependencies (14)

- [ejs](https://npm.io/package/ejs.md) ^6.0.1
- [debug](https://npm.io/package/debug.md) ^4.4.3
- [vinyl](https://npm.io/package/vinyl.md) ^3.0.1
- [commondir](https://npm.io/package/commondir.md) ^1.0.0
- [minimatch](https://npm.io/package/minimatch.md) ^10.2.5
- [@types/ejs](https://npm.io/package/@types/ejs.md) ^3.1.5
- [multimatch](https://npm.io/package/multimatch.md) ^8.0.0
- [tinyglobby](https://npm.io/package/tinyglobby.md) ^0.2.15
- [deep-extend](https://npm.io/package/deep-extend.md) ^0.6.0
- [isbinaryfile](https://npm.io/package/isbinaryfile.md) 5.0.7
- [normalize-path](https://npm.io/package/normalize-path.md) ^3.0.0
- [textextensions](https://npm.io/package/textextensions.md) ^6.11.0
- [@types/picomatch](https://npm.io/package/@types/picomatch.md) ^4.0.2
- [binaryextensions](https://npm.io/package/binaryextensions.md) ^6.11.0

## Recent versions

- 12.0.9 (latest) — 2026-09-05
- 12.0.8 — 2026-08-24
- 12.0.7 — 2026-08-16
- 12.0.6 — 2026-07-14
- 12.0.5 — 2026-07-12
- 12.0.4 — 2026-04-18
- 12.0.3 — 2026-03-04
- 12.0.2 — 2026-02-28
- 12.0.1 — 2026-02-28
- 12.0.0 — 2026-02-28
- 12.0.0-0 — 2026-02-27
- 11.1.4 — 2024-12-12
- 11.1.3 — 2024-10-15
- 11.1.2 — 2024-10-12
- 11.1.1 — 2024-08-18
- … 51 more at https://npm.io/package/mem-fs-editor/versions

## README

# mem-fs-editor

[![NPM version](https://badge.fury.io/js/mem-fs-editor.svg)](http://badge.fury.io/js/mem-fs-editor)
[![Coverage Status](https://codecov.io/gh/SBoudrias/mem-fs-editor/branch/master/graph/badge.svg)](https://codecov.io/gh/SBoudrias/mem-fs-editor)

File edition helpers working on top of [mem-fs](https://github.com/SBoudrias/mem-fs)

## Usage

```js
import { create as createMemFs } from 'mem-fs';
import { create as createEditor } from 'mem-fs-editor';

const store = createMemFs();
const fs = createEditor(store);

fs.write('somefile.js', 'var a = 1;');
await fs.commit();
```

### `#read(filepath, [options])`

Read a file and return its contents as a string.

You can alternatively get the raw contents buffer if you pass `options.raw = true`.

By default, calling `read()` on a file path that does not exist throws error. You can, however, pass `options.defaults = 'your default content'` to get a default content you pass in, if you prefer to not deal with try/catch.

### `#readJSON(filepath, [defaults])`

Read a file and parse its contents as JSON.

`readJSON()` internally calls `read()` but will not throw an error if the file path you pass in does not exist. If you pass in an optional `defaults`, the `defaults` content will be returned in case of the target file is missing, instead of `undefined`. (Error would still be thrown if `JSON.parse` failed to parse your target file.)

### `#write(filepath, contents[, options])`

Replace the content of a file (existing or new) with a string or a buffer.

Optionally pass an `options` object:

- `options.stat`: A `fs.Stats` object to attach to the file.
- `options.metadata`: An arbitrary object attached to the file as `editorMetadata`, available during `store.each()` and commit transforms. See [File metadata](#file-metadata).

> **Deprecated:** Passing a bare `fs.Stats` as the third argument (`write(filepath, contents, stat)`) is deprecated. Use the `WriteOptions` object form instead: `write(filepath, contents, { stat })`.

### `#writeJSON(filepath, contents[, replacer [, space]])`

Replace the content of a file (existing or new) with an object that is to be converted by calling `JSON.stringify()`.

`contents` should usually be a JSON object, but it can technically be anything that is acceptable by [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).

Optionally pass `replacer` and `space` as the last two arguments, as defined by [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). `spacer` is used to format the output string (prettify).

Default value for `space` is `2`, when not specified.

### `#append(filepath, contents, [options])`

Append the new contents to the current file contents.

- `options.trimEnd` (default `true`). Trim trailing whitespace of the current file contents.
- `options.separator` (default `os.EOL`). Separator to insert between current and new contents.
- `options.create` (default `false`). Create the file if doesn't exists.
- `options.metadata`: An arbitrary object attached to the file as `editorMetadata`. See [File metadata](#file-metadata).

### `#appendTpl(filepath, contents, data[, options])`

Append the new `contents` to the existing `filepath` content and parse the new contents as an [ejs](http://ejs.co/) template where `data` is the template context (the variable names available inside the template).

- `options.trimEnd` (default `true`). Trim trailing whitespace of the current file contents.
- `options.separator` (default `os.EOL`). Separator to insert between current and new contents.
- `options.transformOptions`. Options passed to the EJS renderer when processing the template, similar to `copyTpl`.

### `#extendJSON(filepath, contents[, replacer [, space]])`

Extend the content of an existing JSON file with the partial objects provided as argument.

Optionally take the same JSON formatting arguments than `#writeJSON()`.

### `#delete(filepath, [options])`

Delete a file or a directory.

`filePath` can also be a `glob`. If `filePath` is glob, you can optionally pass in an `options.globOptions` object to change its pattern matching behavior. The full list of options are being described [here](https://github.com/mrmlnc/fast-glob#options-1). The `sync` flag is forced to be `true` in `globOptions`.

### `#copy(from, to, [options])`

Copy file(s) from the `from` path to the `to` path.
When passing array, you should pass `options.fromBasePath` to be used to calculate the `to` relative path. The common directory will be detected and used as `fromBasePath` otherwise.

Optionally, pass an `options.fileTransform` function that transforms both the destination path and file contents. The function receives a single object parameter with the following properties:

- `destinationPath`: The resolved destination file path (string)
- `sourcePath`: The source file path (string)
- `contents`: The file contents as a `Buffer`
- `data`: Optional data passed via `options.transformData`
- `options`: Optional options passed via `options.transformOptions`

The function should return an object `{ path, contents }` where:

- `path`: The transformed destination path (string)
- `contents`: The transformed file contents (string | Buffer)

Example:

```js
fs.copy('source.txt', 'dest.txt', {
  fileTransform({ destinationPath, sourcePath, contents, data, options }) {
    const newPath = destinationPath.replace('.txt', '.md');
    const newContents = contents.toString().toUpperCase();
    return { path: newPath, contents: newContents };
  },
});
```

`options.ignoreNoMatch` can be used to silence the error thrown if no files match the `from` pattern.
`options.append` can be used to append `from` contents to `to` instead of copying, when the file is already loaded in mem-fs (safe for regeneration).
`options.metadata` can be used to attach an arbitrary object to the copied file as `editorMetadata`. When omitted, the source file's `editorMetadata` (if any) is carried forward to the copy. See [File metadata](#file-metadata).

`from` can be a glob pattern that'll be match against the file system. If that's the case, then `to` must be an output directory. For a globified `from`, you can optionally pass in an `options.globOptions` object to change its pattern matching behavior. The full list of options are being described [here](https://github.com/mrmlnc/fast-glob#options-1). The `nodir` flag is forced to be `true` in `globOptions` to ensure a vinyl object representing each matching directory is marked as `deleted` in the `mem-fs` store.

`options.noGlob` can be used to by bypass glob matching entirely. In that case, `from` will directly match file paths against the file system.

### `#copyAsync(from, to, [options])`

Async version of `copy`.

`copy` loads `from` to memory and copy its contents to `to`.
`copyAsync` copies directly from the disk to `to`, falling back to `copy` behavior if the file doesn't exists on disk.

Same parameters of `copy`. The `fileTransform` function can also return a `Promise<{ path: string; contents: string | Buffer }>` for async transformations.

See [copy() documentation for more details](#copyfrom-to-options).

### `#copyTpl(from, to, data[, options])`

Copy the `from` file and, if it is not a binary file, parse its content as an [ejs](http://ejs.co/) template where `data` is the template context (the variable names available inside the template).

`options.transformOptions` replaced the old `tplOptions` parameter and is passed as ejs options. `mem-fs-editor` automatically setup the filename option so you can easily use partials.

You can also optionally pass a `options` object (see [copy() documentation for more details](#copyfrom-to-options)).

Templates syntax looks like this:

```
<%= value %>
<%- include('partial.ejs', { name: 'Simon' }) %>
```

Dir syntax looks like this:

```
/some/path/dir<%= value %>/...
```

Refer to the [ejs documentation](http://ejs.co/) for more details.

### `#copyTplAsync(from, to, data[, options])`

Async version of `copyTpl` that uses `copyAsync` instead of `copy`.

Can be used for best performance. Reduces overheads.

Same parameters of `copyTpl` (see [copyTpl() documentation for more details](#copytplfrom-to-data-options)).

### `#move(from, to, [options])`

Move/rename a file from the `from` path to the `to` path.

`#move` internally uses `#copy` and `#delete`, so `from` can be a glob pattern, and you can provide `options.globOptions` with it. `options.metadata` is passed through to the copy (see [File metadata](#file-metadata)).

### `#exists(filepath)`

Returns `true` if a file exists. Returns `false` if the file is not found or deleted.

### `#commit([options,] [...transforms])`

Pass stored files through a pipeline and persist every changes made to files in the mem-fs store to disk.

If provided, `options` is the pipeline options.
By default only modified files are passed through the pipeline.
Pass a custom filter `options.filter` to customize files passed through the pipeline.
If provided, `...transforms` is a vararg of TransformStream to be applied on a stream of vinyl files (like gulp plugins).
`commitTransform` is appended to `transforms` and persists modified files to disk, non modified files are passed through.

Each file passed through the pipeline carries its `editorMetadata` (if any), so transforms can read it to drive content processing. See [File metadata](#file-metadata).

returns promise that is resolved once the pipeline is finished.

## File metadata

Editor actions that create or copy files (`write`, `writeJSON`, `extendJSON`, `append`, `appendTpl`, `copy`, `copyAsync`, `copyTpl`, `copyTplAsync`, `move`) accept an optional `metadata` option. The object is attached to the underlying mem-fs Vinyl file as `editorMetadata` and remains available during `store.each()`, `store.all()`, and commit transforms.

```js
import { Duplex } from 'node:stream';

fs.write('src/app.js', sourceContent, {
  metadata: {
    cleanupMarks: true,
  },
});

await fs.commit(
  Duplex.from(async function* (generator) {
    for await (const file of generator) {
      if (file.contents && file.editorMetadata?.cleanupMarks) {
        file.contents = cleanupMarks(file.contents);
      }

      yield file;
    }
  }),
);
```

When copying, an explicit `metadata` option overrides the source file's metadata; when omitted, the source file's `editorMetadata` (if any) is carried forward to the copy.

When metadata is not provided, behavior is unchanged and no `editorMetadata` is set.

### `#dump([cwd,] [filter])`

Dump files to compare expected result.
Provide a `cwd` for relative path. Allows to omit temporary path.
Provide a `filter` function or a pattern to focus on specific files.
`dump` returns only modified (committed or not) files when no filter or a pattern is provided.

---
_Source: https://npm.io/package/mem-fs-editor · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
