mem-fs-editor 9.7.0 → 10.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +2 -2
  2. package/dist/actions/append-tpl.d.ts +5 -0
  3. package/dist/actions/append-tpl.js +4 -0
  4. package/dist/actions/append.d.ts +8 -0
  5. package/dist/actions/append.js +20 -0
  6. package/dist/actions/commit-file-async.d.ts +2 -0
  7. package/dist/actions/commit-file-async.js +50 -0
  8. package/dist/actions/commit.d.ts +6 -0
  9. package/dist/actions/commit.js +15 -0
  10. package/dist/actions/copy-async.d.ts +17 -0
  11. package/dist/actions/copy-async.js +88 -0
  12. package/dist/actions/copy-tpl-async.d.ts +4 -0
  13. package/dist/actions/copy-tpl-async.js +23 -0
  14. package/dist/actions/copy-tpl.d.ts +12 -0
  15. package/dist/actions/copy-tpl.js +21 -0
  16. package/dist/actions/copy.d.ts +17 -0
  17. package/dist/actions/copy.js +75 -0
  18. package/dist/actions/delete.d.ts +5 -0
  19. package/dist/actions/delete.js +29 -0
  20. package/dist/actions/dump.d.ts +7 -0
  21. package/dist/actions/dump.js +26 -0
  22. package/dist/actions/exists.d.ts +2 -0
  23. package/dist/actions/exists.js +4 -0
  24. package/dist/actions/extend-json.d.ts +2 -0
  25. package/dist/actions/extend-json.js +6 -0
  26. package/dist/actions/move.d.ts +3 -0
  27. package/dist/actions/move.js +4 -0
  28. package/dist/actions/read-json.d.ts +2 -0
  29. package/dist/actions/read-json.js +17 -0
  30. package/dist/actions/read.d.ts +11 -0
  31. package/dist/actions/read.js +12 -0
  32. package/dist/actions/write-json.d.ts +2 -0
  33. package/dist/actions/write-json.js +11 -0
  34. package/dist/actions/write.d.ts +5 -0
  35. package/dist/actions/write.js +17 -0
  36. package/dist/index.d.ts +64 -0
  37. package/dist/index.js +46 -0
  38. package/dist/state.d.ts +22 -0
  39. package/dist/state.js +51 -0
  40. package/dist/transform.d.ts +7 -0
  41. package/dist/transform.js +20 -0
  42. package/dist/util.d.ts +6 -0
  43. package/dist/util.js +66 -0
  44. package/package.json +34 -25
  45. package/lib/actions/append-tpl.js +0 -10
  46. package/lib/actions/append.js +0 -23
  47. package/lib/actions/commit-file-async.js +0 -62
  48. package/lib/actions/commit.js +0 -34
  49. package/lib/actions/copy-async.js +0 -134
  50. package/lib/actions/copy-tpl-async.js +0 -35
  51. package/lib/actions/copy-tpl.js +0 -34
  52. package/lib/actions/copy.js +0 -104
  53. package/lib/actions/delete.js +0 -36
  54. package/lib/actions/dump.js +0 -37
  55. package/lib/actions/exists.js +0 -7
  56. package/lib/actions/extend-json.js +0 -10
  57. package/lib/actions/move.js +0 -6
  58. package/lib/actions/read-json.js +0 -15
  59. package/lib/actions/read.js +0 -16
  60. package/lib/actions/write-json.js +0 -10
  61. package/lib/actions/write.js +0 -27
  62. package/lib/index.js +0 -33
  63. package/lib/state.js +0 -95
  64. package/lib/transform.js +0 -18
  65. package/lib/util.js +0 -102
package/README.md CHANGED
@@ -9,10 +9,10 @@ File edition helpers working on top of [mem-fs](https://github.com/SBoudrias/mem
9
9
  ## Usage
10
10
 
11
11
  ```js
12
- import memFs from 'mem-fs';
12
+ import { create as createMemFs } from 'mem-fs';
13
13
  import editor from 'mem-fs-editor';
14
14
 
15
- const store = memFs.create();
15
+ const store = createMemFs();
16
16
  const fs = editor.create(store);
17
17
 
18
18
  fs.write('somefile.js', 'var a = 1;');
@@ -0,0 +1,5 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { Data, Options } from 'ejs';
3
+ import type { MemFsEditor } from '../index.js';
4
+ import { AppendOptions } from './append.js';
5
+ export default function appendTpl(this: MemFsEditor, to: string, contents: string | Buffer, context?: Data, tplSettings?: Options, options?: AppendOptions): void;
@@ -0,0 +1,4 @@
1
+ import { render } from '../util.js';
2
+ export default function appendTpl(to, contents, context, tplSettings, options) {
3
+ this.append(to, render(contents.toString(), context, tplSettings), options);
4
+ }
@@ -0,0 +1,8 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import type { MemFsEditor } from '../index.js';
3
+ export type AppendOptions = {
4
+ create?: boolean;
5
+ trimEnd?: boolean;
6
+ separator?: string;
7
+ };
8
+ export default function append(this: MemFsEditor, to: string, contents: string | Buffer, options?: AppendOptions): void;
@@ -0,0 +1,20 @@
1
+ import { EOL } from 'os';
2
+ export default function append(to, contents, options) {
3
+ options = {
4
+ trimEnd: true,
5
+ separator: EOL,
6
+ ...options,
7
+ };
8
+ if (!this.exists(to) && options.create) {
9
+ this.write(to, contents);
10
+ return;
11
+ }
12
+ let currentContents = this.read(to);
13
+ if (!currentContents) {
14
+ throw new Error(`Error appending to ${to}, file is empty.`);
15
+ }
16
+ if (currentContents && options.trimEnd) {
17
+ currentContents = currentContents.replace(/\s+$/, '');
18
+ }
19
+ this.write(to, currentContents + options.separator + contents.toString());
20
+ }
@@ -0,0 +1,2 @@
1
+ import type { MemFsEditor, MemFsEditorFile } from '../index.js';
2
+ export default function commitFileAsync<EditorFile extends MemFsEditorFile>(this: MemFsEditor<EditorFile>, file: EditorFile): Promise<void>;
@@ -0,0 +1,50 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { clearFileState, isFileStateModified, isFileStateDeleted, setCommittedFile } from '../state.js';
4
+ async function write(file) {
5
+ if (!file.contents) {
6
+ throw new Error(`${file.path} cannot write an empty file`);
7
+ }
8
+ const dir = path.dirname(file.path);
9
+ try {
10
+ if (!(await fs.stat(dir)).isDirectory()) {
11
+ throw new Error(`${file.path} is not a directory`);
12
+ }
13
+ }
14
+ catch (error) {
15
+ if (error.code === 'ENOENT') {
16
+ await fs.mkdir(dir, { recursive: true });
17
+ }
18
+ else {
19
+ throw error;
20
+ }
21
+ }
22
+ const newMode = file.stat?.mode;
23
+ await fs.writeFile(file.path, file.contents, { mode: newMode });
24
+ if (newMode !== undefined) {
25
+ const { mode: existingMode } = await fs.stat(file.path);
26
+ // eslint-disable-next-line no-bitwise
27
+ if ((existingMode & 0o777) !== (newMode & 0o777)) {
28
+ await fs.chmod(file.path, newMode);
29
+ }
30
+ }
31
+ }
32
+ async function remove(file) {
33
+ const remove = fs.rm || fs.rmdir;
34
+ await remove(file.path, { recursive: true });
35
+ }
36
+ export default async function commitFileAsync(file) {
37
+ const existingFile = this.store.get(file.path);
38
+ if (!existingFile || existingFile !== file) {
39
+ this.store.add(file);
40
+ }
41
+ if (isFileStateModified(file)) {
42
+ setCommittedFile(file);
43
+ await write(file);
44
+ }
45
+ else if (isFileStateDeleted(file)) {
46
+ setCommittedFile(file);
47
+ await remove(file);
48
+ }
49
+ clearFileState(file);
50
+ }
@@ -0,0 +1,6 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { PipelineSource } from 'stream';
3
+ import type { MemFsEditor, MemFsEditorFile } from '../index.js';
4
+ declare function commit<EditorFile extends MemFsEditorFile>(this: MemFsEditor<EditorFile>, stream?: PipelineSource<any>): any;
5
+ declare function commit<EditorFile extends MemFsEditorFile>(this: MemFsEditor<EditorFile>, filters?: any[], stream?: PipelineSource<any>): any;
6
+ export default commit;
@@ -0,0 +1,15 @@
1
+ import { promisify } from 'util';
2
+ import { pipeline as _pipeline } from 'stream';
3
+ const pipeline = promisify(_pipeline);
4
+ import { createPendingFilesPassthrough, createCommitTransform } from '../transform.js';
5
+ import { isFilePending } from '../state.js';
6
+ function commit(filters, stream) {
7
+ if (!Array.isArray(filters)) {
8
+ stream = filters;
9
+ filters = [];
10
+ }
11
+ stream = stream ?? this.store.stream({ filter: (file) => isFilePending(file) });
12
+ filters = filters ?? [];
13
+ return pipeline(stream, createPendingFilesPassthrough(), ...filters, createCommitTransform(this));
14
+ }
15
+ export default commit;
@@ -0,0 +1,17 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { type Options as GlobbyOptions } from 'globby';
3
+ import type { MemFsEditor } from '../index.js';
4
+ import { AppendOptions } from './append.js';
5
+ import { Data, Options } from 'ejs';
6
+ import { CopySingleOptions } from './copy.js';
7
+ export type CopyAsyncOptions = CopySingleAsyncOptions & {
8
+ globOptions?: GlobbyOptions;
9
+ processDestinationPath?: (string: any) => string;
10
+ ignoreNoMatch?: boolean;
11
+ };
12
+ export declare function copyAsync(this: MemFsEditor, from: string | string[], to: string, options?: CopyAsyncOptions, context?: Data, tplSettings?: Options): Promise<void>;
13
+ export type CopySingleAsyncOptions = AppendOptions & CopySingleOptions & {
14
+ append?: boolean;
15
+ processFile?: (this: MemFsEditor, filepath: string) => string | Promise<string | Buffer>;
16
+ };
17
+ export declare function _copySingleAsync(this: MemFsEditor, from: string, to: string, options?: CopySingleAsyncOptions): Promise<void>;
@@ -0,0 +1,88 @@
1
+ import assert from 'assert';
2
+ import fs from 'fs';
3
+ import fsPromises from 'fs/promises';
4
+ import path from 'path';
5
+ import { globbySync, isDynamicPattern } from 'globby';
6
+ import multimatch from 'multimatch';
7
+ import { render, globify, getCommonPath } from '../util.js';
8
+ import normalize from 'normalize-path';
9
+ async function applyProcessingFileFunc(processFile, filename) {
10
+ const output = await Promise.resolve(processFile.call(this, filename));
11
+ return Buffer.isBuffer(output) ? output : Buffer.from(output);
12
+ }
13
+ function renderFilepath(filepath, context, tplSettings) {
14
+ if (!context) {
15
+ return filepath;
16
+ }
17
+ return render(filepath, context, tplSettings);
18
+ }
19
+ async function getOneFile(from) {
20
+ let oneFile;
21
+ if (typeof from === 'string') {
22
+ oneFile = from;
23
+ }
24
+ else {
25
+ return undefined;
26
+ }
27
+ const resolved = path.resolve(oneFile);
28
+ try {
29
+ if ((await fsPromises.stat(resolved)).isFile()) {
30
+ return resolved;
31
+ }
32
+ }
33
+ catch (_) { }
34
+ return undefined;
35
+ }
36
+ export async function copyAsync(from, to, options, context, tplSettings) {
37
+ to = path.resolve(to);
38
+ options = options || {};
39
+ const oneFile = await getOneFile(from);
40
+ if (oneFile) {
41
+ return this._copySingleAsync(oneFile, renderFilepath(to, context, tplSettings), options);
42
+ }
43
+ const fromGlob = globify(from);
44
+ const globOptions = { ...options.globOptions, nodir: true };
45
+ const diskFiles = globbySync(fromGlob, globOptions).map((filepath) => path.resolve(filepath));
46
+ const storeFiles = [];
47
+ this.store.each((file) => {
48
+ // The store may have a glob path and when we try to copy it will fail because not real file
49
+ if (!isDynamicPattern(normalize(file.path)) &&
50
+ multimatch([file.path], fromGlob).length !== 0 &&
51
+ !diskFiles.includes(file.path)) {
52
+ storeFiles.push(file.path);
53
+ }
54
+ });
55
+ let generateDestination = () => to;
56
+ if (Array.isArray(from) || !this.exists(from) || isDynamicPattern(normalize(from))) {
57
+ assert(!this.exists(to) || fs.statSync(to).isDirectory(), 'When copying multiple files, provide a directory as destination');
58
+ const processDestinationPath = options.processDestinationPath || ((path) => path);
59
+ const root = getCommonPath(from);
60
+ generateDestination = (filepath) => {
61
+ const toFile = path.relative(root, filepath);
62
+ return processDestinationPath(path.join(to, toFile));
63
+ };
64
+ }
65
+ // Sanity checks: Makes sure we copy at least one file.
66
+ assert(options.ignoreNoMatch || diskFiles.length > 0 || storeFiles.length > 0, 'Trying to copy from a source that does not exist: ' + from);
67
+ await Promise.all([
68
+ ...diskFiles.map((file) => this._copySingleAsync(file, renderFilepath(generateDestination(file), context, tplSettings), options)),
69
+ ...storeFiles.map((file) => Promise.resolve(this._copySingle(file, renderFilepath(generateDestination(file), context, tplSettings), options))),
70
+ ]);
71
+ }
72
+ export async function _copySingleAsync(from, to, options = {}) {
73
+ if (!options.processFile) {
74
+ return this._copySingle(from, to, options);
75
+ }
76
+ const contents = await applyProcessingFileFunc.call(this, options.processFile, from);
77
+ if (options.append) {
78
+ if (!this.store.existsInMemory) {
79
+ throw new Error('Current mem-fs is not compatible with append');
80
+ }
81
+ if (this.store.existsInMemory(to)) {
82
+ this.append(to, contents, { create: true, ...options });
83
+ return;
84
+ }
85
+ }
86
+ const stat = await fsPromises.stat(from);
87
+ this.write(to, contents, stat);
88
+ }
@@ -0,0 +1,4 @@
1
+ import type { MemFsEditor } from '../index.js';
2
+ import { Data, Options } from 'ejs';
3
+ import { CopyAsyncOptions } from './copy-async.js';
4
+ export default function (this: MemFsEditor, from: string | string[], to: string, context?: Data, tplSettings?: Options, options?: CopyAsyncOptions): Promise<void>;
@@ -0,0 +1,23 @@
1
+ import fs from 'fs/promises';
2
+ import { isBinary, renderFile } from '../util.js';
3
+ export default async function (from, to, context, tplSettings, options) {
4
+ context = context || {};
5
+ tplSettings = tplSettings || {};
6
+ await this.copyAsync(from, to, {
7
+ processDestinationPath: (path) => path.replace(/.ejs$/, ''),
8
+ ...options,
9
+ async processFile(filename) {
10
+ if (isBinary(filename, null)) {
11
+ return fs.readFile(filename);
12
+ }
13
+ return renderFile(filename, context, tplSettings);
14
+ },
15
+ process: (contents, filename, destination) => this._processTpl({
16
+ contents,
17
+ filename,
18
+ destination,
19
+ context,
20
+ tplSettings,
21
+ }),
22
+ }, context, tplSettings);
23
+ }
@@ -0,0 +1,12 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { Data, Options } from 'ejs';
3
+ import type { MemFsEditor } from '../index.js';
4
+ import { CopySingleOptions } from './copy.js';
5
+ export declare function _processTpl(this: MemFsEditor, { contents, filename, context, tplSettings, }: {
6
+ contents: string | Buffer;
7
+ filename: string;
8
+ destination?: string;
9
+ context?: Data;
10
+ tplSettings?: Options;
11
+ }): string | Buffer;
12
+ export declare function copyTpl(this: MemFsEditor, from: string | string[], to: string, context?: Data, tplSettings?: Options, options?: CopySingleOptions): void;
@@ -0,0 +1,21 @@
1
+ import { isBinary, render } from '../util.js';
2
+ export function _processTpl({ contents, filename, context, tplSettings, }) {
3
+ if (isBinary(filename, contents)) {
4
+ return contents;
5
+ }
6
+ return render(contents.toString(), context, {
7
+ // Setting filename by default allow including partials.
8
+ filename,
9
+ cache: true,
10
+ ...tplSettings,
11
+ });
12
+ }
13
+ export function copyTpl(from, to, context, tplSettings, options) {
14
+ context = context || {};
15
+ tplSettings = tplSettings || {};
16
+ this.copy(from, to, {
17
+ processDestinationPath: (path) => path.replace(/.ejs$/, ''),
18
+ ...options,
19
+ process: (contents, filename) => this._processTpl({ contents, filename, context, tplSettings }),
20
+ }, context, tplSettings);
21
+ }
@@ -0,0 +1,17 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { type Options as GlobbyOptions } from 'globby';
3
+ import { Data, Options } from 'ejs';
4
+ import type { MemFsEditor } from '../index.js';
5
+ export type CopyOptions = CopySingleOptions & {
6
+ noGlob?: boolean;
7
+ globOptions?: GlobbyOptions;
8
+ ignoreNoMatch?: boolean;
9
+ fromBasePath?: string;
10
+ processDestinationPath?: (string: any) => string;
11
+ };
12
+ export declare function copy(this: MemFsEditor, from: string | string[], to: string, options?: CopyOptions, context?: Data, tplSettings?: Options): void;
13
+ export type CopySingleOptions = {
14
+ append?: boolean;
15
+ process?: (contents: string | Buffer, filepath: string, destination: string) => string | Buffer;
16
+ };
17
+ export declare function _copySingle(this: MemFsEditor, from: string, to: string, options?: CopySingleOptions): void;
@@ -0,0 +1,75 @@
1
+ import assert from 'assert';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { globbySync, isDynamicPattern } from 'globby';
5
+ import multimatch from 'multimatch';
6
+ import normalize from 'normalize-path';
7
+ import { getCommonPath, globify, render } from '../util.js';
8
+ function applyProcessingFunc(process, contents, filename, destination) {
9
+ const output = process(contents, filename, destination);
10
+ return Buffer.isBuffer(output) ? output : Buffer.from(output);
11
+ }
12
+ export function copy(from, to, options, context, tplSettings) {
13
+ to = path.resolve(to);
14
+ options = options || {};
15
+ let files = [];
16
+ if (options.noGlob) {
17
+ const fromFiles = Array.isArray(from) ? from : [from];
18
+ files = fromFiles.filter((filepath) => this.store.existsInMemory(filepath) || fs.existsSync(filepath));
19
+ }
20
+ else {
21
+ const fromGlob = globify(from);
22
+ const globOptions = { ...options.globOptions, nodir: true };
23
+ const diskFiles = globbySync(fromGlob, globOptions).map((file) => path.resolve(file));
24
+ const storeFiles = [];
25
+ this.store.each((file) => {
26
+ // The store may have a glob path and when we try to copy it will fail because not real file
27
+ if (!diskFiles.includes(file.path) &&
28
+ !isDynamicPattern(normalize(file.path)) &&
29
+ multimatch([file.path], fromGlob).length !== 0) {
30
+ storeFiles.push(file.path);
31
+ }
32
+ });
33
+ files = diskFiles.concat(storeFiles);
34
+ }
35
+ let generateDestination = () => to;
36
+ if (Array.isArray(from) || !this.exists(from) || (isDynamicPattern(normalize(from)) && !options.noGlob)) {
37
+ assert(!this.exists(to) || fs.statSync(to).isDirectory(), 'When copying multiple files, provide a directory as destination');
38
+ const processDestinationPath = options.processDestinationPath || ((path) => path);
39
+ const root = options.fromBasePath || getCommonPath(from);
40
+ generateDestination = (filepath) => {
41
+ const toFile = path.relative(root, filepath);
42
+ return processDestinationPath(path.join(to, toFile));
43
+ };
44
+ }
45
+ // Sanity checks: Makes sure we copy at least one file.
46
+ assert(options.ignoreNoMatch || files.length > 0, 'Trying to copy from a source that does not exist: ' + from);
47
+ files.forEach((file) => {
48
+ let toFile = generateDestination(file);
49
+ if (context) {
50
+ toFile = render(toFile, context, { ...tplSettings, cache: false });
51
+ }
52
+ this._copySingle(file, toFile, options);
53
+ });
54
+ }
55
+ export function _copySingle(from, to, options = {}) {
56
+ assert(this.exists(from), 'Trying to copy from a source that does not exist: ' + from);
57
+ const file = this.store.get(from);
58
+ let { contents } = file;
59
+ if (!contents) {
60
+ throw new Error(`Cannot copy empty file ${from}`);
61
+ }
62
+ if (options.process) {
63
+ contents = applyProcessingFunc(options.process, contents, file.path, to);
64
+ }
65
+ if (options.append) {
66
+ if (!this.store.existsInMemory) {
67
+ throw new Error('Current mem-fs is not compatible with append');
68
+ }
69
+ if (this.store.existsInMemory(to)) {
70
+ this.append(to, contents, { create: true, ...options });
71
+ return;
72
+ }
73
+ }
74
+ this.write(to, contents, file.stat);
75
+ }
@@ -0,0 +1,5 @@
1
+ import { type Options as GlobbyOptions } from 'globby';
2
+ import type { MemFsEditor } from '../index.js';
3
+ export default function deleteAction(this: MemFsEditor, paths: string | string[], options?: {
4
+ globOptions?: GlobbyOptions;
5
+ }): void;
@@ -0,0 +1,29 @@
1
+ import path from 'path';
2
+ import { globbySync } from 'globby';
3
+ import multimatch from 'multimatch';
4
+ import { setDeletedFileState } from '../state.js';
5
+ import { globify } from '../util.js';
6
+ function deleteFile(path, store) {
7
+ const file = store.get(path);
8
+ setDeletedFileState(file);
9
+ file.contents = null;
10
+ store.add(file);
11
+ }
12
+ export default function deleteAction(paths, options) {
13
+ if (!Array.isArray(paths)) {
14
+ paths = [paths];
15
+ }
16
+ paths = paths.map((filePath) => path.resolve(filePath));
17
+ paths = globify(paths);
18
+ options = options || {};
19
+ const globOptions = options.globOptions || {};
20
+ const files = globbySync(paths, globOptions);
21
+ files.forEach((file) => {
22
+ deleteFile(file, this.store);
23
+ });
24
+ this.store.each((file) => {
25
+ if (multimatch([file.path], paths).length !== 0) {
26
+ deleteFile(file.path, this.store);
27
+ }
28
+ });
29
+ }
@@ -0,0 +1,7 @@
1
+ import type { MemFsEditor, MemFsEditorFile } from '../index.js';
2
+ export type MemFsEditorFileDump = {
3
+ contents: string | null;
4
+ state?: string;
5
+ stateCleared?: string;
6
+ };
7
+ export default function <EditorFile extends MemFsEditorFile>(this: MemFsEditor<EditorFile>, cwd?: string, filter?: string | ((file: EditorFile, cwd: string) => boolean)): any;
@@ -0,0 +1,26 @@
1
+ import path from 'path';
2
+ import normalize from 'normalize-path';
3
+ import { minimatch } from 'minimatch';
4
+ import { hasClearedState, hasState } from '../state.js';
5
+ const defaultDumpFilter = (file) => hasClearedState(file) || hasState(file);
6
+ export default function (cwd = process.cwd(), filter) {
7
+ const filterFile = typeof filter === 'string'
8
+ ? (file) => defaultDumpFilter(file) && minimatch(file.path, filter)
9
+ : defaultDumpFilter;
10
+ return Object.fromEntries(this.store
11
+ .all()
12
+ .filter((file) => filterFile(file, cwd))
13
+ .map((file) => {
14
+ const filePath = normalize(cwd ? path.relative(cwd, file.path) : file.path);
15
+ const fileDump = {
16
+ contents: file.contents?.toString() ?? null,
17
+ };
18
+ if (file.state) {
19
+ fileDump.state = file.state;
20
+ }
21
+ if (file.stateCleared) {
22
+ fileDump.stateCleared = file.stateCleared;
23
+ }
24
+ return [filePath, fileDump];
25
+ }));
26
+ }
@@ -0,0 +1,2 @@
1
+ import type { MemFsEditor } from '../index.js';
2
+ export default function exist(this: MemFsEditor, filepath: string): boolean;
@@ -0,0 +1,4 @@
1
+ export default function exist(filepath) {
2
+ const file = this.store.get(filepath);
3
+ return file.contents !== null;
4
+ }
@@ -0,0 +1,2 @@
1
+ import type { MemFsEditor } from '../index.js';
2
+ export default function extendJSON(this: MemFsEditor, filepath: string, contents?: Record<string, unknown>, replacer?: ((this: any, key: string, value: any) => any) | (number | string)[] | null, space?: string | number): void;
@@ -0,0 +1,6 @@
1
+ import extend from 'deep-extend';
2
+ export default function extendJSON(filepath, contents, replacer, space) {
3
+ const originalContent = this.readJSON(filepath, {});
4
+ const newContent = extend({}, originalContent, contents);
5
+ this.writeJSON(filepath, newContent, replacer, space);
6
+ }
@@ -0,0 +1,3 @@
1
+ import type { MemFsEditor } from '../index.js';
2
+ import { CopyOptions } from './copy.js';
3
+ export default function (this: MemFsEditor, from: string, to: string, options?: CopyOptions): void;
@@ -0,0 +1,4 @@
1
+ export default function (from, to, options) {
2
+ this.copy(from, to, options);
3
+ this.delete(from, options);
4
+ }
@@ -0,0 +1,2 @@
1
+ import type { MemFsEditor } from '../index.js';
2
+ export default function readJSON(this: MemFsEditor, filepath: string, defaults?: any): any;
@@ -0,0 +1,17 @@
1
+ export default function readJSON(filepath, defaults) {
2
+ if (this.exists(filepath)) {
3
+ try {
4
+ const content = this.read(filepath);
5
+ if (!content) {
6
+ throw new Error(`${filepath} has no content`);
7
+ }
8
+ return JSON.parse(content);
9
+ }
10
+ catch (error) {
11
+ throw new Error('Could not parse JSON in file: ' + filepath + '. Detail: ' + error.message);
12
+ }
13
+ }
14
+ else {
15
+ return defaults;
16
+ }
17
+ }
@@ -0,0 +1,11 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import type { MemFsEditor } from '../index.js';
3
+ declare function read(this: MemFsEditor, filepath: string, options?: {
4
+ raw?: boolean;
5
+ defaults?: string | null;
6
+ }): string | null;
7
+ declare function read(this: MemFsEditor, filepath: string, options: {
8
+ raw?: true;
9
+ defaults?: Buffer | null;
10
+ }): Buffer | string | null;
11
+ export default read;
@@ -0,0 +1,12 @@
1
+ function read(filepath, options) {
2
+ options = options || { raw: false };
3
+ const file = this.store.get(filepath);
4
+ if (file.contents === null) {
5
+ if ('defaults' in options) {
6
+ return options.defaults ?? null;
7
+ }
8
+ throw new Error(filepath + " doesn't exist");
9
+ }
10
+ return options.raw ? file.contents : file.contents?.toString() || null;
11
+ }
12
+ export default read;
@@ -0,0 +1,2 @@
1
+ import type { MemFsEditor } from '../index.js';
2
+ export default function writeJSON(this: MemFsEditor, filepath: string, contents: any, replacer?: ((this: any, key: string, value: any) => any) | (number | string)[] | null, space?: string | number): string;
@@ -0,0 +1,11 @@
1
+ const DEFAULT_INDENTATION = 2;
2
+ export default function writeJSON(filepath, contents, replacer, space) {
3
+ let jsonStr;
4
+ if (typeof replacer === 'function') {
5
+ jsonStr = JSON.stringify(contents, replacer, space || DEFAULT_INDENTATION) + '\n';
6
+ }
7
+ else {
8
+ jsonStr = JSON.stringify(contents, replacer, space || DEFAULT_INDENTATION) + '\n';
9
+ }
10
+ return this.write(filepath, jsonStr);
11
+ }
@@ -0,0 +1,5 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import type { MemFsEditor } from '../index.js';
3
+ export default function write(this: MemFsEditor, filepath: string, contents: string | Buffer, stat?: {
4
+ mode?: number;
5
+ } | null): string;
@@ -0,0 +1,17 @@
1
+ import assert from 'assert';
2
+ import { isFileStateModified, setModifiedFileState } from '../state.js';
3
+ export default function write(filepath, contents, stat) {
4
+ assert(typeof contents === 'string' || Buffer.isBuffer(contents), 'Expected `contents` to be a String or a Buffer');
5
+ const file = this.store.get(filepath);
6
+ const newContents = Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
7
+ if (!isFileStateModified(file) ||
8
+ !Buffer.isBuffer(file.contents) ||
9
+ !newContents.equals(file.contents) ||
10
+ (stat && file.stat !== stat)) {
11
+ setModifiedFileState(file);
12
+ file.contents = newContents;
13
+ file.stat = stat ?? null;
14
+ this.store.add(file);
15
+ }
16
+ return file.contents.toString();
17
+ }